You are on page 1of 52

Id do

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: helion@helion.pl
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

Jzyk C yje i ma si dobrze. Sprawd, co nowego w wersji C99!


Jak wyglda proces standaryzacji jzyka?
Jak komentowa kod?
Jak przygotowa projekt programu?
Jzyk C naley do nielicznej grupy jzykw, ktre sprawdzaj si w rodowiskach produkcyjnych,
a jednoczenie nadaj si do nauki programowania na uczelniach wyszych. Dziki logicznej
i przejrzystej skadni, jasno okrelonym zasadom wykorzystania oraz ogromnym moliwociom
jzyk ten pomimo swojego wieku cieszy si popularnoci i uznaniem. Nawet dzi, kiedy na rynku
panuj niepodzielnie Java oraz .NET, jzyk C znalaz swoj nisz i wietnie j wypenia. Na tym polu
aden wspczesny jzyk nie ma z nim adnych szans!
Kolejne wydanie ksiki rozszerzono midzy innymi o elementy zawarte w specyfikacji oznaczonej
numerem C99 (ISO 9899:1999). Co jeszcze wyrnia t ksik? Jej pierwsze wydanie byo
wykorzystywane na kursach programowania prowadzonych przez 225 uczelni. Dziki temu
zaliczana jest ona do najbardziej znaczcych wydawnictw dotyczcych jzyka C. Wydanie drugie
powiela zalety pierwszego, a dodatkowo zostao rozbudowane o jeszcze wiksz liczb przykadw,
pyta, wicze i zada programistycznych.
W trakcie pasjonujcej lektury zgadza si, K.N. King potrafi w ten sposb pisa o swoim ulubionym
jzyku poznasz wszystkie aspekty programowania w jzyku C, poczwszy od jego historii, poprzez
fundamentalne pojcia funkcji, zmiennych, a skoczywszy na zarzdzaniu pamici oraz wykorzystaniu
wskanikw. Jzyk C. Nowoczesne programowanie. Wydanie II to obowizkowa pozycja dla
kadego studenta poznajcego tajniki tego jzyka. Programici znajcy jzyk C niewtpliwie
doceni kunszt autora, a ksika znajdzie zastosowanie jako przekrojowy przewodnik taka
pozycja powinna by na pce kadego programisty!
Poznaj jzyk C, korzystajc z uznanego podrcznika!

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.

Piszemy prosty program


Wywietlamy cytat
Kompilacja i konsolidacja
Zintegrowane rodowiska programistyczne
Oglna posta prostego programu w C
Dyrektywy preprocesora
Funkcje
Instrukcje
Wypisywanie cigw znakowych
Komentarze
Zmienne i przypisania
Typy
Deklaracje
Przypisania
Wypisywanie wartoci zmiennej
Obliczanie gabarytu przesyki
Inicjalizacja
Wypisywanie wartoci wyrae

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

Kolejno obliczania podwyrae


Instrukcje wyra eniowe

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.

Wyra enia logiczne


Operatory relacji
Operatory porwna
Operatory logiczne
Instrukcja if
Instrukcje blokowe
Klauzula else
Kaskadowe instrukcje if
Obliczanie prowizji brokera giedowego
Problem bezpaskiego else
Wyraenia warunkowe
Wartoci boolowskie w C89
Wartoci boolowskie w C99
Instrukcja switch
Rola instrukcji break
Wypisywanie daty w zapisie urzdowym

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.

Definiowanie i wywoywanie funkcji


Obliczanie rednich
Odliczanie
Wywietlanie napisu (kolejne podejcie)
Definicja funkcji
Wywoanie funkcji
Sprawdzanie, czy podana liczba jest liczb pierwsz
Deklaracja funkcji
Argumenty
Konwersje argumentw
Argumenty tablicowe
Parametry tablicowe o zmiennym rozmiarze
Deklaracje parametrw tablicowych ze sowem static
Literay tablicowe
Instrukcja return
Zako
czenie programu
Funkcja exit
Rekurencja
Algorytm quicksort
quicksort

10. ORGANIZACJA PROGRAMU


10.1. Zmienne lokalne
Zmienne statyczne funkcji
Parametry
10.2. Zmienne zewntrzne
Przykad. Stos implementowany na zmiennych zewntrznych
Zalety i wady zmiennych zewntrznych
Zgadywanka liczbowa
10.3. Bloki
10.4. Zasig zmiennych
10.5. Organizacja programu w C
Sia rozdania pokerowego

261
261
262
263
263
263
264
266
270
271
272
273

Spis treci
11. WSKA NIKI
11.1. Zmienne wska nikowe
Deklarowanie zmiennych wska
nikowych
11.2. Operator adresu i wyuskania
Operator adresu
Operator wyuskania
11.3. Przypisania a wska niki
11.4. Wska niki jako argumenty funkcji
Wyszukiwanie najwikszego i najmniejszego elementu tablicy
Ochrona argumentw za pomoc const
11.5. Wska niki jako wartoci zwracane

12. WSKA NIKI A TABLICE


12.1. Arytmetyka wska nikw
Dodawanie liczby do wska
nika
Odejmowanie liczby od wska
nika
Odejmowanie wska
nika od wska
nika
Porwnywanie wska
nikw
Wska
niki do literaw tablicowych
12.2. Przetwarzanie tablic na bazie wska nikw
czenie operatorw * i ++
12.3. Nazwa tablicy jako wska nik
Odwracanie szeregu liczbowego
Argumenty tablicowe (ponownie)
Wska
nik jako nazwa tablicy
12.4. Wska niki a tablice wielowymiarowe
Przetwarzanie elementw tablicy wielowymiarowej
Przetwarzanie wierszy tablicy wielowymiarowej
Przetwarzanie kolumn tablicy wielowymiarowej
Nazwa tablicy wielowymiarowej jako wska
nik
12.5. Wska niki a tablice o zmiennym rozmiarze (C99)

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

15. DUE PROGRAMY


15.1. Pliki rdowe
15.2. Pliki nagwkowe
Dyrektywa #include
Wsplne makrodefinicje i synonimy typw
Wsplne prototypy funkcji
Wsplne deklaracje zmiennych
Zagniedone dyrektywy #include
Ochrona plikw nagwkowych
Dyrektywy #error w plikach nagwkowych
15.3. Podzia programu na pliki
Formatowanie tekstu
15.4. Budowanie programu z wielu plikw
Pliki Makefile
Bdy konsolidowania programu

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

16. STRUKTURY, UNIE I WYLICZENIA


16.1. Zmienne strukturalne
Deklarowanie zmiennych strukturalnych
Inicjalizowanie zmiennych strukturowych
Inicjalizatory desygnowane
Operacje na strukturach
16.2. Typy strukturowe
Deklarowanie znacznika struktury
Definiowanie typu strukturowego
Struktury jako argumenty i wartoci zwracane funkcji
Literay strukturowe
16.3. Tablice i struktury zagnie d one
Struktury struktur
Tablice struktur
Inicjalizowanie tablic struktur
Zarzdzanie baz danych magazynu
16.4. Unie
Unie dla oszczdnoci
Unie jako mieszane struktury danych
Pole wyrnika w unii
16.5. Wyliczenia
Znaczniki i typy wyliczeniowe
Wyliczenia jako liczby cakowite
Wyliczenia jako wyrniki unii

17. ZAAWANSOWANE ZASTOSOWANIA WSKA NIKW


17.1. Dynamiczny przydzia pamici
Funkcje przydziau pamici
Wska
niki puste
17.2. Dynamiczny przydzia cigw znakw
Przydzia pamici dla cigu znakw za pomoc funkcji malloc
Przydziay dynamiczne
w funkcjach operujcych na cigach znakw
Tablice cigw przydzielanych dynamicznie
Wypisywanie notatek kalendarzowych (podejcie drugie)
17.3. Tablice przydzielane dynamicznie
Przydzia pamici dla cigu znakw za pomoc funkcji malloc
Funkcja calloc
Funkcja realloc
17.4. Zwalnianie pamici
Funkcja free
Problem wiszcych wska
nikw
17.5. Listy elementw
Deklarowanie typu wza
Tworzenie wza listy
Operator ->
Wstawianie wza na pocztek listy
Przeszukiwanie listy
Usuwanie wza z listy

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)
Wska niki do wska nikw
Wska niki do funkcji
Wska
niki do funkcji w roli argumentw
Funkcja qsort
Inne zastosowania wska
nikw do funkcji
Tablice funkcji trygonometrycznych
Wska niki 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

19. PROJEKT PROGRAMU


19.1. Moduy
Spjno i wspzaleno
Rodzaje moduw
19.2. Ukrywanie informacji
Modu obsugi stosu
19.3. Abstrakcyjne typy danych
Hermetyzacja
Typy niepene
19.4. Stos jako abstrakcyjny typ danych (ADT)
Definiowanie interfejsu stosu w wersji ADT
Implementacja stosu w wersji ADT (na bazie tablicy)
Zmiana typu elementu w stosie w wersji ADT
Implementowanie stosu ADT (na bazie tablicy dynamicznej)
Implementowanie stosu ADT (na bazie listy)
19.5. Problemy projektowe przy ADT
Nomenklatura
Obsuga bdw
Uniwersalny typ ADT
ADT w nowszych jzykach programowania

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

21. BIBLIOTEKA STANDARDOWA


21.1. Stosowanie biblioteki standardowej
Nazewnictwo w bibliotece standardowej
Funkcje ukrywane przez makrodefinicje
21.2. Przegld biblioteki standardowej C89
Diagnostyka
Obsuga znakw
Bdy
Cechy typw zmiennoprzecinkowych
Rozmiary typw cakowitoliczbowych
Lokalizacja programw
Matematyka
Skoki nielokalne
Obsuga sygnaw
Zmienne listy argumentw
Podstawowe definicje
Wejcie-wyjcie
Narzdzia
Obsuga cigw znakw
Daty i godziny
21.3. Uzupenienia i zmiany w C99
Arytmetyka liczb zespolonych
rodowisko implementacji zmiennoprzecinkowej
Znakowe konwersje typw cakowitoliczbowych
Alternatywny zapis skadni C
Wartoci i typy logiczne
Typy cakowitoliczbowe
Matematyka na uniwersalnych typach
Operacje na znakach wielobajtowych
Narzdzia mapowania i klasyfikacji znakw wielobajtowych
21.4. Nagwek <stddef.h> definicje podstawowe
21.5. Nagwek <stdbool.h> (C99) typy i wartoci logiczne

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

23. OBSUGA LICZB I DANYCH ZNAKOWYCH


23.1. Nagwek <float.h> cechy typw zmiennoprzecinkowych
23.2. Nagwek <limits.h> rozmiary typw cakowitych
23.3. Nagwek <math.h> matematyka
Bdy
Funkcje trygonometryczne
Funkcje hiperboliczne
Funkcje wykadnicze i logarytmiczne
Funkcje potgowe
Najblisza liczba cakowita, warto bezwzgldna,
reszta z dzielenia

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

24. OBSUGA BDW


24.1. Nagwek <assert.h> diagnostyka
24.2. Nagwek <errno.h> bdy
Funkcje perror i strerror
24.3. Nagwek <signal.h> obsuga sygnaw
Makrodefinicje sygnaw
Funkcja signal
Predefiniowane funkcje obsugi sygnaw
Funkcja raise
Testowanie mechanizmu sygnaw
24.4. Nagwek <setjmp.h> skoki nielokalne
Testowanie setjmp/longjmp

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

26.1. Nagwek <stdarg.h> zmienna liczba argumentw


Wywoanie funkcji o zmiennej liczbie argumentw
Funkcje vprintf
Funkcje vscanf
26.2. Nagwek <stdlib.h> inne narzdzia
Funkcje konwersji liczbowych
Testowanie funkcji konwersji liczbowych
Funkcje sekwencji pseudolosowych
Testowanie funkcji generowania liczb pseudolosowych
Komunikacja ze rodowiskiem wykonawczym
Wyszukiwanie i sortowanie
Okrelanie odlegoci
Funkcje arytmetyki liczb cakowitych
26.3. Nagwek <time.h> daty i godziny
Funkcje operujce na datach i godzinach
Funkcje konwersji dat i godzin
Wypisywanie daty i godziny

755
758
758
759
760
761
762
764
765
766
768
769
770
771
772
774
778

27. ROZSZERZONE OPERACJE MATEMATYCZNE W C99

787

27.1. Nagwek <stdint.h> typy cakowite


Typy nagwka <stdint.h>
Ograniczenia typw o okrelonym rozmiarze
Ograniczenia pozostaych typw cakowitych
Makrodefinicje dla staych cakowitych

788
788
790
790
791

Spis treci

17

27.2. Nagwek <inttypes.h> konwersje typw cakowitych


Makrodefinicje dla specyfikatorw konwersji
Funkcje obsugi najszerszych typw
27.3. Liczby zespolone (C99)
Definicja liczb zespolonych
Arytmetyka liczb zespolonych
Typy zespolone w C99
Operacje na wartociach zespolonych
Reguy konwersji dla typw zespolonych
27.4. Nagwek <complex.h> (C99) arytmetyka
liczb zespolonych
Makrodefinicje nagwka <complex.h>
CX_LIMITED_RANGE
Funkcje nagwka <complex.h>
Funkcje trygonometryczne
Funkcje hiperboliczne
Funkcje wykadnicze i logarytmiczne
Funkcje potgowe i funkcje wartoci bezwzgldnych
Inne
Szukanie pierwiastkw rwnania kwadratowego
27.5. Nagwek <tgmath.h> (C99) matematyka bez typw
Makrodefinicje rozprowadzajce wywoania
funkcji matematycznych
Wywoania makrodefinicji rozprowadzajcych
27.6. Nagwek <fenv.h> (C99)
rodowisko zmiennoprzecinkowe
Stany i tryby jednostki zmiennoprzecinkowej
Makrodefinicje nagwka <fenv.h>
FENV_ACCESS
Funkcje wyjtkw zmiennoprzecinkowych
Funkcje zaokrglania
Funkcje rodowiska

792
792
793
795
795
797
797
798
798

810
810
811
811
813
814
815

Dodatek A

Operatory jzyka C

819

Dodatek B

C99 kontra C89

821

Dodatek C

C89 kontra K&R

827

Dodatek D

Funkcje biblioteki standardowej

831

Dodatek E

Zestaw znakw ASCII

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.

Do najczciej wykorzystywanych funkcji bibliotecznych jzyka C nale printf


i scanf, suce do obsugi formatowanego wejcia i wyjcia programu. W tym
rozdziale przekonasz si o moliwociach tych funkcji, ale te o koniecznej ostronoci w ich stosowaniu. W podrozdziale 3.1 zajmiemy si funkcj printf. Gwnym zagadnieniem podrozdziau 3.2 bdzie funkcja scanf. W adnym z podrozdziaw nie zgbimy jednak wszystkich detali niektre bd musiay
poczeka do rozdziau 22.

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

Rozdzia 3. Formatowanie wejcia-wyjcia


przekazanej wartoci z jej reprezentacji wewntrznej (binarnej) na reprezentacj
drukowan (znakow) std pojcie specyfikatora konwersji. Na przykad
specyfikator konwersji %d mwi, e printf ma zamieni przekazan warto
typu int z jej reprezentacji binarnej na cig znakw kolejnych cyfr. Podobnie
specyfikator %f nakazuje zamian wartoci zmiennoprzecinkowej (typu float)
na znakow.
Zwyczajne znaki zawarte w cigu formatujcym s wypisywane bez modyfikacji. Specyfikatory konwersji s natomiast zastpowane przez znakowe reprezentacje przekazanych wartoci. We my nastpujcy przykad:
int i, j;
float x, y;
i
j
x
y

=
=
=
=

10;
20;
43.2892f;
5527.0f;

printf("i = %d, j = %d, x = %f, y = %f\n", i, j, x, y);

Takie wywoanie funkcji printf spowoduje wypisanie na wyjciu:


i = 10, j = 20, x = 43.289200, y = 5527.000000

Zwyczajne znaki w cigu formatujcym zostay po prostu skopiowane na wyjcie.


Cztery specyfikatory konwersji zostay za zastpione przez odpowiednio reprezentowane wartoci zmiennych i, j, x i y (w tej kolejnoci).

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

3.1. Funkcja printf

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);

/*** LE ***/

Funkcja printf wypisze poprawnie warto zmiennej i, a nastpnie wypisze


drug oczekiwan, ale nieokrelon warto liczbow. Podobnie kopotliwy
jest nadmiar wartoci w stosunku do specyfikatorw konwersji:
printf("%d\n", i, j);

/*** LE ***/

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);

/*** LE ***/

Poniewa funkcja printf realizuje wytyczne z cigu formatujcego, wypisze


na wyjciu warto float, a za ni warto int. Niestety, obie wartoci jako le
zinterpretowane bd niepoprawne.
spowoduje wypisanie wartoci 123 jako 123 (w caym biecym rozdziale
w miejsce niewidocznych spacji ilustrujcych formatowanie wartoci bd wstawiane znaki ). Z kolei kiedy wypisywana warto bdzie dusza ni m znakw,
pole zostanie automatycznie poszerzone do szerokoci potrzebnej do zmieszczenia
wartoci: specyfikator %4d dla wartoci 12345 spowoduje wypisanie na wyjciu
12345 bez ucinania cyfr. Znak minusa przed m wymusza wyrwnanie wartoci
w polu do lewej strony: specyfikator %4d dla wartoci 123 da na wyjciu 123.
Znaczenie specyfikatora precyzji p jest trudniejsze do opisania, poniewa jego
dziaanie jest zalene od X, czyli waciwego specyfikatora konwersji. X okrela
rodzaj konwersji do przeprowadzenia przed wypisaniem wartoci. Do najpopularniejszych konwersji wartoci liczbowych nale:
Q

d konwersja wartoci cakowitej do postaci dziesitnej. W takim ukadzie


p oznacza minimaln liczb cyfr do wypisania (w razie potrzeby przed wypisywan liczb dopisywane s zera). Przy braku p uznaje si, e ma ono warto
1 (innymi sowy, %d jest tym samym co %.1d).

e konwersja wartoci zmiennoprzecinkowej do postaci wykadnikowej


(w tzw. notacji naukowej). W takim ukadzie p okrela liczb cyfr do wypisania po przecinku dziesitnym (warto domylna to 6). Dla p rwnego 0
cz po przecinku nie jest wywietlana w ogle.

f konwersja wartoci zmiennoprzecinkowej do postaci dziesitnej, bez


wykadnika. W tym ukadzie p ma takie samo znaczenie jak przy konwersji e.

72

Rozdzia 3. Formatowanie wejcia-wyjcia


Q

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

g konwersja wartoci zmiennoprzecinkowej do postaci wykadnikowej albo


dziesitnej, zalenie od rozmiaru liczby. W tym ukadzie p oznacza maksymaln liczb cyfr znaczcych (nie cyfr po przecinku) do wypisania. Inaczej ni
przy konwersji f, konwersja g nie bdzie wypisywaa zer po prawej stronie
wartoci. Co wicej, jeli konwertowana warto nie ma cyfr po przecinku,
konwersja g nie wypisze take symbolu przecinka.

Specyfikator konwersji g jest przydatny zwaszcza do wypisywania wartoci, dla


ktrych rozmiar reprezentacji nie da si ustali na etapie pisania programu, oraz
wartoci z bardzo szerokich dziedzin. Konwersja g dla niezbyt wielkich i niezbyt
maych wartoci bdzie owocowaa zapisem dziesitnym. Dla wartoci bardzo
maych i bardzo duych zastosowany bdzie zapis wykadnikowy, wymagajcy
mniejszej liczby znakw.
%d, %e, %f i %g to bynajmniej nie wszystkie specyfikatory konwersji. Pozostae bd stopniowo wprowadzane przy okazji omawiania kolejnych zagadnie.
Pena lista specyfikatorw wraz z objanieniem ich znaczenia i dziaania znajduje
si w podrozdziale 22.3.

Wykorzystanie printf do formatowania liczb


Poniszy program ilustruje sposb wykorzystania funkcji printf do wypisywania wartoci cakowitych i zmiennoprzecinkowych w rozmaitych formatach:

tprintf.c

/* Wypisuje wartoci int i float w r nych formatach */


#include <stdio.h>
int main(void)
{
int i;
float x;
i = 40;
x = 839.21f;
printf("|%d|%5d|%-5d|%5.3d|\n", i, i, i, i);
printf("|%10.3f|%10.3e|%-10g|\n", x, x, x);
return 0;
}

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

Sprbujmy przeanalizowa znaczenie i dziaanie specyfikatorw konwersji


wykorzystanych w tym programie:

3.1. Funkcja printf

73

%d wypisanie i w zapisie dziesitnym przy jak najmniejszej liczbie znakw.

%5d wypisanie wartoci i w zapisie dziesitnym w polu o szerokoci co


najmniej 5 znakw. Poniewa waciwa warto i ma zaledwie dwa znaki, pole
jest wypeniane trzema spacjami od lewej strony.

%-5d wypisanie wartoci i w zapisie dziesitnym w polu o szerokoci co


najmniej 5 znakw. Poniewa waciwa warto i ma zaledwie dwa znaki,
pole jest wypeniane trzema spacjami po prawej stronie wartoci (warto i jest
wic wyrwnana w picioznakowym polu do lewej strony).

%5.3d wypisanie wartoci i w zapisie dziesitnym w polu o szerokoci


co najmniej 5 znakw i przy reprezentowaniu wartoci co najmniej trzema
cyframi. Poniewa i ma tylko dwie cyfry, przed waciw wartoci wstawiana
jest pojedyncza cyfra zero. Wynikowa trzyznakowa warto jest wypisywana
w polu o szerokoci 5 znakw, a wic jest poprzedzona dwoma spacjami (warto i jest wyrwnana do prawej strony pola).

%10.3f wypisanie wartoci x w zapisie dziesitnym z przecinkiem w polu


o szerokoci co najmniej 10 znakw, z trzema cyframi po przecinku. Poniewa
warto x zajmuje jedynie 7 znakw (trzy przed przecinkiem i trzy po przecinku oraz sam przecinek), jest uzupeniana trzema spacjami z lewej strony.

%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.

%-10g wypisanie wartoci x w zapisie wykadnikowym albo dziesitnym


z przecinkiem w polu o szerokoci co najmniej 10 znakw, z trzema cyframi
po przecinku. W naszym przypadku warto x zostaa zamieniona na dziesitn z przecinkiem. Obecno znaku - w specyfikatorze konwersji wymusza
wyrwnanie wartoci do lewej strony pola czterema spacjami za waciw
wartoci.

Znaki sterujce

znaki sterujce 7.3

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

Rozdzia 3. Formatowanie wejcia-wyjcia


cofnie kursor o jedn pozycj; wypisanie \n przesunie kursor do nowego wiersza;
wypisanie \t przesunie kursor do nastpnej pozycji tabulatora.
Cig formatujcy moe zawiera dowoln liczb znakw sterujcych. Spjrzmy
na kolejny przykad wywoania printf z cigiem formatujcym zawierajcym
cznie sze znakw sterujcych:
printf("Towar\tCena\tData\n\tjed.\tzakupu");

Wykonanie tej instrukcji spowoduje wypisanie na wyjciu dwch wierszy:


Towar

Cena
jed.

Data
zakupu

Innym popularnym znakiem sterujcym jest \", ktry reprezentuje w cigu


znak podwjnego cudzysowu ". Poniewa sam znak " oznacza pocztek albo
koniec literau napisowego, nie moe w takiej postaci pojawi si wewntrz literau musi zosta oznaczony jako znak sterujcy znakiem ukonika. Oto przykad:
printf("\"Ahoj!\"");

Taka instrukcja spowoduje wypisanie na wyjciu komunikatu:


"Ahoj!"

Podobna sytuacja dotyczy znaku lewego ukonika. Jeli zechcemy umieci


taki znak w wypisywanym cigu, nie moemy go wstawi wprost do literau napisowego, bo kompilator zakada, e znak ukonika jest zapowiedzi znaku sterujcego. Aby wypisa znak \, trzeba go rwnie poprzedzi znakiem \, a wic
wstawi do cigu par \\:
printf("\\");

3.2.

/* wypisuje na wyjciu pojedynczy znak \ */

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);

Zamy, e uytkownik wprowadza na wejcie programu nastpujcy wiersz:


1 -20 .3 -4.0e3

3.2. Funkcja scanf

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

Wywoania funkcji scanf s efektywnym, ale potencjalnie niebezpiecznym


sposobem wczytywania danych do programw. Wielu zawodowych programistw C unika funkcji scanf. Wol oni wczyta do programu cao danych
wejciowych w postaci znakowej i dopiero p niej zamieni j na oczekiwane
wartoci liczbowe. My natomiast bdziemy korzysta ze scanf cakiem sporo,
zwaszcza w pocztkowych rozdziaach ksiki jest to zwyczajnie najprostszy
sposb wczytywania wartoci liczbowych do programu. Trzeba tylko mie wiadomo, e wiele programw zachowa si niepoprawnie, kiedy uytkownik wprowadzi do nich nieodpowiednie dane wejciowe. Wkrtce si przekonamy, e mona
zreszt skutecznie sprawdza, czy funkcji scanf udao si wczyta z wejcia
oczekiwane dane (a jeli nie, odpowiednio zareagowa, zamiast brn dalej w program z niepoprawnymi danymi). Takie sprawdziany s jednak mao zasadne
w zakresie pocztkowych przykadw prezentowanych w ksice nadmiernie
rozbudowayby program, ktry ma przecie przede wszystkim ilustrowa biece
zagadnienie.

76

Rozdzia 3. Formatowanie wejcia-wyjcia

Dziaanie funkcji scanf


Funkcja scanf robi w istocie znacznie wicej, ni dotychczas powiedziano. Jest to
w zasadzie mechanizm dopasowywania wzorcw w cigach znakw, ktry prbuje pogrupowa znaki wejciowe i dopasowa je do specyfikatorw konwersji.
Dziaaniem funkcji scanf sterujemy za pomoc cigu formatujcego. Funkcja
scanf analizuje zawarto tego cigu, od lewej do prawej strony. Dla kadego
napotkanego w cigu specyfikatora konwersji prbuje w cigu wejciowym programu zlokalizowa warto odpowiedniego typu. W czasie tego wyszukiwania
automatycznie pomija znaki odstpw. Znaleziony podcig pasujcy do specyfikatora konwersji jest wczytywany a do miejsca, w ktrym wystpi znak niepasujcy do wymaga konwersji. Jeli udao si wczyta taki podcig, funkcja scanf
dokonuje konwersji i przechodzi do analizy reszty cigu formatujcego. Pierwsze
nieudane dopasowanie specyfikatora konwersji do danych wczytywanych z wejcia koczy dziaanie caej funkcji, bez rozpatrywania reszty cigu formatujcego
(i bez uwzgldniania reszty danych wejciowych).
W toku poszukiwania pierwszego znaku liczby funkcja scanf ignoruje
wszystkie znaki odstpw (spacje, znaki tabulacji poziomej i pionowej, znaki
wysuwu formularza i znaki nowego wiersza). Dziki temu na wejciu dane mona
podawa w jednym wierszu albo rozproszy je pomidzy rnymi wierszami. Przy
danym wywoaniu funkcji scanf:
scanf("%d%d%f%f", &i, &j, &x, &y);

uytkownik moe wprowadzi na wejcie np. trzy wiersze danych:


1
-20
.3
-4.0e3

dla funkcji scanf wejcie jest widoczne jako cigy strumie znakw:
1-20.3-4.0e3

(w celu uwidocznienia znakw odstpw zastosowalimy znak dla spacji i znak


dla nowego wiersza). Poniewa funkcja scanf pomija znaki odstpw i szuka
przede wszystkim pocztku liczby i podcigu znakw nadajcych si do konwersji,
takie dane wejciowe mog by za jednym zamachem wczytane przez funkcj
scanf. Poniszy schemat ilustruje dziaanie funkcji scanf. Znak p oznacza tu
pomijanie biecego znaku wejcia, znak w oznacza wczytywanie znakw do biecej konwersji:
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 -.

3.2. Funkcja scanf

77

Po znalezieniu takiego znaku wczytuje wszystkie kolejne znaki a do napotkania


znaku niebdcego cyfr. Natomiast w przypadku liczby zmiennoprzecinkowej
scanf szuka:
znaku + albo - (opcjonalnie), a za nim
cigu cyfr (zawierajcego ewentualnie znak przecinka dziesitnego), a za nim
cigu wykadnika (opcjonalnie); cig wykadnika skada si z litery e (albo E),
opcjonalnego znaku (+ albo -) i co najmniej jednej cyfry.
W przypadku funkcji scanf konwersje %e, %f i %g mona stosowa zamiennie
wszystkie trzy reprezentuj te same reguy dopasowania wartoci zmiennoprzecinkowej.
Kiedy scanf napotyka znak niezgodny z regu dopasowania dla biecej konwersji, znak ten jest odkadany z powrotem do strumienia wejciowego, aby by
dostpny przy obsudze nastpnych specyfikatorw konwersji albo dla kolejnych
wywoa scanf wczytujcych kolejne wartoci. We my na przykad nastpujce
(niewtpliwie patologiczne) rozmieszczenie naszych czterech liczb wejciowych:
1-20.3-4.0e3

Zastosujemy takie same wywoanie scanf jak poprzednio:


scanf("%d%d%f%f", &i, &j, &x, &y);

Funkcja scanf bdzie przetwarza taki cig wejciowy nastpujco:


Q

Bieca konwersja: %d. Pierwszy niepusty znak wejcia to 1. Poniewa liczba


cakowita moe zaczyna si od cyfry, znak jest akceptowany jako pierwszy
znak podcigu do dopasowania. Funkcja wczytuje nastpny znak: -. Taki znak
nie moe si pojawi wewntrz zapisu wartoci cakowitej, wic scanf
odkada znak z powrotem do strumienia i podstawia pod pierwsz zmienn
(i) warto 1.

Bieca konwersja: %d. Pierwszy niepusty znak wejcia to - (dozwolony);


kolejne znaki to 2, 0 i . (kropka). Liczba cakowita nie moe zawiera kropki,
wic znak jest odkadany z powrotem do strumienia wejciowego, a funkcja
podstawia pod kolejn zmienn (j) warto 20.

Bieca konwersja: %f. Pierwszy niepusty znak wejcia to . (dozwolony);


kolejne znaki to 3 i - (minus). Liczba zmiennoprzecinkowa nie zawiera znaku
- po znaku cyfry, wic znak - jest odkadany z powrotem do strumienia wejciowego, a funkcja podstawia pod kolejn zmienn (x) warto 0.3.

Bieca konwersja: %f. Pierwszy niepusty znak wejcia to - (dozwolony);


kolejne znaki to 4, ., 0, e, 3 i (nowy wiersz). Liczba zmiennoprzecinkowa
nie moe zawiera znaku nowego wiersza, wic znak jest odkadany z powrotem do strumienia wejciowego, a funkcja podstawia pod kolejn zmienn
(y) warto 4.0103.

W tym przykadzie funkcja scanf moga skutecznie dopasowa wszystkie kolejne


specyfikatory konwersji do cigu wejciowego. Poniewa znak nowego wiersza
nie zosta zuyty, bdzie pierwszym znakiem wczytywanym przy nastpnym
wywoaniu scanf.

78

Rozdzia 3. Formatowanie wejcia-wyjcia

Zwyke znaki w cigu formatujcym funkcji scanf


Zasad dopasowywania wzorcw do podcigw strumienia wejciowego mona
rozszerzy, zapisujc cig formatujcy zawierajcy poza specyfikatorami konwersji rwnie zwyke napisy. W takim przypadku pomidzy specyfikatorami konwersji funkcja scanf bdzie porwnywaa kolejne znaki wejcia ze znakami
cigu formatujcego. Porwnanie takie odbywa si rnie, zalenie od tego, czy
cig formatujcy zawiera znaki odstpw:
Q

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).

Pozostae znaki. Kiedy w cigu formatujcym znajduje si znak nienalecy


do specyfikatorw konwersji i niebdcy znakiem odstpu, funkcja scanf
porwnuje go wprost z nastpnym znakiem wejcia. Jeli znaki s zgodne,
funkcja przechodzi do przetwarzania nastpnego znaku cigu wejciowego.
Przy braku zgodnoci funkcja odkada niepasujcy znak z powrotem do cigu
wejciowego i przerywa dalsze przetwarzanie cigu formatujcego.

Dla przykadu niech funkcja scanf otrzyma cig formatujcy w postaci


"%d/%d". Jeli na wejciu programu pojawi si cig:
5/96

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

to funkcja scanf pominie pierwszy znak odstpu, szukajc liczby cakowitej,


nastpnie dopasuje do %d podcig 5, a p niej sprbuje dopasowa do wejcia
znak /. Poniewa w biecym miejscu wejcia zamiast tego znaku znajduje si
inny (tu: znak odstpu), funkcja przerwie przetwarzanie wejcia, odkadajc spacj
z powrotem do cigu wejciowego. Na wejciu pozostanie cig /96 czekajcy
na ewentualne kolejne wywoanie scanf. Aby umoliwi dopasowanie wejcia
ze spacj (spacjami) po pierwszej liczbie cakowitej, cig formatujcy powinien
mie posta "%d /%d".

3.2. Funkcja scanf

79

Skutki mylenia printf ze scanf


Wywoania funkcji printf i scanf bywaj bardzo podobne, ale w ich dziaaniu zachodz daleko idce rnice. Zignorowanie tych rnic moe by bardzo niebezpieczne dla dziaania programu.
Jedn z czstych pomyek jest umieszczenie & przed zmienn w wywoaniu
funkcji printf:
printf("%d %d\n", &i, &j);

/*** LE ***/

Na szczcie taka omyka jest stosunkowo prosta do wytropienia w toku


dziaania programu funkcja printf wywietli mieci zamiast oczekiwanych
wartoci i i j.
Poniewa funkcja scanf normalnie pomija znaki odstpw przy poszukiwaniu podcigu do dopasowania do wartoci liczbowej, rzadko kiedy pojawia si
potrzeba umieszczania w cigu formatujcym scanf czegokolwiek poza specyfikatorami konwersji. Nieuprawnione zaoenie, e cig formatujcy scanf
powinien cile odpowiada analogicznemu cigowi formatujcemu printf
to kolejny czsty bd moe doprowadzi do niepoprawnego dziaania funkcji
scanf. Zobaczmy, co si stanie, jeli w programie znajdzie si nastpujca instrukcja:
scanf("%d, %d", &i, &j);

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

Rozdzia 3. Formatowanie wejcia-wyjcia


addfrac.c

/* Dodawanie dwch uamkw zwykych */


#include <stdio.h>
int main(void)
{
int num1, denom1, num2, denom2, result_num, result_denom;
printf("Podaj pierwszy uamek: ");
scanf("%d/%d", &num1, &denom1);
printf("Podaj drugi uamek: ");
scanf("%d/%d", &num2, &denom2);
result_num = num1 * denom2 + num2 * denom1;
result_denom = denom1 * denom2;
printf("Suma uamkw wynosi %d/%d\n",
result_num, result_denom);
return 0;
}

Przykadowa sesja z takim programem wygldaa tak:


Podaj pierwszy uamek: 5/6
Podaj drugi uamek: 3/4
Suma uamkw wynosi 38/24

Zauwamy, e program nie przewiduje znormalizowania wynikowego uamka.

Pytania i odpowiedzi
*P:

Widywaem specyfikatory w postaci %i, przeznaczone do wczytywania i wypisywania liczb cakowitych. Czym rni si od %d (s. 71)?

O:

W cigu formatujcym funkcji printf nie ma pomidzy nimi adnej rnicy.


Za to w funkcji scanf %d dopasuje wycznie liczb cakowit zapisan w postaci
dziesitnej, natomiast %i moe dopasowa take liczby cakowite zapisane
w innych systemach liczbowych (przy innych podstawach), na przykad liczby
semkowe czy szesnastkowe. Ot jeli cig wejciowy zawiera przedrostek
0 (jak w 056), przy konwersji %i taki cig zostanie potraktowany jako semkowy.
Przedrostek 0x albo 0X (jak w 0x56) przy konwersji %i spowoduje konwersj
liczby jako wartoci szesnastkowej. Zastosowanie %i zamiast %d do wczytywania
liczb moe da zaskakujce wyniki, jeli uytkownik omykowo wprowadzi 0 przed
waciw liczb; z tego powodu zaleca si trzymanie si konwersji %d.

liczby semkowe 7.1


liczby szesnastkowe 7.1

P:

Skoro printf traktuje znak % jako pocztek specyfikatora konwersji, jak


mona wypisa na wyjciu programu znak procenta?

O:

Funkcja printf wypisze na wyjciu znak % tam, gdzie w cigu formatujcym


pojawi si para przylegajcych znakw procenta (%%). Na przykad instrukcja:

Pytania i odpowiedzi

81

printf("Zysk netto: %d%%\n", profit);

moe spowodowa wypisanie:


Zysk netto: 10%

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:

Co zrobi funkcja scanf, jeli bdzie miaa wczyta liczb, a uytkownik


wprowadzi cig nieliczbowy?

O:

Wyjanimy to na przykadzie:
printf("Podaj liczb: ");
scanf("%d", &i);

Zamy, e uytkownik wprowadzi poprawn warto liczbow, za ktr umieci


znaki inne ni cyfry:
Podaj liczb: 23bla

W takim przypadku funkcja scanf wczyta znaki 2 oraz 3 i w zmiennej i umieci


warto 23. Reszta znakw (bla) bdzie w strumieniu wejciowym czekaa na
kolejne wywoanie scanf (albo innej funkcji obsugujcej wejcie). Jeli natomiast
uytkownik wprowadzi cig niepoprawny od pierwszego znaku:
Podaj liczb: bla

wykrywanie bdw
w scanf 22.3

zmienna i nie otrzyma wartoci wczytywanej z wejcia programu, a w strumieniu


wejciowym cig bla poczeka na kolejne wywoania funkcji wejcia.
Co mona zrobi w takich smutnych przypadkach? P niej dowiesz si, jak
sprawdzi skuteczno wywoania funkcji scanf. Jeli wywoanie bdzie nieskuteczne, moemy zakoczy program albo sprbowa naprawi sytuacj, na przykad odrzucajc niepoprawne wejcie i ponownie proszc uytkownika o wprowadzenie danych (sposoby odrzucania danych wejciowych bd omawiane
w sekcji pyta i odpowiedzi rozdziau 22.).

P:

Nie rozumiem, w jaki sposb funkcja scanf odkada znak z powrotem


do cigu wejciowego i potem ponownie wczytuje go z wejcia (s. 77)?

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:

Jak zadziaa funkcja scanf, kiedy uytkownik umieci pomidzy liczbami


znaki przestankowe (np. przecinki)?

O:

Zobaczmy to na prostym przykadzie. Zamy, e zamierzamy wczyta funkcj


scanf par liczb:

82

Rozdzia 3. Formatowanie wejcia-wyjcia


printf("Podaj dwie liczby: ");
scanf("%d%d", &i, &j);

Jeli uytkownik wprowadzi:


4,28

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.

Co pojawi si na wyjciu po wykonaniu poniszych wywoa printf?


(a) printf("%6d,%d4", 86, 1040);
(b) printf("%12.5e", 86, 30.253);
(c) printf("%.4f", 83.162);
(d) printf("%-6.2g", .0000009979);

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.

We my nastpujce wywoanie funkcji scanf:


wiczenia z gwiazdk s podchwytliwe poprawna odpowied zazwyczaj jest rna


od odpowiedzi narzucajcej si na pierwszy rzut oka. Przeczytaj pytanie uwanie, dokadnie
przeled kod, w razie potrzeby powtrz lektur odpowiedniego podrozdziau. Powodzenia! przyp. autora.

Zadania programistyczne

83

scanf("%d%f%d", &i, &x, &j);


Jakie bd wartoci zmiennych i, x i j po wykonaniu wywoania (zakadamy, e zmienne
i i j s zmiennymi typu int, a x jest zmienn typu float), jeli uytkownik wprowadzi:
10.3 5 6
* 5.

We my nastpujce wywoanie funkcji scanf:


scanf("%f%d%f", &x, &i, &y);
Jakie bd wartoci zmiennych x, i i y po wykonaniu wywoania (zakadamy, e zmienne
x i y s zmiennymi typu float, a i jest zmienn typu int), jeli uytkownik wprowadzi:
12.3 45.6 789

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.

Napisz program, ktry przyjmuje na wejcie dat w postaci dd/mm/rrrr, a nastpnie


wypisuje j w formacie rrrrmmdd:
Podaj dat (dd/mm/rrrr): 17/2/2011
Podae dat 20110217

2.

Napisz program formatujcy informacje o produkcie wprowadzone przez uytkownika. Sesja


z programem powinna wyglda tak:
Podaj numer towaru: 583
Podaj cen jednostkow: 13.5
Podaj dat zakupu (dd/mm/rrrr): 24/10/2010
Towar
Cena
Data
jed.
zakupu
583
$ 13.50
24/10/2010
Numer towaru i data powinny by wyrwnane do lewej. Cena jednostkowa powinna by
wyrwnana do prawej. Program powinien dopuszcza kwoty do 9999,99. Podpowied : Do
wyrwnania kolumn zastosuj tabulatory.

3.

Ksiki s oznaczane midzynarodowym numerem ISBN (International Standard Book


Number). Numery ISBN nadawane po 1 stycznia 2007 roku skadaj si z 13 cyfr, podzielonych na pi grup, np. 978-0-393-97950-3 (starsze numery ISBN miay 10 cyfr). Pierwsza
grupa (przedrostek GSI) to obecnie albo 978 albo 979. Identyfikator grupy okrela jzyk
kraju wydania (np. w krajach anglojzycznych stosuje si kody 0 i 1). Kod wydawcy
identyfikuje wydawc (393 to kod wydawnictwa W.W. Norton). Numer publikacji to numer
nadawany przez wydawc konkretnej ksice (tutaj 97950). Numer ISBN koczy si sum
kontroln umoliwiajc weryfikacj poprawnoci numeru ISBN. Napisz program, ktry
podzieli na grupy numer ISBN wprowadzony przez uytkownika:
Podaj numer ISBN: 978-0-393-97950-3
Przedrostek GSI: 978
Identyfikator grupy: 0

84

Rozdzia 3. Formatowanie wejcia-wyjcia


Kod wydawcy: 393
Numer publikacji: 97950
Suma kontrolna: 3
Uwaga: Liczba cyfr w kadej z grup moe by rna. Nie mona zaoy, e grupy maj
akurat takie rozmiary jak w tym przykadzie. Przetestuj program na innych, prawdziwych
numerach ISBN (znajdziesz je na okadkach ksiek i na stronach z informacjami o wydaniu).
4.

Napisz program, ktry zapyta uytkownika o numer telefoniczny w formacie (xx)


xxx-xxxx, a potem wywietli ten numer w zapisie 0-xx xxx-xx-xx:
Podaj numer telefonu [(xx) xxx-xxxx]: (61) 817-6900
Podany numer: 0-61 817-69-00

5.

Napisz program, ktry bdzie monitowa, aby uytkownik wprowadzi liczby od 1 do 16


(w dowolnej kolejnoci), potem wywietli te liczby czwrkami (w postaci macierzy 44),
a nastpnie wypisze sumy w wierszach, kolumnach i po przektnych:
Podaj liczby od 1 do 16 (w dowolnej kolejnoci):
16 3 2 13 5 10 11 8 9 6 7 12 4 15 14 1
16 3 2 13
5 10 11 8
9 6 7 12
4 15 14 1
Sumy w wierszach: 34 34 34 34
Sumy w kolumnach: 34 34 34 34
Sumy po przektnych: 34 34
Jeli sumy w wierszach, kolumnach i po przektnych s identyczne (jak w przykadzie),
to takie liczby stanowi tak zwany magiczny kwadrat. Ten magiczny kwadrat pojawi
si w 1514 roku na rycinie artysty i matematyka Albrechta Drera (zauwa, e rodkowe
liczby w ostatnim wierszu skadaj si na dat ryciny).

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

Operatory arytmetyczne, w tym dodawanie, odejmowanie, mnoenie i dzielenie.

Operatory relacji do obliczania wartoci wyrae logicznych, takich jak i jest


wiksze od 0.

Operatory logiczne do budowania warunkw logicznych, jak i jest wiksze


od 0 i jest mniejsze ni 10.

Na tym jednak nie koniec. W jzyku C mamy do dyspozycji dziesitki innych


operatorw. Jest ich tak wiele, e bdziemy zmuszeni do ich stopniowego wprowadzania na przestrzeni a dwudziestu rozdziaw tej ksiki. Opanowanie takiej
liczby operatorw wydaje si zadaniem niepomiernym, ale w istocie wikszo
z nich ma zasadnicze znaczenie dla efektywnoci programowania w jzyku C.
W tym rozdziale zajmiemy si jednymi z najwaniejszych operatorw jzyka C,
mianowicie we miemy na warsztat operatory arytmetyczne (podrozdzia 4.1), operatory przypisania (podrozdzia 4.2) oraz operatory inkrementacji i dekrementacji

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

+ zachowanie znaku liczby

+ dodawanie

* mnoenie

- zmiana znaku liczby

- 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;

/* + jako operator jednoargumentowy */


/* + jako operator jednoargumentowy (negacja) */

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.

4.1. Operatory arytmetyczne

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.

Jeli prawym operandem operatora / albo % bdzie 0, dziaanie programu


bdzie niezdefiniowane.

Najwiksze puapki czaj si przy stosowaniu operatorw / i % z operandami


ujemnymi. Standard C89 stwierdza, e kiedy ktrykolwiek z operandw jest
ujemny, wynik dzielenia moe zosta zaokrglony w gr albo w d (np. wartoci wyraenia -9 / 7 moe by albo 1, albo 2). Rwnie kiedy operand i albo j wyraenia i % j jest ujemny, wedug standardu C89 wynik
dziaania operatora jest zaleny od implementacji (np. wyraenie -9 % 7
moe da warto 2 albo 5). Z kolei w C99 wynik dzielenia cakowitego jest
zawsze zaokrglany w kierunku zera (-9 / 7 da wynik 1), a warto wyraenia i % j bdzie miaa znak taki jak operand i (czyli -9 % 7 to 2).

Zachowanie zalene od implementacji


Pojcie zachowania albo dziaania zale nego od implementacji bdzie si pojawia w naszym omwieniu na tyle czsto, e warto przedstawi je zawczasu.
W standardzie jzyka C celowo pozostawiono specyfikacj dziaania niektrych
elementw jzyka jako nie do koca sprecyzowan, z zaoeniem, e implementacja czyli konkretne wcielenie kompilatora i programu konsolidujcego dla
konkretnej platformy maszynowej i systemowej wypeni luk po swojemu.
W efekcie zachowanie programu w toku wykonania moe by rne na rnych
platformach. Przykadem zachowania zalenego od implementacji jest wanie
dziaanie operatorw / i % wedug standardu C89.
Niedoprecyzowanie niektrych elementw jzyka wydaje si dziwactwem,
a nawet dziaaniem szkodliwym, ale w istocie dobrze odzwierciedla filozofi przywiecajc twrcom jzyka C. Jednym z zaoe projektowych bya przecie moliwie dua wydajno programw pisanych w tym jzyku, co czsto oznacza
konieczno dostosowania implementacji poszczeglnych konstrukcji do zachowania danej maszyny. Tak wic niektre procesory, wykonujc operacj dzielenia
cakowitego 9 przez 7, daj wynik 1, a inne 2. Standard C89 zwyczajnie odzwierciedla rnorodno maszynowej implementacji podstawowych operacji arytmetycznych.
Przy pisaniu programw najlepiej unika polegania na zachowaniach zalenych
od implementacji. Jeli nie da si takiej zalenoci unikn , warto przynajmniej
rcznie sprawdzi wyniki wtpliwych operacji standard C wymaga na szczcie,
aby sposb realizacji zachowa zalenych od implementacji by udokumentowany.

Pierwszestwo i czno operatorw


Kiedy wyraenie zawiera wicej ni jeden operator, interpretacja wyraenia moe
by wtpliwa. Czy na przykad i + j * k oznacza dodanie i do j i pomnoenie sumy przez k, czy moe pomnoenie j przez k i dodanie do iloczynu i?
Wtpliwo mona usun, stosujc nawiasy, a wic zapisujc jawnie albo (i + j)
* k, albo i + (j * k). Jzyk C co do zasady pozwala na grupowanie podwyrae w wyraeniach wanie za pomoc nawiasw.

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

interpretuje si jako


interpretuje si jako
interpretuje si jako

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

interpretuje si jako


interpretuje si jako

(i - j) - k
(i * j) / k

Operator jest prawostronnie czny, kiedy grupuje operandy od prawej strony do


lewej. Prawostronnie cznymi operatorami s jednoargumentowe operatory arytmetyczne (+ i -), wic:
- + i

tabela operatorw Dodatek A

PROGRAM

interpretuje si jako

-(+i)

Reguy pierwszestwa i cznoci operatorw s bardzo wane take w innych


jzykach programowania, ale w C ich znaczenie jest szczeglne. Z drugiej strony
przy liczbie operatorw dostpnych w jzyku C (jest ich niemal pidziesit!) mao
ktry programista jest w stanie zapamita czno i pierwszestwo wszystkich
operatorw. Piszc program w jzyku C, warto wic mie pod rk tabelk operatorw i zaglda do niej w razie wtpliwoci. Mona te je eliminowa poprzez
jawne grupowanie podwyrae w nawiasach.

Obliczanie cyfry kontrolnej kodu kreskowego


W latach siedemdziesitych producenci dbr szybko zbywalnych w Stanach Zjednoczonych i Kanadzie zaczli znakowa swoje towary, umieszczajc na nich kody
kreskowe. Taki kod kreskowy albo inaczej kod UPC (universal product code)

4.1. Operatory arytmetyczne

89

identyfikuje zarwno producenta, jak i konkretny produkt. Kady kod kreskowy


reprezentuje dwunastocyfrow liczb (wypisan zreszt najczciej pod kodem
kreskowym). Oto przykadowy kod kreskowy pizzy Stouffer:

Cyfry:
0 13800 15173 5

s jawnie wypisane pod waciwym kodem kreskowym. Pierwsza z nich okrela


typ towaru (dla wikszoci towarw jest to 0 albo 7, dla towarw waonych 2,
dla lekarstw i produktw farmaceutycznych 3, a dla kuponw 5). Pierwsza grupa piciu cyfr identyfikuje producenta towaru (13800 to kod Nestle USA Frozen
Food Division, czyli dziau mroonek amerykaskiego koncernu Nestle). Druga
grupa cyfr (znw pi) to identyfikator produktu (okrelajcy midzy innymi
rozmiar opakowania). Ostatnia cyfra to cyfra kontrolna, ktrej jedynym zadaniem
jest pomoc w weryfikowaniu poprawnoci kodw kreskowych (poprawnoci
poprzednich cyfr). Kiedy taki kod zostanie niepoprawnie zeskanowany, cyfra kontrolna nie bdzie si zgadza z jedenastoma pierwszymi cyframi, wic skaner
kasowy odrzuci kod.
Oto jedna z metod obliczania cyfry kontrolnej kodu kreskowego UPC:
Dodaj cyfr pierwsz, trzeci, pit, sidm, dziewit i jedenast.
Dodaj cyfr drug, czwart, szst, sm i dziesit.
Pomn pierwsz sum przez 3 i dodaj j do drugiej sumy.
Od wyniku odejmij 1.
Oblicz reszt z dzielenia pomniejszonego wyniku przez 10.
Odejmij reszt z dzielenia od 9.
Na przykadzie pizzy Stouffer otrzymamy pierwsz sum o wartoci (0+3+0+1+
1+3) = 8; druga suma powinna wynosi (1+8+0+5+7) = 21. Suma iloczynu pierwszej sumy przez 3 i drugiej sumy daje 45. 45 odj 1 daje 44. Reszta z dzielenia
44 przez 10 to 4. Rnica 9 4 wynosi 5. Oto kilka innych przykadw liczbowych
kodw kreskowych UPC, ktre moemy sprawdzi lepiej liczy, zamiast szuka
tych produktw w lodwkach:
Jif Creamy Peanut Butter (18 uncji):
Ocean Spray Jellied Cranberry Sauce (8 uncji):

0 51500 24128
0 31200 01005

?
?

Odpowiedzi znajduj si u dou strony1.


1

Brakujce cyfry kontrolne to 8 (Jif) i 6 (Ocean Spray) przyp. autora.

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

pierwsz cyfr kodu: 0


pierwsz grup piciu cyfr kodu: 13800
nastpn grup piciu cyfr kodu: 15173
kontrolna: 5

Zamiast wczytywa poszczeglne grupy cyfr jako liczby piciocyfrowe,


bdziemy wczytywa je jako pitk liczb jednocyfrowych. Wczytanie cyfr jako
osobnych liczb bdzie dla nas potem znacznie wygodniejsze. Nie trzeba bdzie
si te martwi na przykad o to, e jedna z dwch piciocyfrowych liczb nie
zmieci si w zmiennej int (w niektrych starszych kompilatorach graniczna warto typu int to 32 767). Aby wczyta pojedyncz cyfr, wykorzystamy funkcj
scanf ze specyfikatorami konwersji %1d, odpowiadajcymi liczbom jednocyfrowym (w zapisie dziesitnym).
upc.c

/* Obliczanie cyfry kontrolnej kodu kreskowego UPC */


#include <stdio.h>
int main(void)
{
int d, i1, i2, i3, i4, i5, j1, j2, j3, j4, j5,
first_sum, second_sum, total;
printf("Podaj pierwsz cyfr kodu: ");
scanf("%1d", &d);
printf("Podaj pierwsz grup piciu cyfr kodu: ");
scanf("%1d%1d%1d%1d%1d", &i1, &i2, &i3, &i4, &i5);
printf("Podaj drug grup piciu cyfr kodu: ");
scanf("%1d%1d%1d%1d%1d", &j1, &j2, &j3, &j4, &j5);
first_sum = d + i2 + i4 + j1 + j3 + j5;
second_sum = i1 + i3 + i5 + j2 + j4;
total = 3 * first_sum + second_sum;
printf("Cyfra kontrolna: %d\n", 9 - ((total - 1) % 10));
return 0;
}

Zauwamy, e wyraenie 9 - ((total - 1) % 10) mona by zapisa


jako 9 - (total - 1) % 10, ale dodatkowa para nawiasw nie zaciemnia
wyraenia wrcz odwrotnie, czyni je czytelniejszym.

4.2. Operatory przypisania

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 */

Do tematu konwersji wartoci przypisywanej wrcimy nieco p niej.


W wielu innych jzykach programowania przypisanie jest instrukcj. W jzyku
C przypisanie jest jednak operatorem, tak jak +. Innymi sowy, akt przypisania
jest wyraeniem posiadajcym warto, tak samo jak akt dodawania jest wyraeniem posiadajcym warto (tu rwn sumie operandw). Wartoci przypisania
v = e jest warto v, obliczana ju po wykonaniu przypisania. Wartoci przypisania i = 72.99f jest wic nie 72,99, ale 72.

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;

Operator = jest czny prawostronnie, wic takie przypisanie jest interpretowane


jako:
i = (j = (k = 0));

W efekcie w pierwszej kolejnoci nastpi przypisanie 0 do k, nastpnie wynik


przypisania zostanie przypisany do j, a wynik tego przypisania do i.
Naley si wystrzega nieoczekiwanych wynikw w acuchowych przypisaniach,
spowodowanych konwersj typw operandw:
int i;
float f;
f = i = 33.3f;

Zmienna i otrzyma tutaj warto 33 i przez to do zmiennej f przypiszemy 33.0,


a nie 33.3.
Zasadniczo przypisanie w postaci v = e jest dozwolone wszdzie tam,
gdzie byaby dozwolona warto typu v. W poniszym przykadzie wyraenie
j = i kopiuje warto i do zmiennej j. Nowa warto j jest potem dodawana do
1 i tak obliczona warto jest przypisywana do k:
i = 1;
k = 1 + (j = i);
printf("%d %d %d\n", i, j, k);

/* wypisuje "1 1 2" */

Takie stosowanie operatora przypisania trudno jednak uzna za dobr praktyk


programistyczn. Przede wszystkim dlatego, e zagniedone przypisania zmniejszaj czytelno programu. Mog by rwnie przyczyn subtelnych bdw,
o ktrych bdzie mowa w podrozdziale 4.4.

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.

4.2. Operatory przypisania

93

Z wymagania, aby lewym operandem operatora przypisania bya l-warto,


wynika, e po lewej stronie operatora przypisania nie wolno stosowa adnych
wyrae:
12 = i;
i + j = 0;
-i = j;

/*** LE ***/


/*** LE ***/
/*** LE ***/

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;

Takie i tym podobne instrukcje mona w jzyku C skraca do postaci przypisa


zoonych (ang. compound assignments). Moemy wic do analogicznej operacji
wykorzysta operator zoony +=:
/* to samo, co i = i + 2 */

i += 2;

Operator += dodaje do wartoci lewego operandu warto prawego operandu.


W jzyku C mamy do dyspozycji dziewi zoonych operatorw przypisania,
w tym:
-=
inne operatory
przypisania 20.1

*=

/=

%=

(o pozostaych zoonych operatorach przypisania powiemy sobie w jednym


z dalszych rozdziaw). Wszystkie operatory przypisa zoonych dziaaj na
podobnych zasadach:

v
v
v
v
v

+=
-=
*=
/=
%=

e
e
e
e
e

dodaje e do v i zapisuje sum w v


odejmuje e do v i zapisuje rnic w v
mnoy v przez e i zapisuje iloczyn w v
dzieli v przez e i zapisuje iloraz w v
oblicza reszt z dzielenia v przez e i zapisuje wynik v

Wcale nie oznacza to, e zapis v += e jest zupenie tosamy z v = v + e.


Przede wszystkim ze wzgldu na pierwszestwo operatorw i *= j + k to nie
to samo co i = i * j + k. S te rzadkie przypadki, kiedy v += e jest
rne od v = v + e, gdy v posiada efekty uboczne. Podobne zastrzeenia dotycz rwnie pozostaych operatorw przypisania.
Przy stosowaniu przypisa zoonych trzeba uwaa, eby nie zamieni miejscami
znakw w symbolu operatora. Przestawienie znakw moe doprowadzi do utworzenia wyraenia, ktre bdzie poprawne z punktu widzenia skadni programu

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.

Operatory inkrementacji i dekrementacji


Do najczciej uywanych operatorw jzyka C nale operatory inkrementacji
(dodania jedynki) i dekrementacji (odjcia jedynki). Tego rodzaju operacje mona,
rzecz jasna, zapisa za pomoc zwyczajnych operatorw arytmetycznych:
i = i + 1;
j = j - 1;

Mona te wykorzysta operatory przypisa zoonych:


i += 1;
j -= 1;

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);

/* wypisuje "i to 2" */


/* wypisuje "i to 2" */

Z kolei obliczenie wyraenia i++ (po-inkrementacja) daje warto i, ale


w ramach efektu ubocznego i jest zwikszane o 1:

4.3. Operatory inkrementacji i dekrementacji


i = 1;
printf("i to %d\n", i++);
printf("i to %d\n", i);

95

/* wypisuje "i to 1" */


/* wypisuje "i to 2" */

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);

/* wypisuje "i to 0" */


/* wypisuje "i to 0" */

i = 1;
printf("i to %d\n", i--);
printf("i to %d\n", i);

/* wypisuje "i to 1" */


/* wypisuje "i to 0" */

Kiedy operator ++ albo -- wystpi wielokrotnie w jednym wyraeniu, warto


wyraenia i faktyczne wartoci operandw mog by trudne do ustalenia. We my
nastpujcy przykad:
i = 1;
j = 2;
k = ++i + j++;

Jakie s wartoci i, j i k po wykonaniu tych instrukcji? Poniewa zmienna i


bya inkrementowana przed uyciem jej wartoci w wyraeniu, a zmienna j bya
inkrementowana po uyciu jej wartoci w wyraeniu, powysze instrukcje s
rwnowane nastpujcym:
i = i + 1;
k = i + j;
j = j + 1;

wic wynikowe wartoci zmiennych i, j i k to odpowiednio 2, 3 i 4. Dla porwnania wykonanie instrukcji:


i = 1;
j = 2;
k = i++ + j++;

zostawi zmienne i, j i k z wartociami (odpowiednio) 2, 3 i 3.


Dla porzdku wypada dopowiedzie, e przyrostkowe wersje operatorw ++
i -- maj wyszy priorytet ni jednoargumentowe operatory +, - i s czne lewostronnie. Wersje przedrostkowe maj priorytet taki sam jak jednoargumentowe
operatory +, - i s czne prawostronnie.

96

Rozdzia 4. Wyraenia

4.4.

Obliczanie wartoci wyra e

W tabeli 4.2 zestawiono operatory omwione w poprzednich podrozdziaach


(podobn tabel, ale z kompletem operatorw, zawiera dodatek A). Pierwsza
kolumna okrela pierwszestwo operatorw wzgldem pozostaych operatorw
w tabeli (1 to najwikszy priorytet, 5 to priorytet najmniejszy). Ostatnia kolumna
opisuje czno operatorw.

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

Tabela 4.2 (albo jej uzupeniony odpowiednik z dodatku A) bdzie bardzo


przydatna. Zamy, e w kodzie rdowym (na przykad cudzym) napotkamy tak
instrukcj:
a = b += c++ - d + --e / -f

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))

4.4. Obliczanie wartoci wyrae

97

Wyraenie zawiera jeszcze dwa operatory o priorytecie 4. (wedug tabeli 4.2):


dodawanie i odejmowanie. Kiedy w wyraeniu ssiaduj ze sob dwa operatory
o rwnym priorytecie, naley bardzo uwanie zastosowa regu cznoci.
W naszym przykadzie - i + otaczaj d; czno dwuargumentowych operatorw
+ i - jest lewostronna, wic stawiamy nawiasy najpierw wok odejmowania,
a potem wok dodawania:
a = b += (((c++) - d) + ((--e) / (-f)))

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)))))

W ten sposb dokonalimy penego pogrupowania wyraenia.

Kolejno obliczania podwyrae

operatory logiczne and i or 5.1


operator warunkowy 5.2
operator przecinka 5.3

Reguy pierwszestwa i cznoci operatorw pozwalaj na skuteczne rozbicie


dowolnie skomplikowanego wyraenia na podwyraenia moemy dziki nim
pogrupowa poszczeglne podwyraenia w nawiasy. Paradoksalnie obie reguy
nie zawsze pozwalaj na okrelenie wartoci wyraenia, ktra moe zalee od
kolejnoci obliczania wartoci poszczeglnych podwyrae.
Jzyk C nie definiuje kolejnoci obliczania wartoci podwyrae (z wyjtkiem
podwyrae angaujcych operatory logiczne and i or, operatory warunkowe
i operatory przecinka). Przez to w przypadku wyraenia (a + b) * (c - d)
nie moemy mie pewnoci, czy jako pierwsze zostanie obliczone podwyraenie
(a + b), czy moe (c -d).
Wikszo wyrae ma t sam warto niezalenie od tego, w jakiej kolejnoci zostay obliczone ich podwyraenia. Bywa jednak, e podwyraenie modyfikuje jeden ze swoich operandw. We my poniszy przykad:
a = 5;
c = (b = a + 2) - (a = 1);

Efekt wykonania drugiej instrukcji jest niezdefiniowany. Standard jzyka C nie


mwi nic jednoznacznego na temat wartoci tak zbudowanego wyraenia. W przypadku wikszoci kompilatorw zmienna c bdzie miaa warto 6 albo 2. Jeli
jako pierwsze zostanie obliczone podwyraenie (b = a + 2), b otrzyma warto 7, a c zostanie obliczone jako 6. Ale jeli pierwszym obliczonym podwyraeniem bdzie (a = 1), wtedy b otrzyma warto 3, a w c znajdzie si warto 2.
Naley unika konstruowania wyrae, w ktrych odwoujemy si do wartoci
zmiennej, a rwnoczenie (w innym podwyraeniu) bazujemy na tej wartoci.
Wyraenie (b = a + 2) - (a = 1) zawiera odwoanie do wartoci a
w pierwszym podwyraeniu oraz modyfikacj wartoci do a w drugim podwyraeniu
(przez przypisanie jedynki). Niektre kompilatory oznaczaj takie wyraenia komunikatami z ostrzeeniami w rodzaju operacja na a moe by niezdefiniowana.

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;

W ten sposb zagwarantujemy, e zmienna c otrzyma warto 6.


Poza operatorami przypisania mamy jeszcze inne operatory modyfikujce
warto operandu chodzi o operatory inkrementacji i dekrementacji. Przy korzystaniu z tych operatorw rwnie trzeba uwaa, aby nie doprowadzi do powstania
wyraenia, ktrego poprawna warto jest zalena od konkretnej kolejnoci obliczania
podwyrae, jak tutaj, gdzie do j moe zosta przypisana jedna z dwch wartoci:
i = 2;
j = i * i++;

Pozornie jest oczywiste, e j otrzyma warto 4. Niestety, w efekcie wykonania


takich instrukcji zmienna j moe mie rwnie warto 6. Oto moliwy scenariusz: (1) najpierw pobierany jest drugi operand (pierwotna warto i); nastpnie i
podlega inkrementacji (2) i pobierany jest drugi operand (ju nowa warto i);
(3) obliczany jest iloczyn obu operandw: liczba 6. Pobranie wartoci zmiennej
oznacza odczytanie wartoci z pamici skojarzonej ze zmienn. P niejsza zmiana
wartoci zmiennej w pamici nie wpywa ju na warto pobran, bo pobrana warto jest kopiowana w specjalne miejsce (tzw. rejestr) wewntrz procesora.

Niezdefiniowane zachowanie programu


Wedug standardu jzyka C instrukcje takie jak c = (b = a + 2) - (a = 1)
oraz j = i * i++ powoduj tzw. niezdefiniowane zachowanie (ang. undefined
behavior) programu (patrz podrozdzia 4.1). Kiedy program zawiera zachowania
niezdefiniowane, nie mona ju nic powiedzie o jego wykonaniu. W zalenoci od
uytego kompilatora moe si on zachowywa rnie, ale to nie wyczerpuje pojcia
niezdefiniowanego zachowania. Przede wszystkim program moe si w ogle nie
skompilowa , skompilowany moe si nie uruchomi , a uruchomiony moe si
wyoy , dziaa niepoprawnie bd
dawa nieznaczce wyniki (np. za kadym
uruchomieniem inne). Innymi sowy, przestaje by programem zachowa niezdefiniowanych trzeba wic unika jak ognia.

4.5.

Instrukcje wyra eniowe


Jzyk C posiada niezwyk waciwo tutaj ka de wyraenie moe by uyte
jako instrukcja programu. Kade wyraenie (niezalenie od jego typu i od tego, co
oblicza) moe zosta zamienione na instrukcj wystarczy zakoczy je rednikiem. Na przykad na instrukcj moemy zamieni wyraenie ++i:
++i;

Pytania i odpowiedzi

99

Kiedy dochodzi do wykonania tej instrukcji, nastpuje zwikszenie wartoci


i o jeden, a p niej pobierana jest nowa warto i (tak jakby miaa za chwil zosta
wykorzystana przy obliczaniu wyraenia nadrzdnego). Ale skoro ++i nie jest
czci wikszego wyraenia, pobrana warto jest odrzucana, a program przechodzi do wykonania nastpnej instrukcji (ale inkrementacja i jest oczywicie trwaa).
Skoro warto instrukcji wyraeniowej jest odrzucana, nie ma wikszego sensu
wykorzystywanie wyrae jako instrukcji, chyba e s to wyraenia z efektami
ubocznymi; one zostan przecie wykonane mimo odrzucenia obliczonej wartoci
wyraenia. We my trzy przykady. W pierwszym do i przypisywana jest warto 1 nowa warto i jest pobierana, ale zaraz odrzucana:
i = 1;

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++;

W trzecim przykadzie zostanie obliczona warto wyraenia i * j - 1, ale


obliczona warto zaraz bdzie odrzucona:
i * j - 1;

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.

funkcja pow 23.3

100

Rozdzia 4. Wyraenia

funkcja fmod 23.3

P:

Chciaem zastosowa operator % przy operandzie typu float, ale program


nie daje si skompilowa . Co mog zrobi (s. 86)?

O:

Operator % wymaga operandw cakowitych. Sprbuj uy funkcji fmod.

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:

Skoro w C s l-wartoci, czy s te r-wartoci (s. 92)?

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:

Bya mowa o tym, e v += e nie jest odpowiednikiem v = v + e, jeli v


ma efekty uboczne. Jak to rozumie (s. 93)?

O:

Obliczenie wartoci v += e powoduje, e warto v jest obliczana tylko raz.


v w wyraeniu v = v + e jest obliczane dwa razy. Wic jeli w tym drugim
przypadku v posiada efekt uboczny, zostanie on wykonany dwukrotnie. W tym
przykadzie i jest inkrementowane raz:
a[i++] += 2;

ale jeli zamiast += uyjemy przypisania =, otrzymamy:


a[i++] = a[i++] + 2;

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:

Jzyk C odziedziczy operatory ++ i -- w spadku po jzyku B Kena Thompsona.


Thompson wprowadzi te operatory, poniewa jego kompilator B najwyra niej
potrafi efektywniej przetumaczy zapis ++i ni i = i + 1. Operatory te

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:

Czy operatory ++ i -- dziaaj ze zmiennymi typu float?

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:

Co oznacza odrzucenie wartoci instrukcji wyraeniowej (s. 99)?

O:

Z definicji wyraenie reprezentuje warto. Jeli np. i ma warto 5, to obliczenie


wyraenia i + 1 daje warto 6. Zamiemy to wyraenie na instrukcj wyraeniow, dodajc rednik na kocu:
i + 1;

W ramach wykonywania tej instrukcji dochodzi do obliczenia wartoci wyraenia


i + 1. Poniewa jednak ta warto nie jest nigdzie wykorzystywana (nie zostaa
przypisana do zmiennej ani nie jest wykorzystywana jako podwyraenie)
przepada.
P:

A co z instrukcjami typu i = 1;? Nie widz, eby co tu byo tracone.

O:

Pamitajmy, e przypisanie jest w jzyku C operatorem i jak kady operator generuje warto. Przypisanie:
i = 1;

powoduje zapisanie 1 w zmiennej i, ale jako wyraenie ma warto przypisania (1)


i ta wanie warto jest odrzucana. Odrzucanie wartoci wyraenia nie jest jak
wielk strat, poniewa w naszej instrukcji chodzio nam przede wszystkim o zmodyfikowanie zmiennej i.

102

Rozdzia 4. Wyraenia

wiczenia
Podrozdzia 4.1

1.

Napisz, co pojawi si na wyjciu programu wykonujcego ponisze instrukcje. Za, e


i, j i k s zmiennymi typu int:
(a) i = 5; j = 3;
printf("%d %d", i / j, i % j);
(b) i = 2; j = 3;
printf("%d", (i + 10) % j);
(c) i = 7; j = 8; k = 9;
printf("%d", (i + 10) % k / j);
(d) i = 1; j = 2; k = 3;
printf("%d", (i + 5) % (j + 2) / k);

* 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.

Powtrz wiczenie 3. dla wytycznych standardu C99.

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.

Powtrz wiczenie 5. dla wytycznych standardu C99.

7.

Algorytm obliczania cyfry kontrolnej UPC koczy si nastpujcymi krokami:


Odejmij 1 od sumy.
Oblicz reszt z dzielenia zmniejszonej sumy przez 10.
Odejmij reszt z dzielenia od 9.
A korci, eby ten algorytm uproci nastpujco:
Oblicz reszt z dzielenia sumy przez 10.
Odejmij reszt z dzielenia od 10.
Dlaczego taka poprawka nie zadziaa?

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.

Napisz, co pojawi si na wyjciu programu wykonujcego ponisze instrukcje. Za, e i,


j i k s zmiennymi typu int:

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.

Napisz, co pojawi si na wyjciu programu wykonujcego ponisze instrukcje. Za, e


ii j s zmiennymi typu int:
(a) i = 6;
j = i += i;
printf("%d %d", i, j);
(b) i = 5;
j = (i -= 2) + 1;
printf("%d %d", i, j);
(c) i = 7;
j = 6 + (i = 2.5);
printf("%d %d", i, j);
(d) i = 2; j = 8;
j = (i = 6) + (j = 3);
printf("%d %d", i, j);

*11.

Napisz, co pojawi si na wyjciu programu wykonujcego ponisze instrukcje. Za, e i, j


i k s zmiennymi typu int:
(a) i = 1;
printf("%d ", i++ - 1);
printf("%d", i);
(b) i = 10; j = 5;
printf("%d ", i++ - ++j);
printf("%d %d", i, j);
(c) i = 7; j = 8;
printf("%d ", i++ - --j);
printf("%d %d", i, j);
(d) i = 3; j = 4; k = 5;
printf("%d ", i++ - j++ + --k);
printf("%d %d %d", i, j, k);

12.

Napisz, co pojawi si na wyjciu programu wykonujcego ponisze instrukcje. Za, e


i i j s zmiennymi typu int:
(a) i = 5;
j = ++i * 3 - 2;
printf("%d %d", i, j);
(b) i = 5;
j = 3 - 2 * i++;
printf("%d %d", i, j);

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.

Pogrupuj podwyraenia w nawiasy tak, aby zilustrowa sposb interpretacji poniszych


wyrae zoonych:
(a) a * b - c * d + e
(b) a / b % c / d
(c) - a - b + c - + d
(d) a * - b / c - d

Podrozdzia 4.5

15.

Podaj wartoci zmiennych i i j po wykonaniu kadej z poniszych instrukcji (pocztkowa


warto i to 1, a pocztkowa warto j to 2):
(a) i += j;
(b) i--;
(c) i * j / i;
(d) i % ++j;

Zadania programistyczne
1.

Napisz program, ktry bdzie wymaga od uytkownika wprowadzenia liczby dwucyfrowej,


a nastpnie wypisze t liczb w odwrconej kolejnoci cyfr. Sesja z programem powinna
przebiega tak:
Podaj liczb dwucyfrow: 28
Wspak: 82

2.

Rozbuduj program z zadania 1. tak, aby obsugiwa liczby trzycyfrowe.

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.

Napisz program, ktry wczytuje liczb wprowadzon na wejcie i wywietla j w zapisie


semkowym:
Podaj liczb pomidzy 0 i 32767: 1953
W zapisie semkowym to: 03641
Wyjcie programu powinno by wywietlane z uyciem piciu cyfr, nawet jeli zapis
liczby nie wymaga ich tylu. Wskazwka: Aby zamieni liczb na reprezentacj semkow,
naley podzieli j przez osiem. Wynik to pierwsza cyfra zapisu semkowego (tutaj: 1).
Reszt z dzielenia naley znw podzieli przez osiem i powtarza proces tak dugo, jak dugo
reszta bdzie wiksza od 8. Ostatnia cyfra to reszta z ostatniego dzielenia (jest te prostszy
sposb, bo funkcja printf potrafi wypisywa liczby cakowite w zapisie semkowym
przekonasz si o tym w rozdziale 7.).

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

You might also like