Język C. Nowoczesne Programowanie. Wydanie II
Język C. Nowoczesne Programowanie. Wydanie II
Język C. Nowoczesne Programowanie. Wydanie II
Spis treci
Przykadowy rozdzia
Katalog ksiek
Katalog online
Zamw drukowany
katalog
Twj koszyk
Dodaj do koszyka
Cennik i informacje
Zamw informacje
o nowociach
Zamw cennik
Czytelnia
Fragmenty ksiek
online
Kontakt
Helion SA
ul. Kociuszki 1c
44-100 Gliwice
tel. 32 230 98 63
e-mail: [email protected]
Helion 19912010
Jzyk C. Nowoczesne
programowanie. Wydanie II
Autor: K. N. King
Tumaczenie: Przemysaw Szeremiota
ISBN: 978-83-246-2805-6
Tytu oryginau: C Programming: A Modern Approach, 2nd Edition
Format: B5, stron: 928
SPIS TRECI
1.
Wstp
19
WPROWADZENIE
29
1.1.
29
29
30
31
32
33
33
34
1.2.
2.
Historia jzyka C
Pocztki
Standaryzacja
Jzyki oparte na C
Mocne i sabsze strony jzyka C
Mocne strony
Saboci
Efektywne stosowanie jzyka C
FUNDAMENTY JZYKA C
39
2.1.
39
40
40
41
42
43
43
44
45
46
48
48
48
49
50
51
52
53
2.2.
2.3.
2.4.
Spis treci
2.5.
2.6.
2.7.
2.8.
3.
69
3.1.
69
70
72
73
74
76
78
79
79
Funkcja printf
Specyfikatory konwersji
Wykorzystanie printf do formatowania liczb
Znaki sterujce
Funkcja scanf
Dziaanie funkcji scanf
Zwyke znaki w cigu formatujcym funkcji scanf
Skutki mylenia printf ze scanf
Dodawanie uamkw
WYRAENIA
85
4.1.
86
87
88
91
91
92
93
94
96
97
98
4.2.
4.3.
4.4.
4.5.
5.
53
54
55
55
57
58
58
FORMATOWANIE WEJCIA-WYJCIA
3.2.
4.
Wczytywanie danych
Obliczanie gabarytu przesyki (podejcie drugie)
Definiowanie nazw dla staych
Konwersja skali Fahrenheita na skal Celsjusza
Identyfikatory
Sowa kluczowe
Oglny ukad programu C
Operatory arytmetyczne
Pierwszestwo i czno operatorw
Obliczanie cyfry kontrolnej kodu kreskowego
Operatory przypisania
Przypisania proste
L-wartoci
Przypisania zoone
Operatory inkrementacji i dekrementacji
Obliczanie wartoci wyra e
INSTRUKCJE WYBORU
107
5.1.
108
108
109
109
111
112
112
114
115
116
117
118
120
120
123
124
5.2.
5.3.
Spis treci
6.
INSTRUKCJE PTLI
133
6.1.
134
135
136
137
137
138
139
141
142
143
143
144
146
146
147
148
149
151
6.2.
6.3.
6.4.
6.5.
7.
Instrukcja while
Ptle nieskoczone
Wypisywanie tabeli kwadratw liczb
Obliczanie sumy szeregu liczb
Instrukcja do
Obliczanie liczby cyfr w liczbie cakowitej
Instrukcja for
Idiomy instrukcji for
Pomijanie wyrae w instrukcji for
Instrukcje for w C99
Operator przecinka
Wypisywanie tabeli kwadratw liczb (podejcie drugie)
Przerywanie ptli
Instrukcja break
Instrukcja continue
Instrukcja goto
Saldo konta
Instrukcja pusta
PODSTAWOWE TYPY C
161
7.1.
161
164
164
166
166
166
167
168
170
170
171
172
173
173
174
176
176
177
179
180
181
183
184
185
186
187
188
189
7.2.
7.3.
7.4.
7.5.
7.6.
Typy cakowite
Typy cakowite w C99
Literay cakowite
Literay cakowite w C99
Przepenienie zakresu
Wczytywanie i wypisywanie wartoci cakowitych
Sumowanie szeregu liczb cakowitych (podejcie drugie)
Typy zmiennoprzecinkowe
Literay zmiennoprzecinkowe
Wczytywanie i wypisywanie wartoci zmiennoprzecinkowych
Typy znakowe
Operacje na znakach
Znaki ze znakiem i bez znaku
Typy arytmetyczne
Znaki sterujce
Funkcje do manipulowania znakami
Wczytywanie i wypisywanie znakw funkcjami scanf i printf
Wczytywanie i wypisywanie znakw funkcjami getchar i putchar
Okrelanie dugoci komunikatu
Konwersja typw
Zwyczajne konwersje arytmetyczne
Konwersja przy przypisaniu
Niejawne konwersje w C99
Rzutowanie
Definicje typw
Zalety definicji typw
Definicje typw a przenono programw
Operator sizeof
Spis treci
8.
TABLICE
199
8.1.
199
200
202
203
203
204
205
206
208
209
210
211
212
8.2.
8.3.
9.
Tablice jednowymiarowe
Indeksowanie tablic
Odwracanie szeregu liczbowego
Inicjalizacja tablicy
Inicjalizatory desygnowane
Sprawdzanie, czy liczba zawiera powtarzajce si cyfry
Operator sizeof dla tablic
Naliczanie odsetek
Tablice wielowymiarowe
Inicjalizowanie tablic wielowymiarowych
Stae tablicowe
Rozdawanie kart
Tablice o zmiennej liczbie elementw (C99)
FUNKCJE
223
9.1.
223
224
225
226
227
229
230
231
233
234
235
238
240
241
242
243
243
244
246
248
9.2.
9.3.
9.4.
9.5.
9.6.
261
261
262
263
263
263
264
266
270
271
272
273
Spis treci
11. WSKANIKI
11.1. Zmienne wskanikowe
Deklarowanie zmiennych wska
nikowych
11.2. Operator adresu i wyuskania
Operator adresu
Operator wyuskania
11.3. Przypisania a wskaniki
11.4. Wskaniki jako argumenty funkcji
Wyszukiwanie najwikszego i najmniejszego elementu tablicy
Ochrona argumentw za pomoc const
11.5. Wskaniki jako wartoci zwracane
13. CI
GI ZNAKW
13.1. Literay napisowe
Znaki sterujce w literaach napisowych
Kontynuacja literau napisowego w nowym wierszu
Literay napisowe a pami programu
Operacje na literaach napisowych
Literay napisowe a literay znakowe
13.2. Zmienne napisowe
Inicjalizowanie zmiennej napisowej
Tablice znakw a wska
niki do znakw
13.3. Wczytywanie i wypisywanie napisw
Wypisywanie napisw funkcjami printf i puts
Wczytywanie cigw znakw funkcjami scanf i gets
Wczytywanie napisw znak po znaku
13.4. Odwoania do pojedynczych znakw w cigu
13.5. Funkcje biblioteczne jzyka C
Funkcja strcpy (kopiowanie cigw)
Funkcja strlen (dugo cigu)
Funkcja strcat (czenie cigw)
9
283
283
284
285
285
286
287
289
291
293
293
301
302
303
303
304
304
304
305
306
307
308
309
311
311
311
312
313
314
314
323
323
324
324
325
326
326
327
328
329
330
330
331
333
334
335
336
338
338
10
Spis treci
Funkcja strcmp (porwnywanie cigw)
Wypisywanie notatek kalendarzowych
13.6. Idiomy
Szukanie koca cigu
Kopiowanie cigu
13.7. Tablice cigw znakw
Argumenty wywoania programu
Weryfikacja nazw planet
14. PREPROCESOR
14.1. Jak dziaa preprocesor
14.2. Dyrektywy preprocesora
14.3. Makrodefinicje
Makrodefinicje proste
Makrodefinicje sparametryzowane
Operator #
Operator ##
Oglne waciwoci makrodefinicji
Nawiasy w makrodefinicjach
Tworzenie dugich makrodefinicji
Makrodefinicje predefiniowane
Dodatkowe makrodefinicje predefiniowane w C99
Puste argumenty makrodefinicji
Makrodefinicje o zmiennej liczbie argumentw
Identyfikator __func__
14.4. Warunkowa kompilacja kodu
Dyrektywy #if i #endif
Operator defined
Dyrektywy #ifdef i #ifndef
Dyrektywy #elif i #else
Zastosowania warunkowej kompilacji kodu
14.5. Inne dyrektywy
Dyrektywa #error
Dyrektywa #line
Dyrektywa #pragma
Operator _Pragma
339
340
343
343
345
347
349
351
363
363
366
367
367
370
373
374
375
376
377
379
380
381
382
383
384
384
385
386
386
387
388
389
390
391
391
401
401
403
403
405
406
407
409
410
411
411
412
419
419
422
Spis treci
Przebudowa programu
Definiowanie makrodefinicji na zewntrz programu
11
422
425
431
431
432
433
434
435
436
437
438
439
440
441
441
442
443
444
450
452
454
455
456
457
458
459
469
470
470
471
472
472
473
474
475
476
477
478
478
479
480
481
481
482
483
484
484
487
488
12
Spis treci
17.6.
17.7.
17.8.
17.9.
Listy uporzdkowane
Zarzdzanie baz danych magazynu (drugie podejcie)
Wskaniki do wskanikw
Wskaniki do funkcji
Wska
niki do funkcji w roli argumentw
Funkcja qsort
Inne zastosowania wska
nikw do funkcji
Tablice funkcji trygonometrycznych
Wskaniki zastrze one (C99)
Elastyczne skadowe tablicowe (C99)
18. DEKLARACJE
18.1. Skadnia deklaracji
18.2. Klasy przydziau
Wasnoci zmiennych
Klasa przydziau auto
Klasa przydziau static
Klasa przydziau extern
Klasa przydziau register
Klasa przydziau funkcji
Podsumowanie
18.3. Kwalifikatory typw
18.4. Deklaratory
Rozszyfrowywanie zawiych deklaracji
Stosowanie synonimw typw dla uproszczenia deklaracji
18.5. Inicjalizatory
Zmienne niezainicjalizowane
18.6. Funkcje inline (C99)
Definicje rozwijane w miejscu wywoania
Ograniczenia funkcji rozwijanych w miejscu wywoania
Funkcje inline w GCC
490
491
496
497
497
498
501
502
503
505
517
517
519
519
520
521
522
523
524
525
526
528
529
531
531
533
533
534
536
536
545
546
548
548
549
550
553
554
554
555
555
557
559
560
562
564
564
565
565
566
Spis treci
20. PROGRAMOWANIE NISKOPOZIOMOWE
20.1. Operatory bitowe
Operatory przesuni bitowych
Negacja, iloczyn, suma i suma wyczajca
Operatory bitowe w odwoaniach
do poszczeglnych bitw wartoci liczbowych
Operatory bitowe w odwoaniach do pl bitowych
Szyfrowanie XOR
20.2. Pola bitowe w strukturach
Reprezentacja pl bitowych
20.3. Inne niskopoziomowe techniki programistyczne
Definiowanie typw maszynowych
Unie jako perspektywy
Wska
niki jako adresy
Podgld pamici
Kwalifikator typu volatile
13
571
571
572
573
574
576
577
578
580
581
581
582
584
584
586
593
593
594
595
596
596
596
596
596
596
597
597
597
597
597
597
597
598
598
598
598
599
599
599
599
599
599
599
600
600
600
601
14
Spis treci
22. WEJCIE-WYJCIE
22.1. Strumienie
Wska
niki plikowe
Strumienie standardowe a przekierowania
Pliki tekstowe i pliki binarne
22.2. Operacje na plikach
Otwieranie pliku
Tryby dostpu do plikw
Zamykanie pliku
Doczanie pliku do otwartego strumienia
Pobieranie nazw plikw z wiersza polecenia
Sprawdzanie moliwoci otwarcia pliku
Pliki tymczasowe
Buforowanie plikw
Inne operacje na plikach
22.3. Formatowanie wejcia-wyjcia
Funkcje printf
Specyfikatory konwersji dla funkcji printf
Zmiany specyfikatorw konwersji w C99
Przykady specyfikacji konwersji dla funkcji printf
Funkcje scanf
Cigi formatujce funkcji scanf
Specyfikacje konwersji funkcji scanf
Zmiany specyfikatorw konwersji w C99
Przykady dla funkcji scanf
Wykrywanie koca strumienia wejciowego i bdw
22.4. Wejcie-wyjcie znakowe
Funkcje wyjcia
Funkcje wejcia
Kopiowanie pliku
22.5. Wierszowe wejcie-wyjcie
Funkcje wyjcia
Funkcje wejcia
22.6. Blokowe wejcie-wyjcie
22.7. Pozycjonowanie w plikach
Modyfikowanie pliku rekordw bazy danych
22.8. Funkcje wejcia-wyjcia w pamici
Funkcje wyjcia
Funkcje wejcia
605
606
606
607
608
609
610
611
612
613
613
614
615
616
618
619
619
620
622
624
626
627
628
631
631
632
635
635
636
637
638
638
639
640
641
643
644
645
646
659
659
662
664
664
665
666
666
667
668
Spis treci
23.4. Nagwek <math.h> matematyka (C99)
Standard zmiennoprzecinkowy IEEE
Typy
Makrodefinicje
Bdy
Funkcje
Makrodefinicje klasyfikujce
Funkcje trygonometryczne
Funkcje hiperboliczne
Funkcje wykadnicze i logarytmiczne
Funkcje potgowe i funkcje wartoci bezwzgldnej
Funkcje bdw i funkcje gamma
Funkcje zaokrglania
Funkcje reszty z dzielenia
Funkcja manipulacji
Funkcje maksimum, minimum i rnicy dodatniej
Zmiennoprzecinkowy iloczyn-suma
Makrodefinicje porwna
23.5. Nagwek <ctype.h> obsuga znakw
Funkcje klasyfikacji znakw
Test funkcji klasyfikacji znakw
Funkcje mapowania wielkoci liter
Test funkcji zmiany wielkoci liter
23.6. Nagwek <string.h> obsuga cigw znakw
Funkcje kopiujce
Funkcje czenia cigw
Funkcje porwna
Funkcje wyszukujce
Rne
25. MIDZYNARODWKA
25.1. Nagwek <locale.h> rodowiska jzykowe
Kategorie
Funkcja setlocale
Funkcja localeconv
25.2. Znaki wielobajtowe i znaki poszerzone
Znaki wielobajtowe
Znaki poszerzone
Unicode i uniwersalny zestaw znakw UCS
15
669
669
671
671
672
673
674
675
675
676
677
678
679
680
680
681
682
683
684
684
685
686
687
687
688
689
690
691
695
699
700
701
702
703
704
704
705
707
707
708
709
715
716
716
717
719
722
723
724
724
16
Spis treci
25.3.
25.4.
25.5.
25.6.
Kodowanie Unicode
Funkcje konwersji znakw poszerzonych i wielobajtowych
Funkcje konwersji cigw znakw poszerzonych i wielobajtowych
Dwuznaki i trjznaki
Trjznaki
Dwuznaki
Nagwek <iso646.h> symbole alternatywne
Uniwersalne nazwy znakw (C99)
Nagwek <wchar.h> (C99) dodatkowe narzdzia
dla znakw poszerzonych i wielobajtowych
Orientacja strumienia
Funkcje formatowanego wejcia-wyjcia
dla znakw poszerzonych
Funkcje wejcia-wyjcia dla znakw poszerzonych
Obsuga cigw znakw poszerzonych
Funkcja konwersji dat i godzin na cigi znakw poszerzonych
Dodatkowe funkcje konwersji znakw poszerzonych
i wielobajtowych
Nagwek <wctype.h> (C99) klasyfikacja
znakw poszerzonych
Funkcje klasyfikacji znakw poszerzonych
Rozszerzalne funkcje klasyfikacji znakw poszerzonych
Funkcje zmiany wielkoci liter dla znakw poszerzonych
Rozszerzalne funkcje zmiany wielkoci liter
znakw poszerzonych
26. RNE
725
727
729
729
730
731
731
732
733
734
735
737
738
743
743
747
747
748
749
750
755
755
758
758
759
760
761
762
764
765
766
768
769
770
771
772
774
778
787
788
788
790
790
791
Spis treci
17
792
792
793
795
795
797
797
798
798
810
810
811
811
813
814
815
Dodatek A
Operatory jzyka C
819
Dodatek B
821
Dodatek C
827
Dodatek D
831
Dodatek E
893
Bibliografia
895
Skorowidz
899
800
800
801
802
802
803
804
804
805
805
806
807
807
Formatowanie
wejcia-wyjcia
W poszukiwaniu nieosigalnego na przeszkodzie staje tylko prostota.
3.1.
Funkcja printf
Funkcja printf suy do wypisywania na wyjciu programu zawartoci cigu
znakw, okrelanego mianem cigu formatujcego, ktry moe zawiera symbole zastpcze dla wartoci zmiennych wstawianych do cigu wypisywanego.
W wywoaniu funkcji printf musi si znajdowa cig formatujcy, uzupeniony
wartociami, ktre maj by podstawione w odpowiednie miejsca cigu na wyjciu
programu:
printf(cig-formatujcy, wyraenie1, wyraenie1, );
Wartoci przekazywane do podstawienia do cigu formatujcego mog by staymi, zmiennymi albo caymi wyraeniami. Nie istnieje ograniczenie liczby wartoci wypisywanych w ramach pojedynczego wywoania funkcji printf.
Cig formatujcy moe zawiera zarwno zwyczajne znaki drukowalne, jak
i tak zwane specyfikatory konwersji, rozpoczynajce si od znaku %. Specyfikator konwersji to symbol zastpczy reprezentujcy warto, ktra ma zosta
wstawiona w dane miejsce cigu formatujcego, wraz z opisem sposobu wypisania wartoci. Informacje znajdujce si za znakiem % okrelaj sposb konwersji
69
70
=
=
=
=
10;
20;
43.2892f;
5527.0f;
Specyfikatory konwersji
Specyfikatory konwersji pozwalaj programistom zachowa du doz kontroli
nad wygldem (formatem) wypisywanych cigw i wartoci. Z drugiej strony
bywaj skomplikowane i trudne do ogarnicia. Istotnie, szczegowe opisywanie
specyfikatorw konwersji byoby na tym wstpnym etapie omwienia przedwczesne. Zapoznamy si wic tylko z najwaniejszymi cechami i moliwociami
dawanymi przez specyfikatory.
W rozdziale 2. zauwaylimy, e specyfikator konwersji moe zawiera informacje sterujce formatowaniem wartoci. W szczeglnoci zastosowalimy specyfikator %.1f, aby ograniczy liczb wypisywanych cyfr po przecinku w wartoci typu float. Oglniej rzecz biorc, specyfikator konwersji moe przyj
posta %m.pX albo %-m.pX, gdzie m i p to stae cakowite, a X to litera. Wartoci
m i p s opcjonalne. W przypadku nieobecnoci p nie stosuje si rwnie kropki
oddzielajcej m od p. W specyfikatorze konwersji %10.2f m wynosi 10, p wynosi 2, a X to f. W specyfikatorze %10f m wynosi 10, p (wraz z kropk) zostao
pominite, a X to f. Za to w %.2f m zostao pominite, a p wynosi 2.
Minimalna szeroko pola m okrela minimaln liczb znakw, jaka zostanie
wypisana na wyjciu przy wypisywaniu wartoci. Jeli wypisywana warto jest
w reprezentacji znakowej krtsza ni m znakw, zostanie wyrwnana do minimalnej szerokoci pola i do prawej strony pola (innymi sowy, przed waciw warto wstawiona bdzie odpowiednia liczba spacji). Na przykad specyfikator %4d
71
Standard nie wymaga od kompilatorw jzyka C sprawdzania, czy liczba specyfikatorw konwersji okrelona w cigu formatujcym odpowiada liczbie przekazanych wartoci. Ponisze wywoanie funkcji printf posiada wicej specyfikatorw konwersji ni wartoci do wypisania:
printf("%d %d\n", i);
W tym przypadku funkcja printf wypisze jedynie warto i, a warto j zostanie pominita.
Kompilatory nie s te zobligowane do sprawdzania, czy specyfikatory konwersji odpowiadaj typom przekazywanych wartoci. Jeli programista zastosuje
nieodpowiednie specyfikatory, program moe wypisa na wyjciu kompletne
bzdury. We my dla przykadu wywoanie funkcji printf, w ktrym zmienna i
typu int i zmienna x typu float zostan przekazane w zej kolejnoci:
printf("%f %d\n", i, x);
72
specyfikatory
dla wartoci cakowitych 7.1
specyfikatory dla wartoci
zmiennoprzecinkowych 7.2
specyfikatory
dla wartoci znakowych 7.3
specyfikatory
dla cigw znakw 13.3
PROGRAM
tprintf.c
Znaki | w cigach formatujcych dla funkcji printf su jedynie do rozdzielenia wypisywanych wartoci i zilustrowania szerokoci pl, w ktrych s
wypisywane. W przeciwiestwie do znakw % i \ znak | nie ma specjalnego znaczenia dla funkcji printf. Program wypisuje na wyjciu co takiego:
|40|
40|40
| 040|
|
839.210| 8.392e+02|839.21
73
%10.3e wypisanie wartoci x w zapisie wykadnikowym w polu o szerokoci co najmniej 10 znakw, z trzema cyframi po przecinku. Tak zapisana
warto x zajmuje 9 znakw, wic jest uzupeniona spacj z lewej strony.
Znaki sterujce
Symbol \n, wykorzystywany ju w poprzednich cigach formatujcych, to przykad tak zwanego znaku sterujcego (ang. escape sequence). Znaki sterujce
pozwalaj na osadzanie w cigach znakw, ktre zapisane inaczej byyby kopotliwe dla kompilatora. Dotyczy to przede wszystkim znakw niedrukowalnych
terminali znakowych oraz znakw posiadajcych specjalne znaczenie dla samego
kompilatora (jak "). Kompletn list znakw sterujcych zamiecimy p niej, na
razie wystarczy wykaz najwaniejszych:
\a
\b
\n
\t
sygna dzwonka,
kasowanie poprzedniego znaku (ang. backspace),
nowy wiersz,
tabulator poziomy.
Takie symbole w cigu formatujcym reprezentuj czynnoci do wykonania w czasie wypisywania cigu na wyjciu programu. Ot wypisanie \a spowoduje na
wikszoci maszyn wygenerowanie d wiku brzczyka terminala; wypisanie \b
74
Cena
jed.
Data
zakupu
3.2.
Funkcja scanf
Tak jak funkcja printf suy do formatowania wyjcia programu, tak funkcja
scanf obsuguje formatowane wejcie. Cig formatujcy dla funkcji scanf rwnie moe zawiera zwyczajne znaki i specyfikatory konwersji. Konwersje obsugiwane przez funkcj scanf pokrywaj si z grubsza z konwersjami funkcji
printf.
W wielu przypadkach cig formatujcy funkcji scanf zawiera wycznie
specyfikatory konwersji, jak w poniszym przykadzie:
int i, j;
float x, y;
scanf("%d%d%f%f", &i, &j, &x, &y);
75
Funkcja scanf wczyta taki wiersz i rozpocznie stosowanie specyfikatorw konwersji i podstawianie uzyskanych wartoci pod zmienne przekazane w wywoaniu:
1 pod i, -20 pod j, 0.3 pod x i -4000.0 pod y. Takie upakowane cigi
sterujce s dla funkcji scanf typowe. W przypadku funkcji printf czciej
stosuje si rozmaite dekoracje i napisy objaniajce, otaczajce wyprowadzane
wartoci.
Funkcja scanf (tak jak printf zreszt) zastawia na niewiadomych i nieostronych uytkownikw kilka wnykw. Programista stosujcy funkcj scanf
musi starannie sprawdza, czy liczba specyfikatorw konwersji odpowiada liczbie
zmiennych przekazanych w wywoaniu i czy poszczeglne konwersje odpowiadaj
typom przekazanych zmiennych podobnie jak w przypadku printf, kompilator nie ma obowizku przeprowadzania takiej kontroli i wykrywania ewentualnych niezgodnoci. Kolejna puapka czai si w znaku &, ktry zazwyczaj poprzedza kad zmienn przekazywan do scanf. Znak & jest zazwyczaj (cho nie
zawsze) konieczny i to programista ma obowizek pamita o jego stosowaniu.
Pominicie symbolu & przy zmiennej przekazywanej do funkcji scanf prowadzi
do nieprzewidywalnych wynikw dziaania programu. Potencjalnie s to efekty
katastrofalne. Najczciej taki bd prowadzi do wyoenia si programu. W najlepszym przypadku podstawienie wartoci pod zmienn bdzie nieskuteczne
zmienna zachowa swoj poprzedni warto (co nie oznacza, e bdzie miaa jakkolwiek okrelon warto, jeli np. nie zostaa zainicjalizowana!). Pominicie
znaku & jest niestety czstym bdem. Niektre kompilatory wykrywaj taki bd
i generuj komunikat z ostrzeeniem w rodzaju argument nie jest wska nikiem
(o wska nikach powiemy sobie w rozdziale 11.; to wanie znak & tworzy wska nik do zmiennej). Ostrzeenia zwizane z wywoaniami funkcji scanf trzeba traktowa bardzo powanie.
wykrywanie bdw
w scanf 22.3
76
dla funkcji scanf wejcie jest widoczne jako cigy strumie znakw:
1-20.3-4.0e3
ppwpwwwpppwwppppwwwwww
Ostatni znak strumienia wejciowego, czyli pierwszy znak za ostatni skonwertowan grup znakw, jest przez funkcj scanf podgldany, ale nie jest wczytywany. Zostanie on wczytany i pominity bd skonwertowany przy nastpnym
wywoaniu scanf.
Wedug jakich regu scanf rozpoznaje liczb cakowit albo zmiennoprzecinkow w cigu danych wejciowych? Ot kiedy scanf ma wczyta liczb
cakowit, szuka w cigu wejciowym znaku cyfry, ewentualnie znaku + albo -.
77
78
Znaki odstpw w cigu formatujcym. Kiedy w cigu formatujcym znajduj si znaki odstpu, funkcja scanf bdzie w strumieniu wejciowym wczytywaa kolejne znaki tak dugo, a napotka pierwszy znak niebdcy znakiem
odstpu (ten znak zostanie odoony z powrotem do strumienia wejciowego).
Liczba znakw odstpu w cigu formatujcym nie musi dokadnie odpowiada
liczbie takich znakw w cigu wejciowym. Pojedynczy znak odstpu w cigu
formatujcym zostanie dopasowany do dowolnie dugiego podcigu takich
znakw w cigu wejciowym (co wicej, obecno znaku odstpu w cigu
formatujcym nie wymusza obecnoci takiego znaku w cigu wejciowym
znak odstpu w cigu formatujcym odpowiada dowolnie dugiemu podcigowi
takich znakw w cigu wejciowym, a wic rwnie podcigowi zerowemu).
funkcja scanf pominie pierwszy znak odstpu, szukajc liczby cakowitej, nastpnie dopasuje do %d podcig 5, dopasuje bezporednio znak /, pominie spacj
w poszukiwaniu kolejnej liczby cakowitej, a p niej dopasuje do %d podcig 96.
Ale jeli na wejciu pojawi si:
5/96
79
Funkcja scanf bdzie najpierw szuka podcigu wartoci typu int i wpisze t
warto pod zmienn i. Potem bdzie prbowaa dopasowa w cigu wejciowym
znak przecinka. Jeli w cigu wejciowym zamiast przecinka pojawi si spacja,
dziaanie funkcji zostanie przerwane i zmienna j nie otrzyma oczekiwanej wartoci.
Cigi formatujce dla funkcji printf czsto kocz si znakiem sterujcym \n,
wymuszajcym wstawienie do wyjcia nowego wiersza. Taki sam znak na kocu
cigu formatujcego scanf to zazwyczaj zy pomys. Dla scanf znak nowego
wiersza w cigu formatujcym jest rwnowany ze znakiem spacji; pomija go,
szukajc nastpnego znaku niebdcego znakiem odstpu. Jeli na przykad cig
formatujcy ma posta "%d\n", scanf pominie ewentualne pocztkowe znaki
odstpu, wczyta warto cakowit, a nastpnie bdzie w cigu wejciowym szuka
nastpnego znaku innego ni odstp. W przypadku programu interaktywnego moe
to doprowadzi do zawieszenia programu do czasu, kiedy uytkownik wprowadzi na wejcie znak inny ni odstp.
PROGRAM
Dodawanie uamkw
Aby zilustrowa zdolno funkcji scanf do dopasowywania wzorcw w cigach
znakw, we miemy na warsztat problem wczytania uamka wprowadzanego przez
uytkownika. Uamki s zwyczajowo reprezentowane przez zapis licznik/mianownik.
Zamiast zmusza uytkownika do nieintuicyjnego wprowadzania uamka jako
dwch osobnych liczb cakowitych, dziki funkcji scanf pozwolimy mu wprowadzi uamek w klasycznej postaci. Oto program ilustrujcy t technik przy
dodawaniu dwch uamkw:
80
Pytania i odpowiedzi
*P:
Widywaem specyfikatory w postaci %i, przeznaczone do wczytywania i wypisywania liczb cakowitych. Czym rni si od %d (s. 71)?
O:
P:
O:
Pytania i odpowiedzi
81
P:
Znak sterujcy \t ma wymusi na printf przesunicie kursora do nastpnej pozycji tabulacji. Skd wiadomo, jaka jest szeroko tabulatora (s. 74)?
O:
Nie wiadomo. Efekt wypisania na wyjciu znaku \t nie jest cile zdefiniowany
w standardzie jzyka C. Jest on zaleny od reakcji systemu operacyjnego na danie wypisania znaku tabulacji. Zazwyczaj szeroko tabulatora to osiem znakw,
ale C sam w sobie nie daje takiej gwarancji.
P:
O:
Wyjanimy to na przykadzie:
printf("Podaj liczb: ");
scanf("%d", &i);
wykrywanie bdw
w scanf 22.3
P:
O:
Okazuje si, e program nie wczytuje danych wejciowych bezporednio przy ich
wprowadzaniu. Cig wejciowy programu jest przechowywany w ukrytym buforze,
do ktrego odwouje si funkcja scanf. W takim ukadzie funkcja scanf moe
odoy znak z powrotem do bufora. Buforowanie wejcia bdzie omawiane
w rozdziale 22.
P:
O:
82
funkcja scanf wczyta znak 4 i podstawi warto 4 do zmiennej i. W poszukiwaniu nastpnego podcigu reprezentujcego liczb cakowit znajdzie przecinek.
Poniewa przecinek nie moe rozpoczyna zapisu liczby, funkcja przerwie dziaanie. Sam przecinek i druga wprowadzona liczba zostan do dyspozycji nastpnych wywoa scanf.
Oczywicie mona temu atwo zaradzi, instruujc uytkownika co do oczekiwanego formatu danych wejciowych, np. nakazujc mu zawsze oddzielanie liczb
przecinkiem:
printf("Podaj dwie liczby, oddzielone przecinkiem: ");
scanf("%d,%d", &i, &j);
wiczenia
Podrozdzia 3.1
Podrozdzia 3.2
1.
2.
Napisz wywoanie funkcji printf wywietlajcej warto zmiennej typu float w nastpujcych formatach:
(a) w zapisie wykadnikowym, wyrwnan do lewej w polu o szerokoci 8, z jedn cyfr
po przecinku;
(b) w zapisie wykadnikowym, wyrwnan do prawej w polu o szerokoci 10, z szecioma
cyframi po przecinku;
(c) w zapisie dziesitnym z przecinkiem, wyrwnan do lewej w polu o szerokoci 8, z trzema
cyframi po przecinku;
(d) w zapisie dziesitnym z przecinkiem, wyrwnan do prawej w polu o szerokoci 6,
bez cyfr po przecinku.
3.
Dla kadej z poniszych par cigw formatujcych scanf wska, czy oba cigi pary s sobie
rwnowane, czy nie. Jeli nie s, wska rnic w ich dziaaniu.
(a) "%d"
" %d"
(b) "%d-%d-%d" " %d -%d -%d"
(c) "%f"
"%f "
(d) "%f,%f"
"%f, %f"
* 4.
Zadania programistyczne
83
6.
Poka, jak mona przerobi program addfrac.c z podrozdziau 3.2, aby uytkownik mg
wprowadza uamki ze spacjami wok znaku dzielenia /.
Zadania programistyczne
1.
2.
3.
84
5.
6.
Przerb program addfrac.c z podrozdziau 3.2 tak, aby uytkownik za jednym zamachem
wprowadza oba uamki oddzielone znakiem +:
Podaj dwa uamki oddzielone znakiem plusa: 5/6+3/4
Suma uamkw wynosi 38/24
Wyra enia
Uywanie kalkulatorka to jeszcze nie programowanie,
a ju nie matematyka.
Jedn z waniejszych cech jzyka C jest nacisk, jaki jest w nim kadziony na wyraenia inaczej wzory, ktre mwi o sposobie obliczania wartoci. Wyraenia
s tu waniejsze ni instrukcje. Najprostsze wyraenia to zmienne i stae programu.
Zmienna reprezentuje warto, ktra jest obliczana w czasie dziaania programu
poprzez pobranie wartoci z pamici skojarzonej ze zmienn. Staa reprezentuje
warto znan ju w czasie kompilacji. Bardziej rozbudowane wyraenia obejmuj
operandy i operatory (przy czym operatory same w sobie s rwnie wyraeniami).
W wyraeniu a + (b * c) widzimy zastosowanie operatora + do operandw
a oraz (b + c), natomiast obie strony operatora + (oba operandy) s penoprawnymi wyraeniami.
Operatory s podstawowymi narzdziami budowania wyrae, a jzyk C
posiada bardzo bogaty zbir operatorw. Przede wszystkim C obsuguje podstawowe operatory obecne w wikszoci innych jzykw programowania:
Q
85
86
Rozdzia 4. Wyraenia
(podrozdzia 4.3). W podrozdziale 4.1 zajmiemy si te pierwszestwem i cznoci
operatorw te cechy operatorw s niezwykle istotne dla poprawnoci wyrae
obejmujcych wicej ni jeden operator. W podrozdziale 4.4 opisany zostanie sposb, w jaki nastpuje obliczanie wyrae w jzyku C. Wreszcie w podrozdziale 4.5
zajmiemy si tak zwan instrukcj wyraeniow konstrukcj sprawiajc,
e dowolne wyraenie moe odgrywa rol instrukcji w programie.
4.1.
Operatory arytmetyczne
Operatory arytmetyczne czyli operatory realizujce operacje dodawania,
odejmowania, mnoenia i dzielenia to istne woy robocze w wikszoci jzykw
programowania. C nie jest tu wyjtkiem. List operatorw arytmetycznych jzyka
C wymienia tabela 4.1.
Tabela 4.1.
Operatory
arytmetyczne
Z jednym operandem
Z dwoma operandami
Addytywne
Multiplikatywne
+ dodawanie
* mnoenie
- odejmowanie
/ dzielenie
% reszta z dzielenia
Operatory addytywne i multiplikatywne to tak zwane operatory dwuargumentowe (ang. binary), poniewa operuj na dwch operandach. Operatory jednoargumentowe (ang. unary) wymagaj tylko jednego operandu:
i = +1;
j = -i;
Jednoargumentowy operator + nie ma adnego dziaania. W rzeczy samej w specyfikacji K&R w ogle go nie uwzgldniono. Suy gwnie do zaznaczenia, e
staa liczbowa jest dodatnia.
Operatory dwuargumentowe nie powinny budzi wtpliwoci. Moe poza operatorem %, ktry oblicza reszt z dzielenia cakowitego. Warto wyraenia i %
j jest to reszta z cakowitego dzielenia i przez j. Na przykad wartoci wyraenia 10 % 3 jest 1, a wyraenie 12 % 4 ma warto 0.
Operatory dwuargumentowe z tabeli 4.1 z wyjtkiem operatora % mog
by stosowane zarwno do operandw bdcych liczbami cakowitymi, jak i do
operandw zmiennoprzecinkowych; dopuszczalne jest te mieszanie typw operandw. Kiedy w zasigu dziaania operatora wystpuj operandy typu int i float,
wynikiem dziaania operatora jest warto typu float. Dlatego wyraenie 9 +
2.5f daje 11,5, a 6.7f / 2 to 3.35.
Przy stosowaniu operatorw / i % naley zachowa pewn ostrono:
Q
Operator dzielenia / moe dawa nieoczekiwane wyniki. Ot kiedy oba operandy s wartociami cakowitymi, operator / obcina iloraz do najbliszej mniejszej wartoci cakowitej. Dlatego wyraenie 1 / 2 da warto 0.
niezdefiniowane
zachowanie programu 4.4
87
Operator % wymaga, aby operandy byy wartociami cakowitymi. Jeli ktrykolwiek z operandw % jest wartoci inn ni cakowita, program nie da
si skompilowa.
88
Rozdzia 4. Wyraenia
No dobrze, ale jak zinterpretowa wyraenie bez nawiasw? Czy dla kompilatora i + j * k to jest (i + j) * k, czy moe jednak i + (j *k)? Tak
jak w wielu innych jzykach, w C potencjalne wtpliwoci tego rodzaju rozstrzyga
si na bazie regu pierwszestwa (albo inaczej priorytetw) operatorw (ang.
precedence). Dla operatorw arytmetycznych pierwszestwo przedstawia si tak
(od najwyszego priorytetu):
+ - (jednoargumentowe)
* /
%
+ - (dwuargumentowe)
Operatory wymienione w tym samym wierszu (jak jednoargumentowe + i -)
cechuj si identycznym pierwszestwem.
Kiedy w jednym wyraeniu wystpuje wiele operatorw, moemy okreli
sposb ich interpretowania (kolejno obliczania podwyrae) przez kompilator,
posikujc si nawiasami grupujcymi podwyraenia, od operatorw o najwyszym priorytecie po operatory o priorytecie najniszym. Oto przykady:
i + j * k
-i * -j
+i + j / k
i + (j * k)
(-i) * (-j)
(+i) + (j / k)
Reguy pierwszestwa operatorw nie s jednak wystarczajce do rozstrzygnicia wtpliwoci co do kolejnoci obliczania podwyrae, kiedy w wyraeniu
wystpuje wiele operatorw o tym samym priorytecie. W takiej sytuacji zastosowanie ma regua cznoci operatorw (ang. associativity). O operatorze mwimy,
e jest lewostronnie czny, kiedy grupuje operandy od lewej do prawej. Dwuargumentowe operatory arytmetyczne (*, /, %, + i -) s czne lewostronnie, wic:
i - j - k
i * j / k
(i - j) - k
(i * j) / k
PROGRAM
-(+i)
89
Cyfry:
0 13800 15173 5
0 51500 24128
0 31200 01005
?
?
90
Rozdzia 4. Wyraenia
Napiszmy program, ktry bdzie oblicza cyfr kontroln dla dowolnego kodu
UPC. Program bdzie wymaga od uytkownika wprowadzenia pierwszych 11
cyfr kodu UPC, a nastpnie wypisze brakujc cyfr kodu. Aby unikn omyek,
nakaemy uytkownikowi wprowadzanie cyfr kodu partiami: najpierw wprowadzi
samotn cyfr z lewej, potem grup piciu cyfr kodu producenta, a na koniec pitk
cyfr kodu produktu. Sesja z programem bdzie wygldaa mniej wicej tak:
Podaj
Podaj
Podaj
Cyfra
4.2.
91
Operatory przypisania
Po obliczeniu wartoci wyraenia czsto chcemy zachowa t warto w zmiennej
do p niejszego uycia. W jzyku C suy do tego celu operator przypisania
(ang. assignment) w postaci symbolu =. Aby dao si wygodnie aktualizowa
warto zmiennej wartoci wyraenia, C oferuje rwnie zestaw tak zwanych
zoonych operatorw przypisania (ang. compound assignment operators).
Przypisania proste
Efektem przypisania v = e jest obliczenie wyraenia e i skopiowanie wartoci
wyraenia do v. Jak wida na przykadach poniej, e moe by zmienn, sta albo
dowolnym wyraeniem:
i = 5;
j = i;
k = 10 * i + j;
/* i ma teraz warto 5 */
/* j ma teraz warto 5 */
/* k ma teraz warto 55 */
Jeli e i v nie s wartociami tego samego typu, w toku realizacji przypisania warto e zostanie skonwertowana na typ v:
int i;
float f;
i = 72.99f;
f = 136;
przypisania z konwersj 7.4
/* i ma teraz warto 72 */
/* f ma teraz warto 136.0 */
Efekty uboczne
Zazwyczaj nie oczekuje si od operatorw, aby modyfikoway wartoci operandw w matematyce operatory nie maj przecie takich waciwoci. Dziaanie
i + j nie modyfikuje ani i, ani j, a jedynie oblicza sum i oraz j.
Wikszo operatorw jzyka C rwnie nie modyfikuje operandw, ale niektre to robi. O takich operatorach mwimy, e maj efekty uboczne, poniewa ich
dziaanie nie ogranicza si tylko do jawnego wyliczenia wartoci. Pierwszym operatorem, jaki poznajemy od strony efektw ubocznych, jest prosty operator przypisania nie tylko oblicza warto wyraenia prawego operandu, ale take modyfikuje warto lewego operandu. Obliczenie wartoci wyraenia i = 0 daje warto
0, a efektem ubocznym obliczenia wyraenia jest przypisanie 0 do i.
92
Rozdzia 4. Wyraenia
Poniewa przypisanie jest operatorem, moemy konstruowa acuchowe wyraenia z operatorami przypisania:
i = j = k = 0;
L-wartoci
Wikszo operatorw jzyka C pozwala, aby w roli operandw wystpoway
zmienne, stae albo wyraenia (rwnie zawierajce inne operatory). Operator
przypisania jest o tyle wyjtkowy, e wymaga, aby lewy operand by tak zwan
l-wartoci (ang. lvalue). L-warto reprezentuje obiekt przechowywany w pamici
komputera. Nie moe to by staa ani na przykad wynik porwnania. L-wartociami s wszystkie zmienne. Wyraenia w rodzaju 10 albo 2 * i l-wartociami
nie s. Na razie jedyne l-wartoci, ktre znamy, to wanie zmienne. W dalszych
rozdziaach powiemy sobie take o innych l-wartociach.
93
Takie bdne przypisania s wykrywane przez kompilator prba skompilowania powyszych instrukcji zaowocuje bdem kompilacji z komunikatem invalid
lvalue in assignment (niepoprawna l-warto w przypisaniu).
Przypisania zoone
W programach pisanych w jzyku C czsto widzi si przypisania, ktre przy obliczaniu wartoci przypisania bazuj na poprzedniej wartoci modyfikowanej zmiennej. Oto przykadowe przypisanie dodajce 2 do biecej wartoci zmiennej i:
i = i + 2;
i += 2;
*=
/=
%=
v
v
v
v
v
+=
-=
*=
/=
%=
e
e
e
e
e
94
Rozdzia 4. Wyraenia
(kompilator nie zgosi bdu), ale niepoprawne ze wzgldu na oczekiwane dziaanie. Jeli na przykad zamierzamy napisa i += j, ale omykowo napiszemy
i =+ j, program jak najbardziej si skompiluje. Niestety, zamiast doda do i
warto j, wymusimy wykonanie wyraenia i = (+j), czyli skopiujemy j do i.
Operatory przypisa zoonych maj te same waciwoci co operatory przypisa prostych. W szczeglnoci s prawostronnie czne, wic instrukcja:
i += j += k;
oznacza:
i += (j += k);
4.3.
Ale C oferuje jeszcze krtszy zapis tych operacji przy uyciu operatorw ++
(inkrementacja) i -- (dekrementacja).
Na pierwszy rzut oka operatory inkrementacji i dekrementacji to wcielona
prostota: ++ dodaje jeden, a -- odejmuje jeden od operandu. Niestety, prostota
jest tu mylca. Operatory inkrementacji i dekrementacji bywaj nieoczywiste
w uyciu. Wynika to z tego, e operatory inkrementacji (++) i dekrementacji (--)
mog wystpowa w postaci przedrostkowej (np. ++i, --i) albo przyrostkowej
(np. i++, i--). Waciwy wybr rodzaju operatora silnie wpywa na poprawno
programu.
Kolejna komplikacja tkwi w fakcie, e operatory ++ i -- posiadaj efekty
uboczne (tak jak operatory przypisania) modyfikuj warto operandu. Obliczenie wyraenia ++i (inkrementacja przedrostkowa albo tzw. przed-inkrementacja i)
daje warto i + 1 oraz w ramach efektu ubocznego zwiksza i o jeden:
i = 1;
printf("i to %d\n", ++i);
printf("i to %d\n", i);
95
Pierwsza instrukcja z wywoaniem printf wypisze na wyjciu pierwotn warto i, jeszcze sprzed inkrementacji. Drugie wywoanie printf wypisze now
warto i. Jak wida, ++i oznacza zwiksz natychmiast warto i, a i++ oznacza daj biec warto i, a potem zwiksz i. Co to znaczy potem? Standard jzyka C nie precyzuje dokadnego momentu wykonania inkrementacji operatora przyrostkowego, ale mona bezpiecznie zaoy, e i bdzie miao now
warto jeszcze przed wykonaniem nastpnej instrukcji.
Podobne waciwoci ma operator dekrementacji --:
i = 1;
printf("i to %d\n", --i);
printf("i to %d\n", i);
i = 1;
printf("i to %d\n", i--);
printf("i to %d\n", i);
96
Rozdzia 4. Wyraenia
4.4.
Tabela 4.2.
Czciowa lista
operatorw jzyka C
Priorytet
Nazwa
Symbol
inkrementacja (przyrostkowa)
dekrementacja (przyrostkowa)
++
inkrementacja (przedrostkowa)
dekrementacja (przedrostkowa)
jednoargumentowy plus
jednoargumentowy minus
++
-+
-
multiplikatywne
addytywne
przypisania
=
+=
*=
-=
czno
lewostronna
-prawostronna
lewostronna
lewostronna
/=
%=
prawostronna
Takie wyraenie byoby znacznie czytelniejsze, gdyby zawierao nawiasy grupujce podwyraenia. Z pomoc tabeli 4.2 moemy atwo samodzielnie uzupeni
pogrupowa wyraenie wystarczy znale w wyraeniu operator o najwyszym priorytecie i wraz z operandami uj go w nawias. Od tej pory bdzie dla nas
pojedynczym operandem. Technik powtarzamy do momentu rozpoznania wszystkich podwyrae.
W naszym przykadzie operatorem o najwyszym priorytecie jest ++, wystpujcy tu jako operator przyrostkowy. Ujmujemy operator z operandami w nawias:
a = b += (c++) - d + --e / -f
Nastpny wedug pierwszestwa jest operator -- (przedrostkowy) oraz jednoargumentowy minus (oba maj priorytet 2.):
a = b += (c++) - d + (--e) / (-f)
Drugi znak minusa w wyraeniu ma operand po lewej stronie, wic jest operatorem
odejmowania, a nie kolejnym jednoargumentowym minusem.
Nastpnie odnajdujemy operator / (priorytet 3.):
a = b += (c++) - d + ((--e) / (-f))
97
Zostay ju tylko przypisania, oba przy operandzie b, wic znw trzeba si
posikowa cznoci. Operatory przypisania s czne prawostronnie (od prawej
do lewej), zatem najpierw ujmujemy w nawias podwyraenie z operatorem +=,
a potem podwyraenie z operatorem =:
(a = (b += (((c++) - d) + ((--e) / (-f)))))
98
Rozdzia 4. Wyraenia
Aby zapobiec tego rodzaju problemom, najlepiej unika stosowania operatora
przypisania w podwyraeniu. Zamiast tego naley wykona przypisania jako osobne
instrukcje. Na przykad nasze wtpliwe wyraenie moe zosta rozbite nastpujco:
a
b
a
c
=
=
=
=
5;
a + 2;
1;
b - a;
4.5.
Pytania i odpowiedzi
99
W drugim przykadzie warto i jest pobierana, ale znw nie bdzie nigdzie
wykorzystana. Za to samo i ju po pobraniu wartoci zostanie zwikszone o jeden:
i++;
Taka instrukcja nie ma adnego efektu ubocznego, nie zmienia adnego z operandw, wic jest zwyczajnie bezcelowa.
Bezcelow, a wic pust instrukcj wyraeniow mona atwo popeni przez
prost literwk. Wystarczy, e zamiast:
i = j;
przypadkiem napiszemy:
i + j;
(taki bd jest prawdopodobny tym bardziej, e znaki + i = zajmuj ten sam klawisz
na klawiaturze). Niektre kompilatory wykrywaj bezcelowe instrukcje wyraeniowe, generujc przy nich ostrzeenia w rodzaju instrukcja bez efektu (statement with no effect).
Pytania i odpowiedzi
P:
Zauwayem, e jzyk C nie posiada operatora potgowania. Jak mam podnosi liczby do potgi?
O:
Jeli wykadnik potgi jest niewielk dodatni liczb cakowit, potgowanie najlepiej zrealizowa przez wielokrotne mnoenie (np. i * i * i dla obliczenia
szecianu i). Do obliczania potg o wykadnikach niecakowitych najlepiej wykorzysta funkcj pow.
100
Rozdzia 4. Wyraenia
P:
O:
P:
Dlaczego dziaanie operatorw dzielenia (/) i reszty z dzielenia (%) dla ujemnych operandw jest tak zagmatwane (s. 87)?
O:
Zasady dziaania tych operatorw nie s tak zagmatwane, jakby si wydawao.
W obu wersjach standardu celem jest zapewnienie, eby warto (a / b) *
b + a % b zawsze bya rwna a (i faktycznie, oba standardy gwarantuj tak
zaleno, o ile tylko warto a / b jest wartoci reprezentowaln). Problem
polega na tym, e zaoon zaleno mona speni na dwa sposoby, przy rnych
metodach obliczania a / b i a % b. Wedug C89 albo -9 / 7 to 1 i -9 % 7
to 2 (rwno jest speniona), albo -9 / 7 to 2 i -9 % 7 to 5 (i znw rwno jest speniona). W pierwszym przypadku (-9 / 7) * 7 + -9 % 7 daje
17+2 = 9, w drugim przypadku (-9 / 7) * 7 + -9 % 7 to 27+
5 = 9. Do czasu pojawienia si standardu C99 wikszo procesorw wykonywaa ju dzielenie cakowite z obcinaniem w kierunku zera, wic tak wanie
regu dzielenia zapisano w standardzie C99 jako jedyn dozwolon warto
ilorazu z operandem ujemnym.
P:
O:
W rzeczy samej. L-warto jest wyraeniem, ktre jest dozwolone po lewej stronie
operatora przypisania; r-warto to wyraenie, ktre jest dozwolone po prawej
stronie. R-warto moe wic by zmienn, sta albo dowolnym wyraeniem.
W niniejszej ksice, podobnie jak w standardzie jzyka C, bdziemy trzyma
si okrelenia wyraenie, ktre jednak do dobrze oddaje istot r-wartoci.
*P:
O:
Warto i jest rwnoczenie pobierana i modyfikowana w obrbie jednej instrukcji, wic wynik wykonania takiej instrukcji jest niezdefiniowany. Jest prawdopodobne, e i zostanie zwikszone dwukrotnie, ale w istocie nie mona nic pewnego
powiedzie o dziaaniu takiego programu.
P:
W jakim celu w C udostpniono operatory ++ i --? Czy s one szybsz metod inkrementacji i dekrementacji zmiennej, czy s jedynie wygodniejsze
(krtsze w zapisie) (s. 94)?
O:
Pytania i odpowiedzi
101
stay si sol jzyka C (bazuje na nich wiele jego sawnych idiomw). W nowoczesnych kompilatorach stosowanie ++ i -- zapewne ani bardzo nie przyspieszy
programu, ani nie zmniejszy bardzo rozmiaru pliku wynikowego. Nieustajca
popularno tych operatorw wynika chyba z ich zwartoci.
P:
O:
Tak, operacje inkrementacji i dekrementacji mona stosowa do wartoci cakowitych i zmiennoprzecinkowych, jednak w praktyce mao kto prbuje inkrementowa
albo dekrementowa zmienn typu float.
*P:
Kiedy dokadnie nastpuje zwikszenie wartoci operandu w przypadku przyrostkowych wersji ++ i -- (s. 95)?
O:
wietne pytanie. Niestety, nie mona na nie atwo odpowiedzie. Standard jzyka
C wprowadza pojcie tak zwanego punktu sekwencji i mwi, e aktualizacja
skadowanej wartoci operandu powinna odby si pomidzy poprzednim a nastpnym punktem sekwencji. W jzyku C okrelono kilka rnych punktw sekwencji. Jednym z nich jest koniec instrukcji wyraeniowej na kocu instrukcji
wyraeniowej wszystkie op nione inkrementacje i dekrementacje powinny zosta
wykonane; nie moe doj do rozpoczcia wykonywania nastpnej instrukcji
z pominiciem tego kroku.
Niektre operatory, o ktrych powiemy sobie w dalszej czci ksiki (logiczny
operator and, logiczny operator or, operator warunkowy i operator przecinka),
rwnie stanowi punkty sekwencji. To samo dotyczy wywoa funkcji argumenty wywoania funkcji musz by w peni obliczone przed wykonaniem wywoania. Jeli argument wywoania jest wyraeniem zawierajcym przyrostkowy
operator ++ albo --, inkrementacja bd dekrementacja musi zosta wykonana
jeszcze przed wykonaniem wywoania funkcji.
P:
O:
O:
Pamitajmy, e przypisanie jest w jzyku C operatorem i jak kady operator generuje warto. Przypisanie:
i = 1;
102
Rozdzia 4. Wyraenia
wiczenia
Podrozdzia 4.1
1.
* 2.
Czy wyraenie (-i)/j bdzie miao zawsze t sam warto co -(i/j), jeli i i j s
dodatnimi wartociami cakowitymi? Uzasadnij odpowied .
3.
Jaka bdzie warto poniszych wyrae wedug standardu C89 (jeli moliwa jest wicej
ni jedna warto, podaj wszystkie warianty):
(a) 8 / 5
(b) -8 / 5
(c) 8 / -5
(d) -8 / -5
4.
5.
Jaka bdzie warto poniszych wyrae wedug standardu C89 (jeli moliwa jest wicej
ni jedna warto, podaj wszystkie warianty):
(a) 8 % 5
(b) -8 % 5
(c) 8 % -5
(d) -8 % -5
6.
7.
Podrozdzia 4.2
8.
Czy program upc.c bdzie wci poprawny, jeli wyraenie 9 ((total 1) % 10) zostanie
zastpione przez (10 - (total % 10)) % 10?
9.
wiczenia
103
(a) i = 7; j = 8;
i *= j + 1;
printf("%d %d", i, j);
(b) i = j = k = 1;
i += j += k;
printf("%d %d %d", i, j, k);
(c) i = 1; j = 2; k = 3;
i -= j -= k;
printf("%d %d %d", i, j, k);
(d) i = 2; j = 1; k = 0;
i *= j *= k;
printf("%d %d %d", i, j, k);
Podrozdzia 4.3
10.
*11.
12.
104
Rozdzia 4. Wyraenia
(c) i = 7;
j = 3 * i-- + 2;
printf("%d %d", i, j);
(d) i = 7;
j = 3 + --i * 2;
printf("%d %d", i, j);
13.
Ktre z wyrae: ++i czy i++ jest dokadnie rwnowane wyraeniu (i += 1)? Uzasadnij odpowied .
Podrozdzia 4.4
14.
Podrozdzia 4.5
15.
Zadania programistyczne
1.
2.
3.
Przerb program z zadania 2. tak, eby program wypisywa odwrotny zapis liczby trzycyfrowej, bez uycia operacji arytmetycznych do podziau liczby na cyfry. Wskazwka: Zajrzyj do programu upc.c z podrozdziau 4.1.
4.
Zadania programistyczne
5.
105
Przerb program upc.c z podrozdziau 4.1 tak, aby uytkownik wprowadza 11 cyfr kodu
UPC za jednym zamachem:
Podaj 11 cyfr kodu UPC: 01380015173
Cyfra kontrolna: 5
6.
W krajach europejskich stosuje si kody kreskowe z 13 cyframi (tzw. kod EAN). Kady
kod EAN koczy si cyfr kontroln (tak jak UPC). Algorytm obliczania cyfry kontrolnej
kodu EAN rwnie jest do podobny:
Dodaj drug, czwart, szst, sm, dziesit i dwunast cyfr kodu.
Dodaj pierwsz, trzeci, pit, sidm, dziewit i jedenast cyfr kodu.
Pomn pierwsz sum przez 3 i dodaj j do drugiej sumy.
Od sumy odejmij 1.
Oblicz reszt z dzielenia pomniejszonej sumy przez 10.
Odejmij wynik od 9.
Na przykad tureckie sodycze Glloglu Turkish Delight Pistachio & Coconut maj kod
EAN 8691484260008. Pierwsza suma to 6+1+8+2+0+0 = 17, a druga suma to 8+9+4+4+
6+0 = 31. Suma iloczynu pierwszej sumy przez 3 i drugiej sumy daje 82. Po odjciu 1 zostaje 81. Reszta z dzielenia 81 przez 10 to 1. 9 1 daje 8. Zgadza si, cyfra kontrolna naszego
kodu to dokadnie 8. Zadanie polega na przerobieniu programu upc.c z podrozdziau 4.1
tak, aby oblicza cyfr kontroln kodu EAN. Uytkownik powinien wprowadza do programu
pierwsze 12 cyfr kodu EAN jednym cigiem:
Podaj 12 cyfr kodu EAN: 869148426000
Cyfra kontrolna: 8