Dyskusja:C/Instrukcje sterujące
Zmiana przykładu z podzielnością
edytujna stronie umieściłem wcześniej propozycje zmiany przykładu http://pl.wikibooks.org/w/index.php?title=C/Instrukcje_steruj%C4%85ce ten:
W przygotowaniu: Proponował bym zastąpienie tego przykładu innym. Bardziej czytelnym i praktycznym. Wyrazy "Początek" i "Koniec" mylą czytelnika, i nie wie czy to początek i koniec pętli czy czego... Przykładowy zastępczy kod: #include <stdio.h> int main() { int i; for (i = 1 ; i <= 50 ; ++i) { if (i%4==0) continue ; printf ("%d, ", i); } return 0; } generuje on wszystkie liczby z zakresu od 1 do 50, które nie są podzielne przez 4. |
Derbeth odrzucił edycje i propozycje komentując to w ten sposób "mnie się przykład podoba bo jest prosty, nie każdy musi mieć głowę do liczb i podzielności)" ...chciałbym jednak poznać zdanie innych redaktorów. Uważam, że pojęcie podzielności jest znane każdemu który skończył 3 klasę podstawówki. Mało tego...czy ktoś bierze się za programowanie, jeżeli nie ma głowy do liczb? prosiłbym o opinie paru użytkowników
- Moim zdaniem można dać po prostu obydwa przykłady. W zrozumieniu na pewno nie zaszkodzą - raczej pomogą. adellon ? 19:45, 26 lis 2006 (CET)
- Oczywiście, że do programowania komputerów należy znać podstawy matematyki, bo bez tego nie napiszemy niczego bardziej odkrywczego od "Hello world". Po części podzielam Twoje zdanie - Twój przykład demonstrował konretny problem wraz z rozwiązaniem a przy okazji pokazywał działanie słowa continue. Problem jest tylko jeden: ten przykład przede wszystkim powinien demonstrować działanie tego słowa kluczowego, a nie zawracać czytelnikowi głowę problemami podzielności liczb. Czytelnik widząc kod, który prezentuje działanie jakiegoś elementu języka powinien w pierwszej kolejności zadać sobie pytanie dlaczego ten czy owy operator działa tak a nie inaczej. Nie powinien się na początku zastanawiać co właściwie ten program robi. Wydaje mi się, że przykład, który jest już zamieszczony na stronie demonstruje działanie tego słowa w sposób jasny i zupełnie wystarczający. Natomiast na końcu podręcznika istnieje specjalny rozdział, który służy do demonstracji programów rozwiązujących konkretny problem. --Kj 19:48, 26 lis 2006 (CET)
- Chodzi mi o to, że po prostu czytając ten przykład próbowałem sobie wyobrazić co wypisze ten program...I jakoś nie było intuicyjne to, że pętla nie wyświetla napisu Koniec na koniec swojej pracy (czyli ostatnich 60 obiegów). Może jednak zastanowimy się czy nie wstawić dwóch przykładów..albo jakoś zmodyfikować pierwszy, żeby był bardziej intuicyjny. A co do tego, czy warto pokazywać czytelnikowi co możemy zrobić dzięki nowej funkcji, czy poleceniu..to uważam, że warto..pamiętajmy, że programista chce programować, i pisać programy które jakoś mu sie przydadzą. Potrzebna jest i teoria, ale i praktyczne wykorzystanie tej wiedzy. Ja np w szkole gdy były pochodne na matmie, to ich nie lubiałem (mimo tego, że je rozumiałem), bo po prostu nie wiedziałem, do czego one mogą mi się przydać...dopiero gdy nam to wytłumaczono nabrałem ochoty do nauki.. To tak jak w TV Markecie...towar staje się atrakcyjny, kiedy pokażą ile fajnych rzeczy za jego pomocą można zrobić...to właśnie takie rzeczy zapadają nam w pamięci..a o to nam chodzi... Noisy 21:00, 26 lis 2006 (CET)
- Oczywiście, że do programowania komputerów należy znać podstawy matematyki, bo bez tego nie napiszemy niczego bardziej odkrywczego od "Hello world". Po części podzielam Twoje zdanie - Twój przykład demonstrował konretny problem wraz z rozwiązaniem a przy okazji pokazywał działanie słowa continue. Problem jest tylko jeden: ten przykład przede wszystkim powinien demonstrować działanie tego słowa kluczowego, a nie zawracać czytelnikowi głowę problemami podzielności liczb. Czytelnik widząc kod, który prezentuje działanie jakiegoś elementu języka powinien w pierwszej kolejności zadać sobie pytanie dlaczego ten czy owy operator działa tak a nie inaczej. Nie powinien się na początku zastanawiać co właściwie ten program robi. Wydaje mi się, że przykład, który jest już zamieszczony na stronie demonstruje działanie tego słowa w sposób jasny i zupełnie wystarczający. Natomiast na końcu podręcznika istnieje specjalny rozdział, który służy do demonstracji programów rozwiązujących konkretny problem. --Kj 19:48, 26 lis 2006 (CET)
fall-through
edytujPrzykład z tymi podzielnościami jest do bani niestety, bo nie pokazuje ze to jest prosta sprawa. No i modulo nie zwraca liczb ujemnych... Jak tylko opracuje jakis dobry przykład to zmienie go. --Migol 20:38, 5 lut 2007 (CET)
[mina86@erwin ~/code]$ cat a.c #include <stdio.h> int main(void) { printf("%d\n", -3 % 2); return 0; } [mina86@erwin ~/code]$ gcc --std=c89 -pedantic -Wall a.c && ./a.out -1 [mina86@erwin ~/code]$ gcc --std=c99 -pedantic -Wall a.c && ./a.out -1
Wystarczy Ci ten kod jako dowód, że modulo zwraca liczby ujemne, czy mam szukać odpowiedniego fragmentu standardu? A, że przykład jest zły to wie każdy i jest to zapsiane w tekście jako TODO. -- mina86 (talk) 21:50, 5 lut 2007 (CET)
- Ot i ciekawostka. Człowiek całe życie się uczy --Migol 01:16, 30 mar 2007 (CEST)
przyklad ze swichem
edytujjest zrobiony ok od strony składni, ale zero nie jest podzielne przez nic!
- Ależ zero dzieli się bardzo dobrze, bo przez każdą liczbę całkowitą (poza zerem). w:Dzielnik. --Derbeth talk 02:19, 18 lis 2007 (CET)
Pre/post inkrementacja
edytujNie jestem przekonany co do wyższości preinkrementacji nad postinkrementacją. Pozwoliłem sobie przeprowadzić mały test, który wprowadził mnie w osłupienie. Oto kod programu testowego:
#define N 100000000
#include <time.h>
#include <stdio.h>
int main ()
{
/*register long i asm("rax") = 15; - w tym przypadku program wyraźnie przyspiesza (GCC/AMD64)*/
long i = 15;
long a = 0;
long t1,t2,t3,t4;
t1 = clock();
for (i=0;i<N;) {a=2*(++i);}
t1 = clock()-t1;
t2 = clock();
for (i=0;i<N;) {a=2*(i++);}
t2 = clock()-t2;
printf ("1) %fs\n2) %fs\n",t1/(CLOCKS_PER_SEC*1.0f),t2/(CLOCKS_PER_SEC*1.0f));
}
Wyniki na moim komputerze są następujące:
1) 0.290000s 2) 0.240000s
Próby przeprowadzałem kilkukrotnie, aby upewnić się, że test jest wiarygodny. Zamieniałem także kolejność pętli (tj. raz pierwsza pętla zawierała pre- a raz postinkrementację) aby wykluczyć wpływ systemu operacyjnego. Pomimo tego otrzymywałem identyczne wyniki. W opcjach kompilatora nie dodawałem żadnych parametrów optymalizacyjnych (nie o to chodzi). Użyty kompilator: GCC 4.3.2. Z kolei na serwerze uczelnianym na tej samej platformie ale starszym kompilatorze (3.4.3) wyniki były takie:
1) 0.290000s 2) 0.280000s
Lub zamieniały się miejscami (granica błędu). Innymi słowy - nie była widoczna szczególna różnica, uzasadniająca wybór którejkolwiek z rodzajów inkrementacji. Spotkałem się z opinią, która uzasadnia wydłużenie działania "i++" względem "++i" w następujący sposób (cytat):
int PreIncrement(int a)
{
a = a + 1;
return a;
}
int PostIncrement(int a)
{
int temp = a; // This is the reason why it is slightly slower
a = a + 1;
return temp;
}
Oczywiście ma to sens w przypadku, gdy implementujemy takie operatory w języku C. Nie zapominajmy jednak, że na poziomie asemblera, do którego pośrednio tłumaczony jest C oraz na poziomie kodu maszynowego granica między "i++" a "++i" nieco się zaciera. Spróbujmy się zastanowić, jak mogłaby wyglądać pętla wyrażona w C:
int a = 0;
int i = 0;
for (;i<100;) {a=2*(i++);}
Imho mogłaby wyglądać tak:
/* r9 - "a", r10 - "i" */
movq $0, %r9
movq %r9,%r10
loop:
movq %r10,%r9
mulq $2,%r9
incq %r10
cmpq $99,%r10
jle $loop
Jak wyglądałaby pętla:
int a = 0;
int i = 0;
for (;i<100;) {a=2*(++i);}
? Moim zdaniem tak:
/* r9 - "a", r10 - "i" */
movq $0, %r9
movq %r9,%r10
loop:
incq %r10
movq %r10,%r9
mulq $2,%r9
cmpq $99,%r10
jle $loop
(Kod nie był testowany - jest on raczej pewnego rodzaju demonstracją praktycznej różnicy w budowie pętli na poziomie kodu asemblera. Fakt przechowywania zmiennych w rejestrach także nie powinien być brany pod uwagę - analogiczna sytuacja miałaby miejsce w przypadku operacji na pamięci.) Różnica polega tylko i wyłącznie na kolejności instrukcji. Nigdzie tutaj nie musimy tworzyć dodatkowej zmiennej, a szybkość "i++" oraz "++i" moim zdaniem zależeć będzie tylko i wyłącznie od kompilatora i jakości generowanego przez niego kodu. Ośmielę się postawić tezę, że przy współczesnych kompilatorach nie ma to znaczenia. Zachęcam do dyskusji i pozdrawiam. --Kj 01:38, 20 lut 2009 (CET)
Błąd/niekonsekwencja... jak zwał, tak zwał.
edytujPiszę tutaj, bo... zajęty jestem i nie mam czasu na edycję, która nie wymagałaby popraw :P
"obiegiem drugiej pętli for zmniejszyć wartość zmiennej i o 1." Zerknijcie o co chodzi tam, gdzie padły te słowa. Myślę, że lepiej zmniejszyć w pierwszej pętli liczbę w warunku na mniejszą o jeden. Szybciej, przyjemniej... i z wykorzystaniem logiki, o której mowa w artykule. Pozdrawiam, Hilary Jendrasiak
błąd w przykładzie z ulgą (switch )
edytujMoże coś źle zrozumiałem ale wydaje mi się, że koncowy podatek do zapłaty powinien byc równy: podatek - x% od podatku czyli: podatek =podatek - (podatek*x/100); a nie tak jak jest to w przykładzie podatek =podatek - (podatek/x*100);
pętla for jako while
edytujPętla for :
for (wyrażenie1; wyrażenie2; wyrażenie3) {
/* instrukcje do wykonania w pętli */
}
/* dalsze instrukcje */
Jest równoważna (jeżeli wewnątrz pętli nie ma żadnych instrukcji continue z pętlą while :
{
wyrażenie1;
while (wyrażenie2) {
/* instrukcje do wykonania w pętli */
wyrażenie3;
}
}
/* dalsze instrukcje */
Czy nie powinno być :
{
wyrażenie1;
while (wyrażenie2) {
/* instrukcje do wykonania w pętli */
wyrażenie3;
}
wyrażenie3;
}
/* dalsze instrukcje */
?
Pozdrawiam . --Adam majewski (dyskusja) 11:12, 24 mar 2012 (CET)