52

Język C. Nowoczesne - Księgarnia internetowa informatyczna …pdf.helion.pl/jcnpr2/jcnpr2-3_4.pdf · 2011-01-07 · panują niepodzielnie Java oraz .NET, ... „Język C. Nowoczesne

Embed Size (px)

Citation preview

Idź do

• Spis treści• Przykładowy rozdział

• Katalog online

• Dodaj do koszyka

• Zamów cennik

• Zamów informacjeo nowościach

• Fragmenty książekonline

Helion SAul. Kościuszki 1c44-100 Gliwicetel. 32 230 98 63e-mail: [email protected]© Helion 1991–2010

Katalog książek

Twój koszyk

Cennik i informacje

Czytelnia

Kontakt

• Zamów drukowanykatalog

Język C. Nowoczesneprogramowanie. Wydanie II

Autor: K. N. King

Tłumaczenie: Przemysław Szeremiota

ISBN: 978-83-246-2805-6

Tytuł oryginału: C Programming: A Modern Approach, 2nd Edition

Format: B5, stron: 928

Język C żyje i ma się dobrze. Sprawdź, co nowego w wersji C99!• Jak wygląda proces standaryzacji języka?

• Jak komentować kod?

• Jak przygotować projekt programu?

Język C należy do nielicznej grupy języków, które sprawdzają się w środowiskach produkcyjnych,

a jednocześnie nadają się do nauki programowania na uczelniach wyższych. Dzięki logicznej

i przejrzystej składni, jasno określonym zasadom wykorzystania oraz ogromnym możliwościom

język ten pomimo swojego wieku cieszy się popularnością i uznaniem. Nawet dziś, kiedy na rynku

panują niepodzielnie Java oraz .NET, język C znalazł swoją niszę i świetnie ją wypełnia. Na tym polu

żaden współczesny język nie ma z nim żadnych szans!

Kolejne wydanie książki rozszerzono między innymi o elementy zawarte w specyfikacji oznaczonej

numerem C99 (ISO 9899:1999). Co jeszcze wyróżnia tę książkę? Jej pierwsze wydanie było

wykorzystywane na kursach programowania prowadzonych przez 225 uczelni. Dzięki temu

zaliczana jest ona do najbardziej znaczących wydawnictw dotyczących języka C. Wydanie drugie

powiela zalety pierwszego, a dodatkowo zostało rozbudowane o jeszcze większą liczbę przykładów,

pytań, ćwiczeń i zadań programistycznych.

W trakcie pasjonującej lektury – zgadza się, K.N. King potrafi w ten sposób pisać o swoim ulubionym

języku – poznasz wszystkie aspekty programowania w języku C, począwszy od jego historii, poprzez

fundamentalne pojęcia funkcji, zmiennych, a skończywszy na zarządzaniu pamięcią oraz wykorzystaniu

wskaźników. „Język C. Nowoczesne programowanie. Wydanie II” to obowiązkowa pozycja dla

każdego studenta poznającego tajniki tego języka. Programiści znający język C niewątpliwie

docenią kunszt autora, a książka znajdzie zastosowanie jako przekrojowy przewodnik – taka

pozycja powinna być na półce każdego programisty!

Poznaj język C, korzystając z uznanego podręcznika!

5

SPIS TRE�CI

Wst�p 19

1. WPROWADZENIE 291.1. Historia j�zyka C 29

Pocz�tki 29Standaryzacja 30J�zyki oparte na C 31

1.2. Mocne i s�absze strony j�zyka C 32Mocne strony 33S�abo�ci 33Efektywne stosowanie j�zyka C 34

2. FUNDAMENTY J�ZYKA C 392.1. Piszemy prosty program 39

Wy�wietlamy cytat 40Kompilacja i konsolidacja 40Zintegrowane �rodowiska programistyczne 41

2.2. Ogólna posta� prostego programu w C 42Dyrektywy preprocesora 43Funkcje 43Instrukcje 44Wypisywanie ci�gów znakowych 45

2.3. Komentarze 462.4. Zmienne i przypisania 48

Typy 48Deklaracje 48Przypisania 49Wypisywanie warto�ci zmiennej 50Obliczanie gabarytu przesy�ki 51Inicjalizacja 52Wypisywanie warto�ci wyra�e� 53

6 Spis tre�ci

2.5. Wczytywanie danych 53Obliczanie gabarytu przesy�ki (podej�cie drugie) 54

2.6. Definiowanie nazw dla sta�ych 55Konwersja skali Fahrenheita na skal� Celsjusza 55

2.7. Identyfikatory 57S�owa kluczowe 58

2.8. Ogólny uk�ad programu C 58

3. FORMATOWANIE WEJ�CIA-WYJ�CIA 693.1. Funkcja printf 69

Specyfikatory konwersji 70Wykorzystanie printf do formatowania liczb 72Znaki steruj�ce 73

3.2. Funkcja scanf 74Dzia�anie funkcji scanf 76Zwyk�e znaki w ci�gu formatuj�cym funkcji scanf 78Skutki mylenia printf ze scanf 79Dodawanie u�amków 79

4. WYRA�ENIA 854.1. Operatory arytmetyczne 86

Pierwsze�stwo i ��czno� operatorów 87Obliczanie cyfry kontrolnej kodu kreskowego 88

4.2. Operatory przypisania 91Przypisania proste 91L-warto�ci 92Przypisania z�o�one 93

4.3. Operatory inkrementacji i dekrementacji 944.4. Obliczanie warto�ci wyrae 96

Kolejno� obliczania podwyra�e� 974.5. Instrukcje wyraeniowe 98

5. INSTRUKCJE WYBORU 1075.1. Wyraenia logiczne 108

Operatory relacji 108Operatory porówna� 109Operatory logiczne 109

5.2. Instrukcja if 111Instrukcje blokowe 112Klauzula else 112Kaskadowe instrukcje if 114Obliczanie prowizji brokera gie�dowego 115Problem „bezpa�skiego” else 116Wyra�enia warunkowe 117Warto�ci boolowskie w C89 118Warto�ci boolowskie w C99 120

5.3. Instrukcja switch 120Rola instrukcji break 123Wypisywanie daty w zapisie urz�dowym 124

Spis tre�ci 7

6. INSTRUKCJE P�TLI 1336.1. Instrukcja while 134

P�tle niesko�czone 135Wypisywanie tabeli kwadratów liczb 136Obliczanie sumy szeregu liczb 137

6.2. Instrukcja do 137Obliczanie liczby cyfr w liczbie ca�kowitej 138

6.3. Instrukcja for 139Idiomy instrukcji for 141Pomijanie wyra�e� w instrukcji for 142Instrukcje for w C99 143Operator przecinka 143Wypisywanie tabeli kwadratów liczb (podej�cie drugie) 144

6.4. Przerywanie p�tli 146Instrukcja break 146Instrukcja continue 147Instrukcja goto 148Saldo konta 149

6.5. Instrukcja pusta 151

7. PODSTAWOWE TYPY C 1617.1. Typy ca�kowite 161

Typy ca�kowite w C99 164Litera�y ca�kowite 164Litera�y ca�kowite w C99 166Przepe�nienie zakresu 166Wczytywanie i wypisywanie warto�ci ca�kowitych 166Sumowanie szeregu liczb ca�kowitych (podej�cie drugie) 167

7.2. Typy zmiennoprzecinkowe 168Litera�y zmiennoprzecinkowe 170Wczytywanie i wypisywanie warto�ci zmiennoprzecinkowych 170

7.3. Typy znakowe 171Operacje na znakach 172Znaki ze znakiem i bez znaku 173Typy arytmetyczne 173Znaki steruj�ce 174Funkcje do manipulowania znakami 176Wczytywanie i wypisywanie znaków funkcjami scanf i printf 176Wczytywanie i wypisywanie znaków funkcjami getchar i putchar 177Okre�lanie d�ugo�ci komunikatu 179

7.4. Konwersja typów 180Zwyczajne konwersje arytmetyczne 181Konwersja przy przypisaniu 183Niejawne konwersje w C99 184Rzutowanie 185

7.5. Definicje typów 186Zalety definicji typów 187Definicje typów a przeno�no� programów 188

7.6. Operator sizeof 189

8 Spis tre�ci

8. TABLICE 1998.1. Tablice jednowymiarowe 199

Indeksowanie tablic 200Odwracanie szeregu liczbowego 202Inicjalizacja tablicy 203Inicjalizatory desygnowane 203Sprawdzanie, czy liczba zawiera powtarzaj�ce si� cyfry 204Operator sizeof dla tablic 205Naliczanie odsetek 206

8.2. Tablice wielowymiarowe 208Inicjalizowanie tablic wielowymiarowych 209Sta�e tablicowe 210Rozdawanie kart 211

8.3. Tablice o zmiennej liczbie elementów (C99) 212

9. FUNKCJE 2239.1. Definiowanie i wywo�ywanie funkcji 223

Obliczanie �rednich 224Odliczanie 225Wy�wietlanie napisu (kolejne podej�cie) 226Definicja funkcji 227Wywo�anie funkcji 229Sprawdzanie, czy podana liczba jest liczb� pierwsz� 230

9.2. Deklaracja funkcji 2319.3. Argumenty 233

Konwersje argumentów 234Argumenty tablicowe 235Parametry tablicowe o zmiennym rozmiarze 238Deklaracje parametrów tablicowych ze s�owem static 240Litera�y tablicowe 241

9.4. Instrukcja return 2429.5. Zakoczenie programu 243

Funkcja exit 2439.6. Rekurencja 244

Algorytm quicksort 246quicksort 248

10. ORGANIZACJA PROGRAMU 26110.1. Zmienne lokalne 261

Zmienne statyczne funkcji 262Parametry 263

10.2. Zmienne zewn�trzne 263Przyk�ad. Stos implementowany na zmiennych zewn�trznych 263Zalety i wady zmiennych zewn�trznych 264Zgadywanka liczbowa 266

10.3. Bloki 27010.4. Zasi�g zmiennych 27110.5. Organizacja programu w C 272

Si�a rozdania pokerowego 273

Spis tre�ci 9

11. WSKA�NIKI 28311.1. Zmienne wska�nikowe 283

Deklarowanie zmiennych wskanikowych 28411.2. Operator adresu i wy�uskania 285

Operator adresu 285Operator wy�uskania 286

11.3. Przypisania a wska�niki 28711.4. Wska�niki jako argumenty funkcji 289

Wyszukiwanie najwi�kszego i najmniejszego elementu tablicy 291Ochrona argumentów za pomoc� const 293

11.5. Wska�niki jako warto�ci zwracane 293

12. WSKA�NIKI A TABLICE 30112.1. Arytmetyka wska�ników 302

Dodawanie liczby do wskanika 303Odejmowanie liczby od wskanika 303Odejmowanie wskanika od wskanika 304Porównywanie wskaników 304Wskaniki do litera�ów tablicowych 304

12.2. Przetwarzanie tablic na bazie wska�ników 305��czenie operatorów * i ++ 306

12.3. Nazwa tablicy jako wska�nik 307Odwracanie szeregu liczbowego 308Argumenty tablicowe (ponownie) 309Wskanik jako nazwa tablicy 311

12.4. Wska�niki a tablice wielowymiarowe 311Przetwarzanie elementów tablicy wielowymiarowej 311Przetwarzanie wierszy tablicy wielowymiarowej 312Przetwarzanie kolumn tablicy wielowymiarowej 313Nazwa tablicy wielowymiarowej jako wskanik 314

12.5. Wska�niki a tablice o zmiennym rozmiarze (C99) 314

13. CI GI ZNAKÓW 32313.1. Litera�y napisowe 323

Znaki steruj�ce w litera�ach napisowych 324Kontynuacja litera�u napisowego w nowym wierszu 324Litera�y napisowe a pami� programu 325Operacje na litera�ach napisowych 326Litera�y napisowe a litera�y znakowe 326

13.2. Zmienne napisowe 327Inicjalizowanie zmiennej napisowej 328Tablice znaków a wskaniki do znaków 329

13.3. Wczytywanie i wypisywanie napisów 330Wypisywanie napisów funkcjami printf i puts 330Wczytywanie ci�gów znaków funkcjami scanf i gets 331Wczytywanie napisów znak po znaku 333

13.4. Odwo�ania do pojedynczych znaków w ci�gu 33413.5. Funkcje biblioteczne j�zyka C 335

Funkcja strcpy (kopiowanie ci�gów) 336Funkcja strlen (d�ugo� ci�gu) 338Funkcja strcat (��czenie ci�gów) 338

10 Spis tre�ci

Funkcja strcmp (porównywanie ci�gów) 339Wypisywanie notatek kalendarzowych 340

13.6. Idiomy 343Szukanie ko�ca ci�gu 343Kopiowanie ci�gu 345

13.7. Tablice ci�gów znaków 347Argumenty wywo�ania programu 349Weryfikacja nazw planet 351

14. PREPROCESOR 36314.1. Jak dzia�a preprocesor 36314.2. Dyrektywy preprocesora 36614.3. Makrodefinicje 367

Makrodefinicje proste 367Makrodefinicje sparametryzowane 370Operator # 373Operator ## 374Ogólne w�a�ciwo�ci makrodefinicji 375Nawiasy w makrodefinicjach 376Tworzenie d�ugich makrodefinicji 377Makrodefinicje predefiniowane 379Dodatkowe makrodefinicje predefiniowane w C99 380Puste argumenty makrodefinicji 381Makrodefinicje o zmiennej liczbie argumentów 382Identyfikator __func__ 383

14.4. Warunkowa kompilacja kodu 384Dyrektywy #if i #endif 384Operator defined 385Dyrektywy #ifdef i #ifndef 386Dyrektywy #elif i #else 386Zastosowania warunkowej kompilacji kodu 387

14.5. Inne dyrektywy 388Dyrektywa #error 389Dyrektywa #line 390Dyrektywa #pragma 391Operator _Pragma 391

15. DU�E PROGRAMY 40115.1. Pliki �ród�owe 40115.2. Pliki nag�ówkowe 403

Dyrektywa #include 403Wspólne makrodefinicje i synonimy typów 405Wspólne prototypy funkcji 406Wspólne deklaracje zmiennych 407Zagnie�d�one dyrektywy #include 409Ochrona plików nag�ówkowych 410Dyrektywy #error w plikach nag�ówkowych 411

15.3. Podzia� programu na pliki 411Formatowanie tekstu 412

15.4. Budowanie programu z wielu plików 419Pliki Makefile 419B��dy konsolidowania programu 422

Spis tre�ci 11

Przebudowa programu 422Definiowanie makrodefinicji na zewn�trz programu 425

16. STRUKTURY, UNIE I WYLICZENIA 43116.1. Zmienne strukturalne 431

Deklarowanie zmiennych strukturalnych 432Inicjalizowanie zmiennych strukturowych 433Inicjalizatory desygnowane 434Operacje na strukturach 435

16.2. Typy strukturowe 436Deklarowanie znacznika struktury 437Definiowanie typu strukturowego 438Struktury jako argumenty i warto�ci zwracane funkcji 439Litera�y strukturowe 440

16.3. Tablice i struktury zagniedone 441Struktury struktur 441Tablice struktur 442Inicjalizowanie tablic struktur 443Zarz�dzanie baz� danych magazynu 444

16.4. Unie 450Unie dla oszcz�dno�ci 452Unie jako mieszane struktury danych 454Pole „wyró�nika” w unii 455

16.5. Wyliczenia 456Znaczniki i typy wyliczeniowe 457Wyliczenia jako liczby ca�kowite 458Wyliczenia jako wyró�niki unii 459

17. ZAAWANSOWANE ZASTOSOWANIA WSKA�NIKÓW 46917.1. Dynamiczny przydzia� pami�ci 470

Funkcje przydzia�u pami�ci 470Wskaniki puste 471

17.2. Dynamiczny przydzia� ci�gów znaków 472Przydzia� pami�ci dla ci�gu znaków za pomoc� funkcji malloc 472Przydzia�y dynamiczne

w funkcjach operuj�cych na ci�gach znaków 473Tablice ci�gów przydzielanych dynamicznie 474Wypisywanie notatek kalendarzowych (podej�cie drugie) 475

17.3. Tablice przydzielane dynamicznie 476Przydzia� pami�ci dla ci�gu znaków za pomoc� funkcji malloc 477Funkcja calloc 478Funkcja realloc 478

17.4. Zwalnianie pami�ci 479Funkcja free 480Problem „wisz�cych” wskaników 481

17.5. Listy elementów 481Deklarowanie typu w�z�a 482Tworzenie w�z�a listy 483Operator -> 484Wstawianie w�z�a na pocz�tek listy 484Przeszukiwanie listy 487Usuwanie w�z�a z listy 488

12 Spis tre�ci

Listy uporz�dkowane 490Zarz�dzanie baz� danych magazynu (drugie podej�cie) 491

17.6. Wska�niki do wska�ników 49617.7. Wska�niki do funkcji 497

Wskaniki do funkcji w roli argumentów 497Funkcja qsort 498Inne zastosowania wskaników do funkcji 501Tablice funkcji trygonometrycznych 502

17.8. Wska�niki zastrzeone (C99) 50317.9. Elastyczne sk�adowe tablicowe (C99) 505

18. DEKLARACJE 51718.1. Sk�adnia deklaracji 51718.2. Klasy przydzia�u 519

W�asno�ci zmiennych 519Klasa przydzia�u auto 520Klasa przydzia�u static 521Klasa przydzia�u extern 522Klasa przydzia�u register 523Klasa przydzia�u funkcji 524Podsumowanie 525

18.3. Kwalifikatory typów 52618.4. Deklaratory 528

Rozszyfrowywanie zawi�ych deklaracji 529Stosowanie synonimów typów dla uproszczenia deklaracji 531

18.5. Inicjalizatory 531Zmienne niezainicjalizowane 533

18.6. Funkcje inline (C99) 533Definicje rozwijane w miejscu wywo�ania 534Ograniczenia funkcji rozwijanych w miejscu wywo�ania 536Funkcje inline w GCC 536

19. PROJEKT PROGRAMU 54519.1. Modu�y 546

Spójno� i wspó�zale�no� 548Rodzaje modu�ów 548

19.2. Ukrywanie informacji 549Modu� obs�ugi stosu 550

19.3. Abstrakcyjne typy danych 553Hermetyzacja 554Typy niepe�ne 554

19.4. Stos jako abstrakcyjny typ danych (ADT) 555Definiowanie interfejsu stosu w wersji ADT 555Implementacja stosu w wersji ADT (na bazie tablicy) 557Zmiana typu elementu w stosie w wersji ADT 559Implementowanie stosu ADT (na bazie tablicy dynamicznej) 560Implementowanie stosu ADT (na bazie listy) 562

19.5. Problemy projektowe przy ADT 564Nomenklatura 564Obs�uga b��dów 565Uniwersalny typ ADT 565ADT w nowszych j�zykach programowania 566

Spis tre�ci 13

20. PROGRAMOWANIE NISKOPOZIOMOWE 57120.1. Operatory bitowe 571

Operatory przesuni� bitowych 572Negacja, iloczyn, suma i suma wy��czaj�ca 573Operatory bitowe w odwo�aniach

do poszczególnych bitów warto�ci liczbowych 574Operatory bitowe w odwo�aniach do pól bitowych 576Szyfrowanie XOR 577

20.2. Pola bitowe w strukturach 578Reprezentacja pól bitowych 580

20.3. Inne niskopoziomowe techniki programistyczne 581Definiowanie typów maszynowych 581Unie jako perspektywy 582Wskaniki jako adresy 584Podgl�d pami�ci 584Kwalifikator typu volatile 586

21. BIBLIOTEKA STANDARDOWA 59321.1. Stosowanie biblioteki standardowej 593

Nazewnictwo w bibliotece standardowej 594Funkcje ukrywane przez makrodefinicje 595

21.2. Przegl�d biblioteki standardowej C89 596Diagnostyka 596Obs�uga znaków 596B��dy 596Cechy typów zmiennoprzecinkowych 596Rozmiary typów ca�kowitoliczbowych 596Lokalizacja programów 597Matematyka 597Skoki nielokalne 597Obs�uga sygna�ów 597Zmienne listy argumentów 597Podstawowe definicje 597Wej�cie-wyj�cie 597Narz�dzia 598Obs�uga ci�gów znaków 598Daty i godziny 598

21.3. Uzupe�nienia i zmiany w C99 598Arytmetyka liczb zespolonych 599�rodowisko implementacji zmiennoprzecinkowej 599Znakowe konwersje typów ca�kowitoliczbowych 599Alternatywny zapis sk�adni C 599Warto�ci i typy logiczne 599Typy ca�kowitoliczbowe 599Matematyka na uniwersalnych typach 599Operacje na znakach wielobajtowych 600Narz�dzia mapowania i klasyfikacji znaków wielobajtowych 600

21.4. Nag�ówek <stddef.h> — definicje podstawowe 60021.5. Nag�ówek <stdbool.h> (C99) — typy i warto�ci logiczne 601

14 Spis tre�ci

22. WEJ�CIE-WYJ�CIE 60522.1. Strumienie 606

Wskaniki plikowe 606Strumienie standardowe a przekierowania 607Pliki tekstowe i pliki binarne 608

22.2. Operacje na plikach 609Otwieranie pliku 610Tryby dost�pu do plików 611Zamykanie pliku 612Do��czanie pliku do otwartego strumienia 613Pobieranie nazw plików z wiersza polecenia 613Sprawdzanie mo�liwo�ci otwarcia pliku 614Pliki tymczasowe 615Buforowanie plików 616Inne operacje na plikach 618

22.3. Formatowanie wej�cia-wyj�cia 619Funkcje …printf 619Specyfikatory konwersji dla funkcji …printf 620Zmiany specyfikatorów konwersji w C99 622Przyk�ady specyfikacji konwersji dla funkcji …printf 624Funkcje …scanf 626Ci�gi formatuj�ce funkcji …scanf 627Specyfikacje konwersji funkcji …scanf 628Zmiany specyfikatorów konwersji w C99 631Przyk�ady dla funkcji scanf 631Wykrywanie ko�ca strumienia wej�ciowego i b��dów 632

22.4. Wej�cie-wyj�cie znakowe 635Funkcje wyj�cia 635Funkcje wej�cia 636Kopiowanie pliku 637

22.5. Wierszowe wej�cie-wyj�cie 638Funkcje wyj�cia 638Funkcje wej�cia 639

22.6. Blokowe wej�cie-wyj�cie 64022.7. Pozycjonowanie w plikach 641

Modyfikowanie pliku rekordów bazy danych 64322.8. Funkcje wej�cia-wyj�cia w pami�ci 644

Funkcje wyj�cia 645Funkcje wej�cia 646

23. OBS�UGA LICZB I DANYCH ZNAKOWYCH 65923.1. Nag�ówek <float.h> — cechy typów zmiennoprzecinkowych 65923.2. Nag�ówek <limits.h> — rozmiary typów ca�kowitych 66223.3. Nag�ówek <math.h> — matematyka 664

B��dy 664Funkcje trygonometryczne 665Funkcje hiperboliczne 666Funkcje wyk�adnicze i logarytmiczne 666Funkcje pot�gowe 667Najbli�sza liczba ca�kowita, warto� bezwzgl�dna,

reszta z dzielenia 668

Spis tre�ci 15

23.4. Nag�ówek <math.h> — matematyka (C99) 669Standard zmiennoprzecinkowy IEEE 669Typy 671Makrodefinicje 671B��dy 672Funkcje 673Makrodefinicje klasyfikuj�ce 674Funkcje trygonometryczne 675Funkcje hiperboliczne 675Funkcje wyk�adnicze i logarytmiczne 676Funkcje pot�gowe i funkcje warto�ci bezwzgl�dnej 677Funkcje b��dów i funkcje gamma 678Funkcje zaokr�glania 679Funkcje reszty z dzielenia 680Funkcja manipulacji 680Funkcje maksimum, minimum i ró�nicy dodatniej 681Zmiennoprzecinkowy iloczyn-suma 682Makrodefinicje porówna� 683

23.5. Nag�ówek <ctype.h> — obs�uga znaków 684Funkcje klasyfikacji znaków 684Test funkcji klasyfikacji znaków 685Funkcje mapowania wielko�ci liter 686Test funkcji zmiany wielko�ci liter 687

23.6. Nag�ówek <string.h> — obs�uga ci�gów znaków 687Funkcje kopiuj�ce 688Funkcje ��czenia ci�gów 689Funkcje porówna� 690Funkcje wyszukuj�ce 691Ró�ne 695

24. OBS�UGA B��DÓW 69924.1. Nag�ówek <assert.h> — diagnostyka 70024.2. Nag�ówek <errno.h> — b��dy 701

Funkcje perror i strerror 70224.3. Nag�ówek <signal.h> — obs�uga sygna�ów 703

Makrodefinicje sygna�ów 704Funkcja signal 704Predefiniowane funkcje obs�ugi sygna�ów 705Funkcja raise 707Testowanie mechanizmu sygna�ów 707

24.4. Nag�ówek <setjmp.h> — skoki nielokalne 708Testowanie setjmp/longjmp 709

25. „MI�DZYNARODÓWKA” 71525.1. Nag�ówek <locale.h> — �rodowiska j�zykowe 716

Kategorie 716Funkcja setlocale 717Funkcja localeconv 719

25.2. Znaki wielobajtowe i znaki poszerzone 722Znaki wielobajtowe 723Znaki poszerzone 724Unicode i uniwersalny zestaw znaków UCS 724

16 Spis tre�ci

Kodowanie Unicode 725Funkcje konwersji znaków poszerzonych i wielobajtowych 727Funkcje konwersji ci�gów znaków poszerzonych i wielobajtowych 729

25.3. Dwuznaki i trójznaki 729Trójznaki 730Dwuznaki 731Nag�ówek <iso646.h> — symbole alternatywne 731

25.4. Uniwersalne nazwy znaków (C99) 73225.5. Nag�ówek <wchar.h> (C99) — dodatkowe narz�dzia

dla znaków poszerzonych i wielobajtowych 733Orientacja strumienia 734Funkcje formatowanego wej�cia-wyj�cia

dla znaków poszerzonych 735Funkcje wej�cia-wyj�cia dla znaków poszerzonych 737Obs�uga ci�gów znaków poszerzonych 738Funkcja konwersji dat i godzin na ci�gi znaków poszerzonych 743Dodatkowe funkcje konwersji znaków poszerzonych

i wielobajtowych 74325.6. Nag�ówek <wctype.h> (C99) — klasyfikacja

znaków poszerzonych 747Funkcje klasyfikacji znaków poszerzonych 747Rozszerzalne funkcje klasyfikacji znaków poszerzonych 748Funkcje zmiany wielko�ci liter dla znaków poszerzonych 749Rozszerzalne funkcje zmiany wielko�ci liter

znaków poszerzonych 750

26. RÓ�NE 75526.1. Nag�ówek <stdarg.h> — zmienna liczba argumentów 755

Wywo�anie funkcji o zmiennej liczbie argumentów 758Funkcje v…printf 758Funkcje v…scanf 759

26.2. Nag�ówek <stdlib.h> — inne narz�dzia 760Funkcje konwersji liczbowych 761Testowanie funkcji konwersji liczbowych 762Funkcje sekwencji pseudolosowych 764Testowanie funkcji generowania liczb pseudolosowych 765Komunikacja ze �rodowiskiem wykonawczym 766Wyszukiwanie i sortowanie 768Okre�lanie odleg�o�ci 769Funkcje arytmetyki liczb ca�kowitych 770

26.3. Nag�ówek <time.h> — daty i godziny 771Funkcje operuj�ce na datach i godzinach 772Funkcje konwersji dat i godzin 774Wypisywanie daty i godziny 778

27. ROZSZERZONE OPERACJE MATEMATYCZNE W C99 78727.1. Nag�ówek <stdint.h> — typy ca�kowite 788

Typy nag�ówka <stdint.h> 788Ograniczenia typów o okre�lonym rozmiarze 790Ograniczenia pozosta�ych typów ca�kowitych 790Makrodefinicje dla sta�ych ca�kowitych 791

Spis tre�ci 17

27.2. Nag�ówek <inttypes.h> — konwersje typów ca�kowitych 792Makrodefinicje dla specyfikatorów konwersji 792Funkcje obs�ugi najszerszych typów 793

27.3. Liczby zespolone (C99) 795Definicja liczb zespolonych 795Arytmetyka liczb zespolonych 797Typy zespolone w C99 797Operacje na warto�ciach zespolonych 798Regu�y konwersji dla typów zespolonych 798

27.4. Nag�ówek <complex.h> (C99) — arytmetykaliczb zespolonych 800Makrodefinicje nag�ówka <complex.h> 800CX_LIMITED_RANGE 801Funkcje nag�ówka <complex.h> 802Funkcje trygonometryczne 802Funkcje hiperboliczne 803Funkcje wyk�adnicze i logarytmiczne 804Funkcje pot�gowe i funkcje warto�ci bezwzgl�dnych 804Inne 805Szukanie pierwiastków równania kwadratowego 805

27.5. Nag�ówek <tgmath.h> (C99) — matematyka bez typów 806Makrodefinicje rozprowadzaj�ce wywo�ania

funkcji matematycznych 807Wywo�ania makrodefinicji rozprowadzaj�cych 807

27.6. Nag�ówek <fenv.h> (C99)— �rodowisko zmiennoprzecinkowe 810Stany i tryby jednostki zmiennoprzecinkowej 810Makrodefinicje nag�ówka <fenv.h> 811FENV_ACCESS 811Funkcje wyj�tków zmiennoprzecinkowych 813Funkcje zaokr�glania 814Funkcje �rodowiska 815

Dodatek A Operatory j�zyka C 819Dodatek B C99 kontra C89 821Dodatek C C89 kontra K&R 827Dodatek D Funkcje biblioteki standardowej 831Dodatek E Zestaw znaków ASCII 893

Bibliografia 895Skorowidz 899

69

3 Formatowaniewej�cia-wyj�cia

W poszukiwaniu nieosi�galnego na przeszkodzie staje tylko prostota.

Do najcz��ciej wykorzystywanych funkcji bibliotecznych j�zyka C nale�� printfi scanf, s�u��ce do obs�ugi formatowanego wej�cia i wyj�cia programu. W tymrozdziale przekonasz si� o mo�liwo�ciach tych funkcji, ale te� o koniecznej ostro�-no�ci w ich stosowaniu. W podrozdziale 3.1 zajmiemy si� funkcj� printf. G�ów-nym zagadnieniem podrozdzia�u 3.2 b�dzie funkcja scanf. W �adnym z pod-rozdzia�ów nie zg��bimy jednak wszystkich detali — niektóre b�d� musia�ypoczeka� do rozdzia�u 22.

3.1. Funkcja printfFunkcja printf s�u�y do wypisywania na wyj�ciu programu zawarto�ci ci�guznaków, okre�lanego mianem ci�gu formatuj�cego, który mo�e zawiera� sym-bole zast�pcze dla warto�ci zmiennych wstawianych do ci�gu wypisywanego.W wywo�aniu funkcji printf musi si� znajdowa� ci�g formatuj�cy, uzupe�nionywarto�ciami, które maj� by� podstawione w odpowiednie miejsca ci�gu na wyj�ciuprogramu:

printf(ci�g-formatuj�cy, wyra�enie1, wyra�enie1, …);

Warto�ci przekazywane do podstawienia do ci�gu formatuj�cego mog� by� sta-�ymi, zmiennymi albo ca�ymi wyra�eniami. Nie istnieje ograniczenie liczby war-to�ci wypisywanych w ramach pojedynczego wywo�ania funkcji printf.

Ci�g formatuj�cy mo�e zawiera� zarówno zwyczajne znaki drukowalne, jaki tak zwane specyfikatory konwersji, rozpoczynaj�ce si� od znaku %. Specyfi-kator konwersji to symbol zast�pczy reprezentuj�cy warto��, która ma zosta�wstawiona w dane miejsce ci�gu formatuj�cego, wraz z opisem sposobu wypisa-nia warto�ci. Informacje znajduj�ce si� za znakiem % okre�laj� sposób konwersji

70 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

przekazanej warto�ci z jej reprezentacji wewn�trznej (binarnej) na reprezentacj�drukowan� (znakow�) — st�d poj�cie „specyfikatora konwersji”. Na przyk�adspecyfikator konwersji %d mówi, �e printf ma zamieni� przekazan� warto��typu int z jej reprezentacji binarnej na ci�g znaków kolejnych cyfr. Podobniespecyfikator %f nakazuje zamian� warto�ci zmiennoprzecinkowej (typu float)na znakow�.

Zwyczajne znaki zawarte w ci�gu formatuj�cym s� wypisywane bez modyfi-kacji. Specyfikatory konwersji s� natomiast zast�powane przez znakowe repre-zentacje przekazanych warto�ci. Wemy nast�puj�cy przyk�ad:

int i, j;float x, y;

i = 10;j = 20;x = 43.2892f;y = 5527.0f;

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

Takie wywo�anie funkcji printf spowoduje wypisanie na wyj�ciu:

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

Zwyczajne znaki w ci�gu formatuj�cym zosta�y po prostu skopiowane na wyj�cie.Cztery specyfikatory konwersji zosta�y za� zast�pione przez odpowiednio repre-zentowane warto�ci zmiennych i, j, x i y (w tej kolejno�ci).

Specyfikatory konwersjiSpecyfikatory konwersji pozwalaj� programistom zachowa� du�� doz� kontrolinad wygl�dem (formatem) wypisywanych ci�gów i warto�ci. Z drugiej stronybywaj� skomplikowane i trudne do ogarni�cia. Istotnie, szczegó�owe opisywaniespecyfikatorów konwersji by�oby na tym wst�pnym etapie omówienia przed-wczesne. Zapoznamy si� wi�c tylko z najwa�niejszymi cechami i mo�liwo�ciamidawanymi przez specyfikatory.

W rozdziale 2. zauwa�yli�my, �e specyfikator konwersji mo�e zawiera� infor-macje steruj�ce formatowaniem warto�ci. W szczególno�ci zastosowali�my spe-cyfikator %.1f, aby ograniczy� liczb� wypisywanych cyfr po przecinku w war-to�ci typu float. Ogólniej rzecz bior�c, specyfikator konwersji mo�e przyj��posta� %m.pX albo %-m.pX, gdzie m i p to sta�e ca�kowite, a X to litera. Warto�cim i p s� opcjonalne. W przypadku nieobecno�ci p nie stosuje si� równie� kropkioddzielaj�cej m od p. W specyfikatorze konwersji %10.2f m wynosi 10, p wy-nosi 2, a X to f. W specyfikatorze %10f m wynosi 10, p (wraz z kropk�) zosta�opomini�te, a X to f. Za to w %.2f m zosta�o pomini�te, a p wynosi 2.

Minimalna szeroko� pola m okre�la minimaln� liczb� znaków, jaka zostaniewypisana na wyj�ciu przy wypisywaniu warto�ci. Je�li wypisywana warto�� jestw reprezentacji znakowej krótsza ni� m znaków, zostanie wyrównana do minimal-nej szeroko�ci pola i do prawej strony pola (innymi s�owy, przed w�a�ciw� war-to�� wstawiona b�dzie odpowiednia liczba spacji). Na przyk�ad specyfikator %4d

3.1. Funkcja printf 71

Standard nie wymaga od kompilatorów j�zyka C sprawdzania, czy liczba specy-fikatorów konwersji okre�lona w ci�gu formatuj�cym odpowiada liczbie przeka-zanych warto�ci. Poni�sze wywo�anie funkcji printf posiada wi�cej specyfika-torów konwersji ni� warto�ci do wypisania:

printf("%d %d\n", i); /*** �LE ***/

Funkcja printf wypisze poprawnie warto�� zmiennej i, a nast�pnie wypiszedrug� — oczekiwan�, ale nieokre�lon� — warto�� liczbow�. Podobnie k�opotliwyjest nadmiar warto�ci w stosunku do specyfikatorów konwersji:

printf("%d\n", i, j); /*** �LE ***/

W tym przypadku funkcja printf wypisze jedynie warto�� i, a warto�� j zosta-nie pomini�ta.

Kompilatory nie s� te� zobligowane do sprawdzania, czy specyfikatory kon-wersji odpowiadaj� typom przekazywanych warto�ci. Je�li programista zastosujenieodpowiednie specyfikatory, program mo�e wypisa� na wyj�ciu kompletnebzdury. Wemy dla przyk�adu wywo�anie funkcji printf, w którym zmienna itypu int i zmienna x typu float zostan� przekazane w z�ej kolejno�ci:

printf("%f %d\n", i, x); /*** �LE ***/

Poniewa� funkcja printf realizuje wytyczne z ci�gu formatuj�cego, wypiszena wyj�ciu warto�� float, a za ni� warto�� int. Niestety, obie warto�ci jako lezinterpretowane b�d� niepoprawne.

spowoduje wypisanie warto�ci 123 jako ·123 (w ca�ym bie��cym rozdzialew miejsce niewidocznych spacji ilustruj�cych formatowanie warto�ci b�d� wsta-wiane znaki ·). Z kolei kiedy wypisywana warto�� b�dzie d�u�sza ni� m znaków,pole zostanie automatycznie poszerzone do szeroko�ci potrzebnej do zmieszczeniawarto�ci: specyfikator %4d dla warto�ci 12345 spowoduje wypisanie na wyj�ciu12345 — bez ucinania cyfr. Znak minusa przed m wymusza wyrównanie warto�ciw polu do lewej strony: specyfikator %4d dla warto�ci 123 da na wyj�ciu 123·.

Znaczenie specyfikatora precyzji p jest trudniejsze do opisania, poniewa� jegodzia�anie jest zale�ne od X, czyli w�a�ciwego specyfikatora konwersji. X okre�larodzaj konwersji do przeprowadzenia przed wypisaniem warto�ci. Do najpopu-larniejszych konwersji warto�ci liczbowych nale��:

� d — konwersja warto�ci ca�kowitej do postaci dziesi�tnej. W takim uk�adziep oznacza minimaln� liczb� cyfr do wypisania (w razie potrzeby przed wypi-sywan� liczb� dopisywane s� zera). Przy braku p uznaje si�, �e ma ono warto��1 (innymi s�owy, %d jest tym samym co %.1d).

� e — konwersja warto�ci zmiennoprzecinkowej do postaci wyk�adnikowej(w tzw. notacji naukowej). W takim uk�adzie p okre�la liczb� cyfr do wypi-sania po przecinku dziesi�tnym (warto�� domy�lna to 6). Dla p równego 0cz��� po przecinku nie jest wy�wietlana w ogóle.

� f — konwersja warto�ci zmiennoprzecinkowej do postaci dziesi�tnej, bezwyk�adnika. W tym uk�adzie p ma takie samo znaczenie jak przy konwersji e.

72 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

� g — konwersja warto�ci zmiennoprzecinkowej do postaci wyk�adnikowej albodziesi�tnej, zale�nie od rozmiaru liczby. W tym uk�adzie p oznacza maksy-maln� liczb� cyfr znacz�cych (nie cyfr po przecinku) do wypisania. Inaczej ni�przy konwersji f, konwersja g nie b�dzie wypisywa�a zer po prawej stroniewarto�ci. Co wi�cej, je�li konwertowana warto�� nie ma cyfr po przecinku,konwersja g nie wypisze tak�e symbolu przecinka.

Specyfikator konwersji g jest przydatny zw�aszcza do wypisywania warto�ci, dlaktórych rozmiar reprezentacji nie da si� ustali� na etapie pisania programu, orazwarto�ci z bardzo szerokich dziedzin. Konwersja g dla niezbyt wielkich i niezbytma�ych warto�ci b�dzie owocowa�a zapisem dziesi�tnym. Dla warto�ci bardzoma�ych i bardzo du�ych zastosowany b�dzie zapis wyk�adnikowy, wymagaj�cymniejszej liczby znaków.

%d, %e, %f i %g to bynajmniej nie wszystkie specyfikatory konwersji. Pozo-sta�e b�d� stopniowo wprowadzane przy okazji omawiania kolejnych zagadnie�.Pe�na lista specyfikatorów wraz z obja�nieniem ich znaczenia i dzia�ania znajdujesi� w podrozdziale 22.3.

Wykorzystanie printf do formatowania liczbPoni�szy program ilustruje sposób wykorzystania funkcji printf do wypisy-wania warto�ci ca�kowitych i zmiennoprzecinkowych w rozmaitych formatach:

/* Wypisuje warto�ci 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 ci�gach formatuj�cych dla funkcji printf s�u�� jedynie do roz-dzielenia wypisywanych warto�ci i zilustrowania szeroko�ci pól, w których s�wypisywane. W przeciwie�stwie do znaków % i \ znak | nie ma specjalnego zna-czenia dla funkcji printf. Program wypisuje na wyj�ciu co� takiego:

|40| 40|40 | 040|| 839.210| 8.392e+02|839.21 |

Spróbujmy przeanalizowa� znaczenie i dzia�anie specyfikatorów konwersjiwykorzystanych w tym programie:

specyfikatorydla warto�ci ca�kowitych � 7.1

specyfikatory dla warto�cizmiennoprzecinkowych � 7.2

specyfikatorydla warto�ci znakowych � 7.3

specyfikatorydla ci�gów znaków � 13.3

PROGRAM

tprintf.c

3.1. Funkcja printf 73

� %d — wypisanie i w zapisie dziesi�tnym przy jak najmniejszej liczbie znaków.� %5d — wypisanie warto�ci i w zapisie dziesi�tnym w polu o szeroko�ci co

najmniej 5 znaków. Poniewa� w�a�ciwa warto�� i ma zaledwie dwa znaki, polejest wype�niane trzema spacjami od lewej strony.

� %-5d — wypisanie warto�ci i w zapisie dziesi�tnym w polu o szeroko�ci conajmniej 5 znaków. Poniewa� w�a�ciwa warto�� i ma zaledwie dwa znaki,pole jest wype�niane trzema spacjami po prawej stronie warto�ci (warto�� i jestwi�c wyrównana w pi�cioznakowym polu do lewej strony).

� %5.3d — wypisanie warto�ci i w zapisie dziesi�tnym w polu o szeroko�cico najmniej 5 znaków i przy reprezentowaniu warto�ci co najmniej trzemacyframi. Poniewa� i ma tylko dwie cyfry, przed w�a�ciw� warto�ci� wstawianajest pojedyncza cyfra zero. Wynikowa trzyznakowa warto�� jest wypisywanaw polu o szeroko�ci 5 znaków, a wi�c jest poprzedzona dwoma spacjami (war-to�� i jest wyrównana do prawej strony pola).

� %10.3f — wypisanie warto�ci x w zapisie dziesi�tnym z przecinkiem w poluo szeroko�ci co najmniej 10 znaków, z trzema cyframi po przecinku. Poniewa�warto�� x zajmuje jedynie 7 znaków (trzy przed przecinkiem i trzy po prze-cinku oraz sam przecinek), jest uzupe�niana trzema spacjami z lewej strony.

� %10.3e — wypisanie warto�ci x w zapisie wyk�adnikowym w polu o sze-roko�ci co najmniej 10 znaków, z trzema cyframi po przecinku. Tak zapisanawarto�� x zajmuje 9 znaków, wi�c jest uzupe�niona spacj� z lewej strony.

� %-10g — wypisanie warto�ci x w zapisie wyk�adnikowym albo dziesi�tnymz przecinkiem w polu o szeroko�ci co najmniej 10 znaków, z trzema cyframipo przecinku. W naszym przypadku warto�� x zosta�a zamieniona na dzie-si�tn� z przecinkiem. Obecno�� znaku - w specyfikatorze konwersji wymuszawyrównanie warto�ci do lewej strony pola czterema spacjami za w�a�ciw�warto�ci�.

Znaki steruj�ceSymbol \n, wykorzystywany ju� w poprzednich ci�gach formatuj�cych, to przy-k�ad tak zwanego znaku steruj�cego (ang. escape sequence). Znaki steruj�cepozwalaj� na osadzanie w ci�gach znaków, które zapisane inaczej by�yby k�opo-tliwe dla kompilatora. Dotyczy to przede wszystkim znaków niedrukowalnychterminali znakowych oraz znaków posiadaj�cych specjalne znaczenie dla samegokompilatora (jak "). Kompletn� list� znaków steruj�cych zamie�cimy póniej, narazie wystarczy wykaz najwa�niejszych:

\a sygna� dzwonka,\b kasowanie poprzedniego znaku (ang. backspace),\n nowy wiersz,\t tabulator poziomy.

Takie symbole w ci�gu formatuj�cym reprezentuj� czynno�ci do wykonania w cza-sie wypisywania ci�gu na wyj�ciu programu. Otó� wypisanie \a spowoduje nawi�kszo�ci maszyn wygenerowanie dwi�ku brz�czyka terminala; wypisanie \b

znaki steruj�ce � 7.3

74 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

cofnie kursor o jedn� pozycj�; wypisanie \n przesunie kursor do nowego wiersza;wypisanie \t przesunie kursor do nast�pnej pozycji tabulatora.

Ci�g formatuj�cy mo�e zawiera� dowoln� liczb� znaków steruj�cych. Spójrzmyna kolejny przyk�ad wywo�ania printf z ci�giem formatuj�cym zawieraj�cym��cznie sze�� znaków steruj�cych:

printf("Towar\tCena\tData\n\tjed.\tzakupu");

Wykonanie tej instrukcji spowoduje wypisanie na wyj�ciu dwóch wierszy:

Towar Cena Data jed. zakupu

Innym popularnym znakiem steruj�cym jest \", który reprezentuje w ci�guznak podwójnego cudzys�owu ". Poniewa� sam znak " oznacza pocz�tek albokoniec litera�u napisowego, nie mo�e w takiej postaci pojawi� si� wewn�trz lite-ra�u — musi zosta� oznaczony jako znak steruj�cy znakiem uko�nika. Oto przyk�ad:

printf("\"Ahoj!\"");

Taka instrukcja spowoduje wypisanie na wyj�ciu komunikatu:

"Ahoj!"

Podobna sytuacja dotyczy znaku lewego uko�nika. Je�li zechcemy umie�ci�taki znak w wypisywanym ci�gu, nie mo�emy go wstawi� wprost do litera�u napi-sowego, bo kompilator zak�ada, �e znak uko�nika jest zapowiedzi� znaku steru-j�cego. Aby wypisa� znak \, trzeba go równie� poprzedzi� znakiem \, a wi�cwstawi� do ci�gu par� \\:

printf("\\"); /* wypisuje na wyj�ciu pojedynczy znak \ */

3.2. Funkcja scanfTak jak funkcja printf s�u�y do formatowania wyj�cia programu, tak funkcjascanf obs�uguje formatowane wej�cie. Ci�g formatuj�cy dla funkcji scanf rów-nie� mo�e zawiera� zwyczajne znaki i specyfikatory konwersji. Konwersje obs�u-giwane przez funkcj� scanf pokrywaj� si� z grubsza z konwersjami funkcjiprintf.

W wielu przypadkach ci�g formatuj�cy funkcji scanf zawiera wy��czniespecyfikatory konwersji, jak w poni�szym przyk�adzie:

int i, j;float x, y;

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

Za�ó�my, �e u�ytkownik wprowadza na wej�cie programu nast�puj�cy wiersz:

1 -20 .3 -4.0e3

3.2. Funkcja scanf 75

Funkcja scanf wczyta taki wiersz i rozpocznie stosowanie specyfikatorów kon-wersji i podstawianie uzyskanych warto�ci pod zmienne przekazane w wywo�aniu:1 pod i, -20 pod j, 0.3 pod x i -4000.0 pod y. Takie „upakowane” ci�gisteruj�ce s� dla funkcji scanf typowe. W przypadku funkcji printf cz��ciejstosuje si� rozmaite „dekoracje” i napisy obja�niaj�ce, otaczaj�ce wyprowadzanewarto�ci.

Funkcja scanf (tak jak printf zreszt�) zastawia na nie�wiadomych i nie-ostro�nych u�ytkowników kilka wnyków. Programista stosuj�cy funkcj� scanfmusi starannie sprawdza�, czy liczba specyfikatorów konwersji odpowiada liczbiezmiennych przekazanych w wywo�aniu i czy poszczególne konwersje odpowiadaj�typom przekazanych zmiennych — podobnie jak w przypadku printf, kom-pilator nie ma obowi�zku przeprowadzania takiej kontroli i wykrywania ewentu-alnych niezgodno�ci. Kolejna pu�apka czai si� w znaku &, który zazwyczaj poprze-dza ka�d� zmienn� przekazywan� do scanf. Znak & jest zazwyczaj (cho� niezawsze) konieczny i to programista ma obowi�zek pami�ta� o jego stosowaniu.

Pomini�cie symbolu & przy zmiennej przekazywanej do funkcji scanf prowadzido nieprzewidywalnych wyników dzia�ania programu. Potencjalnie s� to efektykatastrofalne. Najcz��ciej taki b��d prowadzi do wy�o�enia si� programu. W naj-lepszym przypadku podstawienie warto�ci pod zmienn� b�dzie nieskuteczne —zmienna zachowa swoj� poprzedni� warto�� (co nie oznacza, �e b�dzie mia�a jak�-kolwiek okre�lon� warto��, je�li np. nie zosta�a zainicjalizowana!). Pomini�cieznaku & jest niestety cz�stym b��dem. Niektóre kompilatory wykrywaj� taki b��di generuj� komunikat z ostrze�eniem w rodzaju „argument nie jest wskanikiem”(o wskanikach powiemy sobie w rozdziale 11.; to w�a�nie znak & tworzy wska-nik do zmiennej). Ostrze�enia zwi�zane z wywo�aniami funkcji scanf trzeba trak-towa� bardzo powa�nie.

Wywo�ania funkcji scanf s� efektywnym, ale potencjalnie niebezpiecznymsposobem wczytywania danych do programów. Wielu zawodowych programi-stów C unika funkcji scanf. Wol� oni wczyta� do programu ca�o�� danychwej�ciowych w postaci znakowej i dopiero póniej zamieni� j� na oczekiwanewarto�ci liczbowe. My natomiast b�dziemy korzysta� ze scanf ca�kiem sporo,zw�aszcza w pocz�tkowych rozdzia�ach ksi��ki — jest to zwyczajnie najprostszysposób wczytywania warto�ci liczbowych do programu. Trzeba tylko mie� �wia-domo��, �e wiele programów zachowa si� niepoprawnie, kiedy u�ytkownik wpro-wadzi do nich nieodpowiednie dane wej�ciowe. Wkrótce si� przekonamy, �e mo�nazreszt� skutecznie sprawdza�, czy funkcji scanf uda�o si� wczyta� z wej�ciaoczekiwane dane (a je�li nie, odpowiednio zareagowa�, zamiast brn�� dalej w pro-gram z niepoprawnymi danymi). Takie sprawdziany s� jednak ma�o zasadnew zakresie pocz�tkowych przyk�adów prezentowanych w ksi��ce — nadmiernierozbudowa�yby program, który ma przecie� przede wszystkim ilustrowa� bie��cezagadnienie.

wykrywanie b��dóww scanf � 22.3

76 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

Dzia�anie funkcji scanfFunkcja scanf robi w istocie znacznie wi�cej, ni� dotychczas powiedziano. Jest tow zasadzie mechanizm dopasowywania wzorców w ci�gach znaków, który pró-buje pogrupowa� znaki wej�ciowe i dopasowa� je do specyfikatorów konwersji.

Dzia�aniem funkcji scanf sterujemy za pomoc� ci�gu formatuj�cego. Funkcjascanf analizuje zawarto�� tego ci�gu, od lewej do prawej strony. Dla ka�degonapotkanego w ci�gu specyfikatora konwersji próbuje w ci�gu wej�ciowym pro-gramu zlokalizowa� warto�� odpowiedniego typu. W czasie tego wyszukiwaniaautomatycznie pomija znaki odst�pów. Znaleziony podci�g pasuj�cy do specyfi-katora konwersji jest wczytywany a� do miejsca, w którym wyst�pi znak niepasu-j�cy do wymaga� konwersji. Je�li uda�o si� wczyta� taki podci�g, funkcja scanfdokonuje konwersji i przechodzi do analizy reszty ci�gu formatuj�cego. Pierwszenieudane dopasowanie specyfikatora konwersji do danych wczytywanych z wej-�cia ko�czy dzia�anie ca�ej funkcji, bez rozpatrywania reszty ci�gu formatuj�cego(i bez uwzgl�dniania reszty danych wej�ciowych).

W toku poszukiwania pierwszego znaku liczby funkcja scanf ignorujewszystkie znaki odst�pów (spacje, znaki tabulacji poziomej i pionowej, znakiwysuwu formularza i znaki nowego wiersza). Dzi�ki temu na wej�ciu dane mo�napodawa� w jednym wierszu albo rozproszy� je pomi�dzy ró�nymi wierszami. Przydanym wywo�aniu funkcji scanf:

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

u�ytkownik mo�e wprowadzi� na wej�cie np. trzy wiersze danych:

1-20 .3 -4.0e3

dla funkcji scanf wej�cie jest widoczne jako ci�g�y strumie� znaków:

··1¤-20···.3¤···-4.0e3¤

(w celu uwidocznienia znaków odst�pów zastosowali�my znak · dla spacji i znak¤ dla nowego wiersza). Poniewa� funkcja scanf pomija znaki odst�pów i szukaprzede wszystkim pocz�tku liczby i podci�gu znaków nadaj�cych si� do konwersji,takie dane wej�ciowe mog� by� za jednym zamachem wczytane przez funkcj�scanf. Poni�szy schemat ilustruje dzia�anie funkcji scanf. Znak p oznacza tupomijanie bie��cego znaku wej�cia, znak w oznacza wczytywanie znaków do bie-��cej konwersji:

··1¤-20···.3¤···-4.0e3¤ppwpwwwpppwwppppwwwwww

Ostatni znak strumienia wej�ciowego, czyli pierwszy znak za ostatni� skonwerto-wan� grup� znaków, jest przez funkcj� scanf „podgl�dany”, ale nie jest wczy-tywany. Zostanie on wczytany i pomini�ty b�d skonwertowany przy nast�pnymwywo�aniu scanf.

Wed�ug jakich regu� scanf rozpoznaje liczb� ca�kowit� albo zmiennoprze-cinkow� w ci�gu danych wej�ciowych? Otó� kiedy scanf ma wczyta� liczb�ca�kowit�, szuka w ci�gu wej�ciowym znaku cyfry, ewentualnie znaku + albo -.

3.2. Funkcja scanf 77

Po znalezieniu takiego znaku wczytuje wszystkie kolejne znaki a� do napotkaniaznaku nieb�d�cego cyfr�. Natomiast w przypadku liczby zmiennoprzecinkowejscanf szuka:

znaku + albo - (opcjonalnie), a za nimci�gu cyfr (zawieraj�cego ewentualnie znak przecinka dziesi�tnego), a za nimci�gu wyk�adnika (opcjonalnie); ci�g wyk�adnika sk�ada si� z litery e (albo E),

opcjonalnego znaku (+ albo -) i co najmniej jednej cyfry.

W przypadku funkcji scanf konwersje %e, %f i %g mo�na stosowa� zamiennie —wszystkie trzy reprezentuj� te same regu�y dopasowania warto�ci zmiennoprze-cinkowej.

Kiedy scanf napotyka znak niezgodny z regu�� dopasowania dla bie��cej kon-wersji, znak ten jest „odk�adany” z powrotem do strumienia wej�ciowego, aby by�dost�pny przy obs�udze nast�pnych specyfikatorów konwersji albo dla kolejnychwywo�a� scanf wczytuj�cych kolejne warto�ci. Wemy na przyk�ad nast�puj�ce(niew�tpliwie patologiczne) rozmieszczenie naszych czterech liczb wej�ciowych:

1-20.3-4.0e3¤

Zastosujemy takie same wywo�anie scanf jak poprzednio:

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

Funkcja scanf b�dzie przetwarza� taki ci�g wej�ciowy nast�puj�co:

� Bie��ca konwersja: %d. Pierwszy niepusty znak wej�cia to 1. Poniewa� liczbaca�kowita mo�e zaczyna� si� od cyfry, znak jest akceptowany jako pierwszyznak podci�gu do dopasowania. Funkcja wczytuje nast�pny znak: -. Taki znaknie mo�e si� pojawi� wewn�trz zapisu warto�ci ca�kowitej, wi�c scanfodk�ada znak z powrotem do strumienia i podstawia pod pierwsz� zmienn�(i) warto�� 1.

� Bie��ca konwersja: %d. Pierwszy niepusty znak wej�cia to - (dozwolony);kolejne znaki to 2, 0 i . (kropka). Liczba ca�kowita nie mo�e zawiera� kropki,wi�c znak jest odk�adany z powrotem do strumienia wej�ciowego, a funkcjapodstawia pod kolejn� zmienn� (j) warto�� –20.

� Bie��ca konwersja: %f. Pierwszy niepusty znak wej�cia to . (dozwolony);kolejne znaki to 3 i - (minus). Liczba zmiennoprzecinkowa nie zawiera znaku- po znaku cyfry, wi�c znak - jest odk�adany z powrotem do strumienia wej-�ciowego, a funkcja podstawia pod kolejn� zmienn� (x) warto�� 0.3.

� Bie��ca konwersja: %f. Pierwszy niepusty znak wej�cia to - (dozwolony);kolejne znaki to 4, ., 0, e, 3 i ¤ (nowy wiersz). Liczba zmiennoprzecinkowanie mo�e zawiera� znaku nowego wiersza, wi�c znak jest odk�adany z powro-tem do strumienia wej�ciowego, a funkcja podstawia pod kolejn� zmienn�(y) warto�� –4.0×103.

W tym przyk�adzie funkcja scanf mog�a skutecznie dopasowa� wszystkie kolejnespecyfikatory konwersji do ci�gu wej�ciowego. Poniewa� znak nowego wierszanie zosta� „zu�yty”, b�dzie pierwszym znakiem wczytywanym przy nast�pnymwywo�aniu scanf.

78 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

Zwyk�e znaki w ci�gu formatuj�cym funkcji scanfZasad� dopasowywania wzorców do podci�gów strumienia wej�ciowego mo�narozszerzy�, zapisuj�c ci�g formatuj�cy zawieraj�cy poza specyfikatorami konwer-sji równie� zwyk�e napisy. W takim przypadku pomi�dzy specyfikatorami kon-wersji funkcja scanf b�dzie porównywa�a kolejne znaki wej�cia ze znakamici�gu formatuj�cego. Porównanie takie odbywa si� ró�nie, zale�nie od tego, czyci�g formatuj�cy zawiera znaki odst�pów:

� Znaki odst�pów w ci�gu formatuj�cym. Kiedy w ci�gu formatuj�cym znaj-duj� si� znaki odst�pu, funkcja scanf b�dzie w strumieniu wej�ciowym wczy-tywa�a kolejne znaki tak d�ugo, a� napotka pierwszy znak nieb�d�cy znakiemodst�pu (ten znak zostanie „od�o�ony” z powrotem do strumienia wej�ciowego).Liczba znaków odst�pu w ci�gu formatuj�cym nie musi dok�adnie odpowiada�liczbie takich znaków w ci�gu wej�ciowym. Pojedynczy znak odst�pu w ci�guformatuj�cym zostanie dopasowany do dowolnie d�ugiego podci�gu takichznaków w ci�gu wej�ciowym (co wi�cej, obecno�� znaku odst�pu w ci�guformatuj�cym nie wymusza obecno�ci takiego znaku w ci�gu wej�ciowym —znak odst�pu w ci�gu formatuj�cym odpowiada dowolnie d�ugiemu podci�gowitakich znaków w ci�gu wej�ciowym, a wi�c równie� podci�gowi zerowemu).

� Pozosta�e znaki. Kiedy w ci�gu formatuj�cym znajduje si� znak nienale��cydo specyfikatorów konwersji i nieb�d�cy znakiem odst�pu, funkcja scanfporównuje go wprost z nast�pnym znakiem wej�cia. Je�li znaki s� zgodne,funkcja przechodzi do przetwarzania nast�pnego znaku ci�gu wej�ciowego.Przy braku zgodno�ci funkcja odk�ada niepasuj�cy znak z powrotem do ci�guwej�ciowego i przerywa dalsze przetwarzanie ci�gu formatuj�cego.

Dla przyk�adu niech funkcja scanf otrzyma ci�g formatuj�cy w postaci"%d/%d". Je�li na wej�ciu programu pojawi si� ci�g:

·5/·96

funkcja scanf pominie pierwszy znak odst�pu, szukaj�c liczby ca�kowitej, nast�p-nie dopasuje do %d podci�g 5, dopasuje bezpo�rednio znak /, pominie spacj�w poszukiwaniu kolejnej liczby ca�kowitej, a póniej dopasuje do %d podci�g 96.Ale je�li na wej�ciu pojawi si�:

·5·/·96

to funkcja scanf pominie pierwszy znak odst�pu, szukaj�c liczby ca�kowitej,nast�pnie dopasuje do %d podci�g 5, a póniej spróbuje dopasowa� do wej�ciaznak /. Poniewa� w bie��cym miejscu wej�cia zamiast tego znaku znajduje si�inny (tu: znak odst�pu), funkcja przerwie przetwarzanie wej�cia, odk�adaj�c spacj�z powrotem do ci�gu wej�ciowego. Na wej�ciu pozostanie ci�g ·/·96 czekaj�cyna ewentualne kolejne wywo�anie scanf. Aby umo�liwi� dopasowanie wej�ciaze spacj� (spacjami) po pierwszej liczbie ca�kowitej, ci�g formatuj�cy powinienmie� posta� "%d /%d".

3.2. Funkcja scanf 79

Skutki mylenia printf ze scanfWywo�ania funkcji printf i scanf bywaj� bardzo podobne, ale w ich dzia�a-niu zachodz� daleko id�ce ró�nice. Zignorowanie tych ró�nic mo�e by� bardzo nie-bezpieczne dla dzia�ania programu.

Jedn� z cz�stych pomy�ek jest umieszczenie & przed zmienn� w wywo�aniufunkcji printf:

printf("%d %d\n", &i, &j); /*** �LE ***/

Na szcz��cie taka omy�ka jest stosunkowo prosta do wytropienia — w tokudzia�ania programu funkcja printf wy�wietli „�mieci” zamiast oczekiwanychwarto�ci i i j.

Poniewa� funkcja scanf normalnie pomija znaki odst�pów przy poszuki-waniu podci�gu do dopasowania do warto�ci liczbowej, rzadko kiedy pojawia si�potrzeba umieszczania w ci�gu formatuj�cym scanf czegokolwiek poza specy-fikatorami konwersji. Nieuprawnione za�o�enie, �e ci�g formatuj�cy scanfpowinien �ci�le odpowiada� analogicznemu ci�gowi formatuj�cemu printf —to kolejny cz�sty b��d — mo�e doprowadzi� do niepoprawnego dzia�ania funkcjiscanf. Zobaczmy, co si� stanie, je�li w programie znajdzie si� nast�puj�ca in-strukcja:

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

Funkcja scanf b�dzie najpierw szuka� podci�gu warto�ci typu int i wpisze t�warto�� pod zmienn� i. Potem b�dzie próbowa�a dopasowa� w ci�gu wej�ciowymznak przecinka. Je�li w ci�gu wej�ciowym zamiast przecinka pojawi si� spacja,dzia�anie funkcji zostanie przerwane i zmienna j nie otrzyma oczekiwanej warto�ci.

Ci�gi formatuj�ce dla funkcji printf cz�sto ko�cz� si� znakiem steruj�cym \n,wymuszaj�cym wstawienie do wyj�cia nowego wiersza. Taki sam znak na ko�cuci�gu formatuj�cego scanf to zazwyczaj z�y pomys�. Dla scanf znak nowegowiersza w ci�gu formatuj�cym jest równowa�ny ze znakiem spacji; pomija go,szukaj�c nast�pnego znaku nieb�d�cego znakiem odst�pu. Je�li na przyk�ad ci�gformatuj�cy ma posta� "%d\n", scanf pominie ewentualne pocz�tkowe znakiodst�pu, wczyta warto�� ca�kowit�, a nast�pnie b�dzie w ci�gu wej�ciowym szuka�nast�pnego znaku innego ni� odst�p. W przypadku programu interaktywnego mo�eto doprowadzi� do „zawieszenia” programu do czasu, kiedy u�ytkownik wprowa-dzi na wej�cie znak inny ni� odst�p.

Dodawanie u�amkówAby zilustrowa� zdolno�� funkcji scanf do dopasowywania wzorców w ci�gachznaków, wemiemy na warsztat problem wczytania u�amka wprowadzanego przezu�ytkownika. U�amki s� zwyczajowo reprezentowane przez zapis licznik/mianownik.Zamiast zmusza� u�ytkownika do nieintuicyjnego wprowadzania u�amka jakodwóch osobnych liczb ca�kowitych, dzi�ki funkcji scanf pozwolimy mu wpro-wadzi� u�amek w klasycznej postaci. Oto program ilustruj�cy t� technik� przydodawaniu dwóch u�amków:

PROGRAM

80 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

/* Dodawanie dwóch u�amków zwyk�ych */

#include <stdio.h>

int main(void){ int num1, denom1, num2, denom2, result_num, result_denom;

printf("Podaj pierwszy u�amek: "); scanf("%d/%d", &num1, &denom1);

printf("Podaj drugi u�amek: "); scanf("%d/%d", &num2, &denom2);

result_num = num1 * denom2 + num2 * denom1; result_denom = denom1 * denom2; printf("Suma u�amków wynosi %d/%d\n", result_num, result_denom);

return 0;}

Przyk�adowa sesja z takim programem wygl�da�a tak:

Podaj pierwszy u�amek: 5/6Podaj drugi u�amek: 3/4Suma u�amków wynosi 38/24

Zauwa�my, �e program nie przewiduje znormalizowania wynikowego u�amka.

Pytania i odpowiedzi*P: Widywa�em specyfikatory w postaci %i, przeznaczone do wczytywania i wy-

pisywania liczb ca�kowitych. Czym ró�ni� si� od %d (s. 71)?O: W ci�gu formatuj�cym funkcji printf nie ma pomi�dzy nimi �adnej ró�nicy.

Za to w funkcji scanf %d dopasuje wy��cznie liczb� ca�kowit� zapisan� w postacidziesi�tnej, natomiast %i mo�e dopasowa� tak�e liczby ca�kowite zapisanew innych systemach liczbowych (przy innych podstawach), na przyk�ad liczbyósemkowe czy szesnastkowe. Otó� je�li ci�g wej�ciowy zawiera przedrostek0 (jak w 056), przy konwersji %i taki ci�g zostanie potraktowany jako ósemkowy.Przedrostek 0x albo 0X (jak w 0x56) przy konwersji %i spowoduje konwersj�liczby jako warto�ci szesnastkowej. Zastosowanie %i zamiast %d do wczytywanialiczb mo�e da� zaskakuj�ce wyniki, je�li u�ytkownik omy�kowo wprowadzi 0 przedw�a�ciw� liczb�; z tego powodu zaleca si� trzymanie si� konwersji %d.

P: Skoro printf traktuje znak % jako pocz�tek specyfikatora konwersji, jakmo�na wypisa na wyj�ciu programu znak procenta?

O: Funkcja printf wypisze na wyj�ciu znak % tam, gdzie w ci�gu formatuj�cympojawi si� para przylegaj�cych znaków procenta (%%). Na przyk�ad instrukcja:

addfrac.c

liczby ósemkowe � 7.1

liczby szesnastkowe � 7.1

Pytania i odpowiedzi 81

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

mo�e spowodowa� wypisanie:

Zysk netto: 10%

P: Znak steruj�cy \t ma wymusi na printf przesuni�cie kursora do nast�p-nej pozycji tabulacji. Sk�d wiadomo, jaka jest szeroko� tabulatora (s. 74)?

O: Nie wiadomo. Efekt wypisania na wyj�ciu znaku \t nie jest �ci�le zdefiniowanyw standardzie j�zyka C. Jest on zale�ny od reakcji systemu operacyjnego na ��da-nie wypisania znaku tabulacji. Zazwyczaj szeroko�� tabulatora to osiem znaków,ale C sam w sobie nie daje takiej gwarancji.

P: Co zrobi funkcja scanf, je�li b�dzie mia�a wczyta liczb�, a u�ytkownikwprowadzi ci�g nieliczbowy?

O: Wyja�nimy to na przyk�adzie:

printf("Podaj liczb�: ");scanf("%d", &i);

Za�ó�my, �e u�ytkownik wprowadzi poprawn� warto�� liczbow�, za któr� umie�ciznaki inne ni� cyfry:

Podaj liczb�: 23bla

W takim przypadku funkcja scanf wczyta znaki 2 oraz 3 i w zmiennej i umie�ciwarto�� 23. Reszta znaków (bla) b�dzie w strumieniu wej�ciowym czeka�a nakolejne wywo�anie scanf (albo innej funkcji obs�uguj�cej wej�cie). Je�li natomiastu�ytkownik wprowadzi ci�g niepoprawny od pierwszego znaku:

Podaj liczb�: bla

zmienna i nie otrzyma warto�ci wczytywanej z wej�cia programu, a w strumieniuwej�ciowym ci�g bla poczeka na kolejne wywo�ania funkcji wej�cia.

Co mo�na zrobi� w takich smutnych przypadkach? Póniej dowiesz si�, jaksprawdzi� skuteczno�� wywo�ania funkcji scanf. Je�li wywo�anie b�dzie nie-skuteczne, mo�emy zako�czy� program albo spróbowa� naprawi� sytuacj�, na przy-k�ad odrzucaj�c niepoprawne wej�cie i ponownie prosz�c u�ytkownika o wpro-wadzenie danych (sposoby odrzucania danych wej�ciowych b�d� omawianew sekcji pyta� i odpowiedzi rozdzia�u 22.).

P: Nie rozumiem, w jaki sposób funkcja scanf „odk�ada” znak z powrotemdo ci�gu wej�ciowego i potem ponownie wczytuje go z wej�cia (s. 77)?

O: Okazuje si�, �e program nie wczytuje danych wej�ciowych bezpo�rednio przy ichwprowadzaniu. Ci�g wej�ciowy programu jest przechowywany w ukrytym buforze,do którego odwo�uje si� funkcja scanf. W takim uk�adzie funkcja scanf mo�e„od�o�y�” znak z powrotem do bufora. Buforowanie wej�cia b�dzie omawianew rozdziale 22.

P: Jak zadzia�a funkcja scanf, kiedy u�ytkownik umie�ci pomi�dzy liczbamiznaki przestankowe (np. przecinki)?

O: Zobaczmy to na prostym przyk�adzie. Za�ó�my, �e zamierzamy wczyta� funkcj�scanf par� liczb:

wykrywanie b��dóww scanf � 22.3

82 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

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

Je�li u�ytkownik wprowadzi:

4,28

funkcja scanf wczyta znak 4 i podstawi warto�� 4 do zmiennej i. W poszuki-waniu nast�pnego podci�gu reprezentuj�cego liczb� ca�kowit� znajdzie przecinek.Poniewa� przecinek nie mo�e rozpoczyna� zapisu liczby, funkcja przerwie dzia-�anie. Sam przecinek i druga wprowadzona liczba zostan� do dyspozycji nast�p-nych wywo�a� scanf.

Oczywi�cie mo�na temu �atwo zaradzi�, instruuj�c u�ytkownika co do ocze-kiwanego formatu danych wej�ciowych, np. nakazuj�c mu zawsze oddzielanie liczbprzecinkiem:

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

�wiczenia 1. Co pojawi si� na wyj�ciu po wykonaniu poni�szych wywo�a� 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 wywo�anie funkcji printf wy�wietlaj�cej warto�� zmiennej typu float w nast�-puj�cych formatach:

(a) w zapisie wyk�adnikowym, wyrównan� do lewej w polu o szeroko�ci 8, z jedn� cyfr�po przecinku;

(b) w zapisie wyk�adnikowym, wyrównan� do prawej w polu o szeroko�ci 10, z sze�ciomacyframi po przecinku;

(c) w zapisie dziesi�tnym z przecinkiem, wyrównan� do lewej w polu o szeroko�ci 8, z trzemacyframi po przecinku;

(d) w zapisie dziesi�tnym z przecinkiem, wyrównan� do prawej w polu o szeroko�ci 6,bez cyfr po przecinku.

3. Dla ka�dej z poni�szych par ci�gów formatuj�cych scanf wska�, czy oba ci�gi pary s� sobierównowa�ne, czy nie. Je�li nie s�, wska� ró�nic� w ich dzia�aniu.

(a) "%d" " %d" (b) "%d-%d-%d" " %d -%d -%d" (c) "%f" "%f " (d) "%f,%f" "%f, %f"

4. Wemy nast�puj�ce wywo�anie funkcji scanf:�

� wiczenia z gwiazdk� s� podchwytliwe — poprawna odpowied zazwyczaj jest ró�na

od odpowiedzi narzucaj�cej si� na pierwszy rzut oka. Przeczytaj pytanie uwa�nie, dok�adnieprze�led kod, w razie potrzeby powtórz lektur� odpowiedniego podrozdzia�u. Powodze-nia! — przyp. autora.

Podrozdzia� 3.1

Podrozdzia� 3.2

*

Zadania programistyczne 83

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

Jakie b�d� warto�ci zmiennych i, x i j po wykonaniu wywo�ania (zak�adamy, �e zmiennei i j s� zmiennymi typu int, a x jest zmienn� typu float), je�li u�ytkownik wprowadzi:

10.3 5 6

5. Wemy nast�puj�ce wywo�anie funkcji scanf:

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

Jakie b�d� warto�ci zmiennych x, i i y po wykonaniu wywo�ania (zak�adamy, �e zmiennex i y s� zmiennymi typu float, a i jest zmienn� typu int), je�li u�ytkownik wprowadzi:

12.3 45.6 789

6. Poka�, jak mo�na przerobi� program addfrac.c z podrozdzia�u 3.2, aby u�ytkownik móg�wprowadza� u�amki ze spacjami wokó� znaku dzielenia /.

Zadania programistyczne 1. Napisz program, który przyjmuje na wej�cie dat� w postaci dd/mm/rrrr, a nast�pnie

wypisuje j� w formacie rrrrmmdd:

Podaj dat� (dd/mm/rrrr): 17/2/2011Poda�e� dat� 20110217

2. Napisz program formatuj�cy informacje o produkcie wprowadzone przez u�ytkownika. Sesjaz programem powinna wygl�da� tak:

Podaj numer towaru: 583Podaj cen� jednostkow�: 13.5Podaj dat� zakupu (dd/mm/rrrr): 24/10/2010Towar Cena Data jed. zakupu583 $ 13.50 24/10/2010

Numer towaru i data powinny by� wyrównane do lewej. Cena jednostkowa powinna by�wyrównana do prawej. Program powinien dopuszcza� kwoty do 9999,99. Podpowied�: Dowyrównania kolumn zastosuj tabulatory.

3. Ksi��ki s� oznaczane mi�dzynarodowym numerem ISBN (International Standard BookNumber). Numery ISBN nadawane po 1 stycznia 2007 roku sk�adaj� si� z 13 cyfr, podzie-lonych na pi�� grup, np. 978-0-393-97950-3 (starsze numery ISBN mia�y 10 cyfr). Pierwszagrupa (przedrostek GSI) to obecnie albo 978 albo 979. Identyfikator grupy okre�la j�zykkraju wydania (np. w krajach angloj�zycznych stosuje si� kody 0 i 1). Kod wydawcyidentyfikuje wydawc� (393 to kod wydawnictwa W.W. Norton). Numer publikacji to numernadawany przez wydawc� konkretnej ksi��ce (tutaj 97950). Numer ISBN ko�czy si� sum�kontroln� umo�liwiaj�c� weryfikacj� poprawno�ci numeru ISBN. Napisz program, którypodzieli na grupy numer ISBN wprowadzony przez u�ytkownika:

Podaj numer ISBN: 978-0-393-97950-3Przedrostek GSI: 978Identyfikator grupy: 0

*

84 Rozdzia� 3. Formatowanie wej�cia-wyj�cia

Kod wydawcy: 393Numer publikacji: 97950Suma kontrolna: 3

Uwaga: Liczba cyfr w ka�dej z grup mo�e by� ró�na. Nie mo�na za�o�y�, �e grupy maj�akurat takie rozmiary jak w tym przyk�adzie. Przetestuj program na innych, prawdziwychnumerach ISBN (znajdziesz je na ok�adkach ksi��ek i na stronach z informacjami o wydaniu).

4. Napisz program, który zapyta u�ytkownika o numer telefoniczny w formacie (xx)xxx-xxxx, a potem wy�wietli ten numer w zapisie 0-xx xxx-xx-xx:

Podaj numer telefonu [(xx) xxx-xxxx]: (61) 817-6900Podany numer: 0-61 817-69-00

5. Napisz program, który b�dzie monitowa�, aby u�ytkownik wprowadzi� liczby od 1 do 16(w dowolnej kolejno�ci), potem wy�wietli te liczby czwórkami (w postaci macierzy 4×4),a nast�pnie wypisze sumy w wierszach, kolumnach i po przek�tnych:

Podaj liczby od 1 do 16 (w dowolnej kolejno�ci):16 3 2 13 5 10 11 8 9 6 7 12 4 15 14 116 3 2 13 5 10 11 8 9 6 7 12 4 15 14 1

Sumy w wierszach: 34 34 34 34Sumy w kolumnach: 34 34 34 34Sumy po przek�tnych: 34 34

Je�li sumy w wierszach, kolumnach i po przek�tnych s� identyczne (jak w przyk�adzie),to takie liczby stanowi� tak zwany magiczny kwadrat. Ten magiczny kwadrat pojawi�si� w 1514 roku na rycinie artysty i matematyka Albrechta Dürera (zauwa�, �e �rodkoweliczby w ostatnim wierszu sk�adaj� si� na dat� ryciny).

6. Przerób program addfrac.c z podrozdzia�u 3.2 tak, aby u�ytkownik za jednym zamachemwprowadza� oba u�amki oddzielone znakiem +:

Podaj dwa u�amki oddzielone znakiem plusa: 5/6+3/4Suma u�amków wynosi 38/24

85

4 Wyraenia

U�ywanie kalkulatorka to jeszcze nie programowanie,a ju� nie matematyka.

Jedn� z wa�niejszych cech j�zyka C jest nacisk, jaki jest w nim k�adziony na wyra-�enia — inaczej „wzory”, które mówi� o sposobie obliczania warto�ci. Wyra�enias� tu wa�niejsze ni� instrukcje. Najprostsze wyra�enia to zmienne i sta�e programu.Zmienna reprezentuje warto��, która jest obliczana w czasie dzia�ania programupoprzez pobranie warto�ci z pami�ci skojarzonej ze zmienn�. Sta�a reprezentujewarto�� znan� ju� w czasie kompilacji. Bardziej rozbudowane wyra�enia obejmuj�operandy i operatory (przy czym operatory same w sobie s� równie� wyra�eniami).W wyra�eniu a + (b * c) widzimy zastosowanie operatora + do operandówa oraz (b + c), natomiast obie strony operatora + (oba operandy) s� pe�nopraw-nymi wyra�eniami.

Operatory s� podstawowymi narz�dziami budowania wyra�e�, a j�zyk Cposiada bardzo bogaty zbiór operatorów. Przede wszystkim C obs�uguje podsta-wowe operatory obecne w wi�kszo�ci innych j�zyków programowania:

� Operatory arytmetyczne, w tym dodawanie, odejmowanie, mno�enie i dzielenie.� Operatory relacji do obliczania warto�ci wyra�e� logicznych, takich jak „i jest

wi�ksze od 0”.� Operatory logiczne do budowania warunków logicznych, jak „i jest wi�ksze

od 0 i jest mniejsze ni� 10”.

Na tym jednak nie koniec. W j�zyku C mamy do dyspozycji dziesi�tki innychoperatorów. Jest ich tak wiele, �e b�dziemy zmuszeni do ich stopniowego wpro-wadzania na przestrzeni a� dwudziestu rozdzia�ów tej ksi��ki. Opanowanie takiejliczby operatorów wydaje si� zadaniem niepomiernym, ale w istocie wi�kszo��z nich ma zasadnicze znaczenie dla efektywno�ci programowania w j�zyku C.

W tym rozdziale zajmiemy si� jednymi z najwa�niejszych operatorów j�zyka C,mianowicie wemiemy na warsztat operatory arytmetyczne (podrozdzia� 4.1), ope-ratory przypisania (podrozdzia� 4.2) oraz operatory inkrementacji i dekrementacji

86 Rozdzia� 4. Wyra�enia

(podrozdzia� 4.3). W podrozdziale 4.1 zajmiemy si� te� pierwsze�stwem i ��czno�ci�operatorów — te cechy operatorów s� niezwykle istotne dla poprawno�ci wyra�e�obejmuj�cych wi�cej ni� jeden operator. W podrozdziale 4.4 opisany zostanie spo-sób, w jaki nast�puje obliczanie wyra�e� w j�zyku C. Wreszcie w podrozdziale 4.5zajmiemy si� tak zwan� instrukcj� wyra�eniow� — konstrukcj� sprawiaj�c�,�e dowolne wyra�enie mo�e odgrywa� rol� instrukcji w programie.

4.1. Operatory arytmetyczneOperatory arytmetyczne — czyli operatory realizuj�ce operacje dodawania,odejmowania, mno�enia i dzielenia — to istne wo�y robocze w wi�kszo�ci j�zykówprogramowania. C nie jest tu wyj�tkiem. List� operatorów arytmetycznych j�zykaC wymienia tabela 4.1.

Z jednym operandem Z dwoma operandami

Addytywne Multiplikatywne

Tabela 4.1.Operatory

arytmetyczne

+ zachowanie znaku liczby + dodawanie * mno�enie- zmiana znaku liczby - odejmowanie / dzielenie

% reszta z dzielenia

Operatory addytywne i multiplikatywne to tak zwane operatory dwuargumen-towe (ang. binary), poniewa� operuj� na dwóch operandach. Operatory jednoar-gumentowe (ang. unary) wymagaj� tylko jednego operandu:

i = +1; /* + jako operator jednoargumentowy */j = -i; /* + jako operator jednoargumentowy (negacja) */

Jednoargumentowy operator + nie ma �adnego dzia�ania. W rzeczy samej w spe-cyfikacji K&R w ogóle go nie uwzgl�dniono. S�u�y g�ównie do zaznaczenia, �esta�a liczbowa jest dodatnia.

Operatory dwuargumentowe nie powinny budzi� w�tpliwo�ci. Mo�e poza ope-ratorem %, który oblicza reszt� z dzielenia ca�kowitego. Warto�� wyra�enia i %j jest to reszta z ca�kowitego dzielenia i przez j. Na przyk�ad warto�ci� wyra�e-nia 10 % 3 jest 1, a wyra�enie 12 % 4 ma warto�� 0.

Operatory dwuargumentowe z tabeli 4.1 — z wyj�tkiem operatora % — mog�by� stosowane zarówno do operandów b�d�cych liczbami ca�kowitymi, jak i dooperandów zmiennoprzecinkowych; dopuszczalne jest te� mieszanie typów operan-dów. Kiedy w zasi�gu dzia�ania operatora wyst�puj� operandy typu int i float,wynikiem dzia�ania operatora jest warto�� typu float. Dlatego wyra�enie 9 +2.5f daje 11,5, a 6.7f / 2 to 3.35.

Przy stosowaniu operatorów / i % nale�y zachowa� pewn� ostro�no��:� Operator dzielenia / mo�e dawa� nieoczekiwane wyniki. Otó� kiedy oba ope-

randy s� warto�ciami ca�kowitymi, operator / obcina iloraz do najbli�szej mniej-szej warto�ci ca�kowitej. Dlatego wyra�enie 1 / 2 da warto�� 0.

4.1. Operatory arytmetyczne 87

� Operator % wymaga, aby operandy by�y warto�ciami ca�kowitymi. Je�li któ-rykolwiek z operandów % jest warto�ci� inn� ni� ca�kowita, program nie dasi� skompilowa�.

� Je�li prawym operandem operatora / albo % b�dzie 0, dzia�anie programub�dzie niezdefiniowane.

� Najwi�ksze pu�apki czaj� si� przy stosowaniu operatorów / i % z operandamiujemnymi. Standard C89 stwierdza, �e kiedy którykolwiek z operandów jestujemny, wynik dzielenia mo�e zosta� zaokr�glony w gór� albo w dó� (np. war-to�ci� wyra�enia -9 / 7 mo�e by� albo –1, albo –2). Równie� kiedy ope-rand i albo j wyra�enia i % j jest ujemny, wed�ug standardu C89 wynikdzia�ania operatora jest zale�ny od implementacji (np. wyra�enie -9 % 7mo�e da� warto�� –2 albo 5). Z kolei w C99 wynik dzielenia ca�kowitego jestzawsze zaokr�glany w kierunku zera (-9 / 7 da wynik –1), a warto�� wyra-�enia i % j b�dzie mia�a znak taki jak operand i (czyli -9 % 7 to –2).

Zachowanie zale�ne od implementacjiPoj�cie zachowania albo dzia�ania zalenego od implementacji b�dzie si� poja-wia w naszym omówieniu na tyle cz�sto, �e warto przedstawi je zawczasu.W standardzie j�zyka C celowo pozostawiono specyfikacj� dzia�ania niektórychelementów j�zyka jako nie do ko�ca sprecyzowan�, z za�o�eniem, �e „implemen-tacja” — czyli konkretne wcielenie kompilatora i programu konsoliduj�cego dlakonkretnej platformy maszynowej i systemowej — wype�ni luk� po swojemu.W efekcie zachowanie programu w toku wykonania mo�e by ró�ne na ró�nychplatformach. Przyk�adem zachowania zale�nego od implementacji jest w�a�niedzia�anie operatorów / i % wed�ug standardu C89.

Niedoprecyzowanie niektórych elementów j�zyka wydaje si� dziwactwem,a nawet dzia�aniem szkodliwym, ale w istocie dobrze odzwierciedla filozofi� przy-�wiecaj�c� twórcom j�zyka C. Jednym z za�o�e� projektowych by�a przecie� mo�-liwie du�a wydajno� programów pisanych w tym j�zyku, co cz�sto oznaczakonieczno� dostosowania implementacji poszczególnych konstrukcji do zachowa-nia danej maszyny. Tak wi�c niektóre procesory, wykonuj�c operacj� dzieleniaca�kowitego –9 przez 7, daj� wynik –1, a inne –2. Standard C89 zwyczajnie odzwier-ciedla� ró�norodno� maszynowej implementacji podstawowych operacji arytme-tycznych.

Przy pisaniu programów najlepiej unika polegania na zachowaniach zale�nychod implementacji. Je�li nie da si� takiej zale�no�ci unikn�, warto przynajmniej„r�cznie” sprawdzi wyniki w�tpliwych operacji — standard C wymaga na szcz��cie,aby sposób realizacji zachowa� zale�nych od implementacji by� udokumentowany.

Pierwsze�stwo i ��czno� operatorówKiedy wyra�enie zawiera wi�cej ni� jeden operator, interpretacja wyra�enia mo�eby� w�tpliwa. Czy na przyk�ad i + j * k oznacza „dodanie i do j i pomno-�enie sumy przez k”, czy mo�e „pomno�enie j przez k i dodanie do iloczynu i”?W�tpliwo�� mo�na usun��, stosuj�c nawiasy, a wi�c zapisuj�c jawnie albo (i + j)* k, albo i + (j * k). J�zyk C co do zasady pozwala na grupowanie podwy-ra�e� w wyra�eniach w�a�nie za pomoc� nawiasów.

niezdefiniowanezachowanie programu � 4.4

88 Rozdzia� 4. Wyra�enia

No dobrze, ale jak zinterpretowa� wyra�enie bez nawiasów? Czy dla kompi-latora i + j * k to jest (i + j) * k, czy mo�e jednak i + (j *k)? Takjak w wielu innych j�zykach, w C potencjalne w�tpliwo�ci tego rodzaju rozstrzygasi� na bazie regu� pierwsze�stwa (albo inaczej priorytetów) operatorów (ang.precedence). Dla operatorów arytmetycznych pierwsze�stwo przedstawia si� tak(od najwy�szego priorytetu):

+ - (jednoargumentowe)* / %+ - (dwuargumentowe)

Operatory wymienione w tym samym wierszu (jak jednoargumentowe + i -)cechuj� si� identycznym pierwsze�stwem.

Kiedy w jednym wyra�eniu wyst�puje wiele operatorów, mo�emy okre�li�sposób ich interpretowania (kolejno�� obliczania podwyra�e�) przez kompilator,posi�kuj�c si� nawiasami grupuj�cymi podwyra�enia, od operatorów o najwy�-szym priorytecie po operatory o priorytecie najni�szym. Oto przyk�ady:

i + j * k interpretuje si� jako i + (j * k)-i * -j interpretuje si� jako (-i) * (-j)+i + j / k interpretuje si� jako (+i) + (j / k)

Regu�y pierwsze�stwa operatorów nie s� jednak wystarczaj�ce do rozstrzy-gni�cia w�tpliwo�ci co do kolejno�ci obliczania podwyra�e�, kiedy w wyra�eniuwyst�puje wiele operatorów o tym samym priorytecie. W takiej sytuacji zastoso-wanie ma regu�a ��czno�ci operatorów (ang. associativity). O operatorze mówimy,�e jest lewostronnie ��czny, kiedy grupuje operandy od lewej do prawej. Dwuar-gumentowe operatory arytmetyczne (*, /, %, + i -) s� ��czne lewostronnie, wi�c:

i - j - k interpretuje si� jako (i - j) - ki * j / k interpretuje si� jako (i * j) / k

Operator jest prawostronnie ��czny, kiedy grupuje operandy od prawej strony dolewej. Prawostronnie ��cznymi operatorami s� jednoargumentowe operatory aryt-metyczne (+ i -), wi�c:

- + i interpretuje si� jako -(+i)

Regu�y pierwsze�stwa i ��czno�ci operatorów s� bardzo wa�ne tak�e w innychj�zykach programowania, ale w C ich znaczenie jest szczególne. Z drugiej stronyprzy liczbie operatorów dost�pnych w j�zyku C (jest ich niemal pi��dziesi�t!) ma�októry programista jest w stanie zapami�ta� ��czno�� i pierwsze�stwo wszystkichoperatorów. Pisz�c program w j�zyku C, warto wi�c mie� pod r�k� tabelk� opera-torów i zagl�da� do niej w razie w�tpliwo�ci. Mo�na te� je eliminowa� poprzezjawne grupowanie podwyra�e� w nawiasach.

Obliczanie cyfry kontrolnej kodu kreskowegoW latach siedemdziesi�tych producenci dóbr szybko zbywalnych w Stanach Zjed-noczonych i Kanadzie zacz�li znakowa� swoje towary, umieszczaj�c na nich kodykreskowe. Taki kod kreskowy albo inaczej kod UPC (universal product code)

tabela operatorów � Dodatek A

PROGRAM

4.1. Operatory arytmetyczne 89

identyfikuje zarówno producenta, jak i konkretny produkt. Ka�dy kod kreskowyreprezentuje dwunastocyfrow� liczb� (wypisan� zreszt� najcz��ciej pod kodemkreskowym). Oto przyk�adowy kod kreskowy pizzy Stouffer:

Cyfry:

0 13800 15173 5

s� jawnie wypisane pod w�a�ciwym kodem kreskowym. Pierwsza z nich okre�latyp towaru (dla wi�kszo�ci towarów jest to 0 albo 7, dla towarów wa�onych 2,dla lekarstw i produktów farmaceutycznych 3, a dla kuponów 5). Pierwsza gru-pa pi�ciu cyfr identyfikuje producenta towaru (13800 to kod Nestle USA FrozenFood Division, czyli dzia�u mro�onek ameryka�skiego koncernu Nestle). Drugagrupa cyfr (znów pi��) to identyfikator produktu (okre�laj�cy mi�dzy innymirozmiar opakowania). Ostatnia cyfra to cyfra kontrolna, której jedynym zadaniemjest pomoc w weryfikowaniu poprawno�ci kodów kreskowych (poprawno�cipoprzednich cyfr). Kiedy taki kod zostanie niepoprawnie zeskanowany, cyfra kon-trolna nie b�dzie si� zgadza� z jedenastoma pierwszymi cyframi, wi�c skanerkasowy odrzuci kod.

Oto jedna z metod obliczania cyfry kontrolnej kodu kreskowego UPC:

Dodaj cyfr� pierwsz�, trzeci�, pi�t�, siódm�, dziewi�t� i jedenast�.Dodaj cyfr� drug�, czwart�, szóst�, ósm� i dziesi�t�.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 przyk�adzie pizzy Stouffer otrzymamy pierwsz� sum� o warto�ci (0+3+0+1+1+3) = 8; druga suma powinna wynosi� (1+8+0+5+7) = 21. Suma iloczynu pierw-szej sumy przez 3 i drugiej sumy daje 45. 45 odj�� 1 daje 44. Reszta z dzielenia44 przez 10 to 4. Ró�nica 9 – 4 wynosi 5. Oto kilka innych przyk�adów liczbowychkodów kreskowych UPC, które mo�emy sprawdzi� — lepiej liczy�, zamiast szuka�tych produktów w lodówkach:

Jif Creamy Peanut Butter (18 uncji): 0 51500 24128 ?Ocean Spray Jellied Cranberry Sauce (8 uncji): 0 31200 01005 ?

Odpowiedzi znajduj� si� u do�u strony1.

1 Brakuj�ce cyfry kontrolne to 8 (Jif) i 6 (Ocean Spray) — przyp. autora.

90 Rozdzia� 4. Wyra�enia

Napiszmy program, który b�dzie oblicza� cyfr� kontroln� dla dowolnego koduUPC. Program b�dzie wymaga� od u�ytkownika wprowadzenia pierwszych 11cyfr kodu UPC, a nast�pnie wypisze brakuj�c� cyfr� kodu. Aby unikn�� omy�ek,naka�emy u�ytkownikowi wprowadzanie cyfr kodu partiami: najpierw wprowadzisamotn� cyfr� z lewej, potem grup� pi�ciu cyfr kodu producenta, a na koniec pi�tk�cyfr kodu produktu. Sesja z programem b�dzie wygl�da�a mniej wi�cej tak:

Podaj pierwsz� cyfr� kodu: 0Podaj pierwsz� grup� pi�ciu cyfr kodu: 13800Podaj nast�pn� grup� pi�ciu cyfr kodu: 15173Cyfra kontrolna: 5

Zamiast wczytywa� poszczególne grupy cyfr jako liczby pi�ciocyfrowe,b�dziemy wczytywa� je jako pi�tk� liczb jednocyfrowych. Wczytanie cyfr jakoosobnych liczb b�dzie dla nas potem znacznie wygodniejsze. Nie trzeba b�dziesi� te� martwi� na przyk�ad o to, �e jedna z dwóch pi�ciocyfrowych liczb niezmie�ci si� w zmiennej int (w niektórych starszych kompilatorach graniczna war-to�� typu int to 32 767). Aby wczyta� pojedyncz� cyfr�, wykorzystamy funkcj�scanf ze specyfikatorami konwersji %1d, odpowiadaj�cymi liczbom jednocyfro-wym (w zapisie dziesi�tnym).

/* 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� pi�ciu cyfr kodu: "); scanf("%1d%1d%1d%1d%1d", &i1, &i2, &i3, &i4, &i5); printf("Podaj drug� grup� pi�ciu 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;}

Zauwa�my, �e wyra�enie 9 - ((total - 1) % 10) mo�na by zapisa�jako 9 - (total - 1) % 10, ale dodatkowa para nawiasów nie zaciemniawyra�enia — wr�cz odwrotnie, czyni je czytelniejszym.

upc.c

4.2. Operatory przypisania 91

4.2. Operatory przypisaniaPo obliczeniu warto�ci wyra�enia cz�sto chcemy zachowa� t� warto�� w zmiennejdo póniejszego u�ycia. W j�zyku C s�u�y do tego celu operator przypisania(ang. assignment) w postaci symbolu =. Aby da�o si� wygodnie aktualizowa�warto�� zmiennej warto�ci� wyra�enia, C oferuje równie� zestaw tak zwanychz�o�onych operatorów przypisania (ang. compound assignment operators).

Przypisania prosteEfektem przypisania v = e jest obliczenie wyra�enia e i skopiowanie warto�ciwyra�enia do v. Jak wida� na przyk�adach poni�ej, e mo�e by� zmienn�, sta�� albodowolnym wyra�eniem:

i = 5; /* i ma teraz warto�� 5 */j = i; /* j ma teraz warto�� 5 */k = 10 * i + j; /* k ma teraz warto�� 55 */

Je�li e i v nie s� warto�ciami tego samego typu, w toku realizacji przypisania war-to�� e zostanie skonwertowana na typ v:

int i;float f;

i = 72.99f; /* i ma teraz warto�� 72 */f = 136; /* f ma teraz warto�� 136.0 */

Do tematu konwersji warto�ci przypisywanej wrócimy nieco póniej.W wielu innych j�zykach programowania przypisanie jest instrukcj�. W j�zyku

C przypisanie jest jednak operatorem, tak jak +. Innymi s�owy, akt przypisaniajest wyra�eniem posiadaj�cym warto��, tak samo jak akt dodawania jest wyra�e-niem posiadaj�cym warto�� (tu równ� sumie operandów). Warto�ci� przypisaniav = e jest warto�� v, obliczana ju� po wykonaniu przypisania. Warto�ci� przy-pisania i = 72.99f jest wi�c nie 72,99, ale 72.

Efekty uboczneZazwyczaj nie oczekuje si� od operatorów, aby modyfikowa�y warto�ci operan-dów — w matematyce operatory nie maj� przecie� takich w�a�ciwo�ci. Dzia�aniei + j nie modyfikuje ani i, ani j, a jedynie oblicza sum� i oraz j.

Wi�kszo� operatorów j�zyka C równie� nie modyfikuje operandów, ale nie-które to robi�. O takich operatorach mówimy, �e maj� efekty uboczne, poniewa� ichdzia�anie nie ogranicza si� tylko do jawnego wyliczenia warto�ci. Pierwszym ope-ratorem, jaki poznajemy od strony efektów ubocznych, jest prosty operator przypi-sania — nie tylko oblicza warto� wyra�enia prawego operandu, ale tak�e mody-fikuje warto� lewego operandu. Obliczenie warto�ci wyra�enia i = 0 daje warto�0, a efektem ubocznym obliczenia wyra�enia jest przypisanie 0 do i.

przypisania z konwersj� � 7.4

92 Rozdzia� 4. Wyra�enia

Poniewa� przypisanie jest operatorem, mo�emy konstruowa� �a�cuchowe wyra-�enia z operatorami przypisania:

i = j = k = 0;

Operator = jest ��czny prawostronnie, wi�c takie przypisanie jest interpretowanejako:

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

W efekcie w pierwszej kolejno�ci nast�pi przypisanie 0 do k, nast�pnie wynikprzypisania zostanie przypisany do j, a wynik tego przypisania — do i.

Nale�y si� wystrzega� nieoczekiwanych wyników w �a�cuchowych przypisaniach,spowodowanych konwersj� typów operandów:

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 wsz�dzie tam,gdzie by�aby dozwolona warto�� typu v. W poni�szym przyk�adzie wyra�eniej = i kopiuje warto�� i do zmiennej j. Nowa warto�� j jest potem dodawana do1 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 „zagnie�d�one przypisania” zmniej-szaj� czytelno�� programu. Mog� by� równie� przyczyn� subtelnych b��dów,o których b�dzie mowa w podrozdziale 4.4.

L-warto�ciWi�kszo�� operatorów j�zyka C pozwala, aby w roli operandów wyst�powa�yzmienne, sta�e albo wyra�enia (równie� zawieraj�ce inne operatory). Operatorprzypisania jest o tyle wyj�tkowy, �e wymaga, aby lewy operand by� tak zwan�l-warto�ci� (ang. lvalue). L-warto�� reprezentuje obiekt przechowywany w pami�cikomputera. Nie mo�e to by� sta�a ani na przyk�ad wynik porównania. L-warto-�ciami s� wszystkie zmienne. Wyra�enia w rodzaju 10 albo 2 * i l-warto�ciaminie s�. Na razie jedyne l-warto�ci, które znamy, to w�a�nie zmienne. W dalszychrozdzia�ach powiemy sobie tak�e o innych l-warto�ciach.

4.2. Operatory przypisania 93

Z wymagania, aby lewym operandem operatora przypisania by�a l-warto��,wynika, �e po lewej stronie operatora przypisania nie wolno stosowa� �adnychwyra�e�:

12 = i; /*** �LE ***/i + j = 0; /*** �LE ***/-i = j; /*** �LE ***/

Takie b��dne przypisania s� wykrywane przez kompilator — próba skompilowa-nia powy�szych instrukcji zaowocuje b��dem kompilacji z komunikatem „invalidlvalue in assignment” („niepoprawna l-warto�� w przypisaniu”).

Przypisania z�o�oneW programach pisanych w j�zyku C cz�sto widzi si� przypisania, które przy obli-czaniu warto�ci przypisania bazuj� na poprzedniej warto�ci modyfikowanej zmien-nej. Oto przyk�adowe przypisanie dodaj�ce 2 do bie��cej warto�ci zmiennej i:

i = i + 2;

Takie i tym podobne instrukcje mo�na w j�zyku C skraca� do postaci przypisa�z�o�onych (ang. compound assignments). Mo�emy wi�c do analogicznej operacjiwykorzysta� operator z�o�ony +=:

i += 2; /* to samo, co i = i + 2 */

Operator += dodaje do warto�ci lewego operandu warto�� prawego operandu.W j�zyku C mamy do dyspozycji dziewi�� z�o�onych operatorów przypisania,

w tym:

-= *= /= %=

(o pozosta�ych z�o�onych operatorach przypisania powiemy sobie w jednymz dalszych rozdzia�ów). Wszystkie operatory przypisa� z�o�onych dzia�aj� napodobnych zasadach:

v += e dodaje e do v i zapisuje sum� w vv -= e odejmuje e do v i zapisuje ró�nic� w vv *= e mno�y v przez e i zapisuje iloczyn w vv /= e dzieli v przez e i zapisuje iloraz w vv %= e oblicza reszt� z dzielenia v przez e i zapisuje wynik v

Wcale nie oznacza to, �e zapis v += e jest zupe�nie „to�samy” z v = v + e.Przede wszystkim ze wzgl�du na pierwsze�stwo operatorów i *= j + k to nieto samo co i = i * j + k. S� te� rzadkie przypadki, kiedy v += e jestró�ne od v = v + e, gdy v posiada efekty uboczne. Podobne zastrze�enia doty-cz� równie� pozosta�ych operatorów przypisania.

Przy stosowaniu przypisa� z�o�onych trzeba uwa�a�, �eby nie zamieni� miejscamiznaków w symbolu operatora. Przestawienie znaków mo�e doprowadzi� do utwo-rzenia wyra�enia, które b�dzie poprawne z punktu widzenia sk�adni programu

inne operatoryprzypisania � 20.1

94 Rozdzia� 4. Wyra�enia

(kompilator nie zg�osi b��du), ale niepoprawne ze wzgl�du na oczekiwane dzia-�anie. Je�li na przyk�ad zamierzamy napisa� i += j, ale omy�kowo napiszemyi =+ j, program jak najbardziej si� skompiluje. Niestety, zamiast doda� do iwarto�� j, wymusimy wykonanie wyra�enia i = (+j), czyli skopiujemy j do i.

Operatory przypisa� z�o�onych maj� te same w�a�ciwo�ci co operatory przy-pisa� prostych. W szczególno�ci s� prawostronnie ��czne, wi�c instrukcja:

i += j += k;

oznacza:

i += (j += k);

4.3. Operatory inkrementacji i dekrementacjiDo najcz��ciej u�ywanych operatorów j�zyka C nale�� operatory inkrementacji(dodania jedynki) i dekrementacji (odj�cia jedynki). Tego rodzaju operacje mo�na,rzecz jasna, zapisa� za pomoc� zwyczajnych operatorów arytmetycznych:

i = i + 1;j = j - 1;

Mo�na te� wykorzysta� operatory przypisa� z�o�onych:

i += 1;j -= 1;

Ale C oferuje jeszcze krótszy zapis tych operacji przy u�yciu operatorów ++(inkrementacja) i -- (dekrementacja).

Na pierwszy rzut oka operatory inkrementacji i dekrementacji to wcielonaprostota: ++ dodaje jeden, a -- odejmuje jeden od operandu. Niestety, prostotajest tu myl�ca. Operatory inkrementacji i dekrementacji bywaj� nieoczywistew u�yciu. Wynika to z tego, �e operatory inkrementacji (++) i dekrementacji (--)mog� wyst�powa� w postaci przedrostkowej (np. ++i, --i) albo przyrostkowej(np. i++, i--). W�a�ciwy wybór rodzaju operatora silnie wp�ywa na poprawno��programu.

Kolejna komplikacja tkwi w fakcie, �e operatory ++ i -- posiadaj� efektyuboczne (tak jak operatory przypisania) — modyfikuj� warto�� operandu. Oblicze-nie wyra�enia ++i (inkrementacja przedrostkowa albo tzw. „przed-inkrementacja” i)daje warto�� i + 1 oraz — w ramach efektu ubocznego — zwi�ksza i o jeden:

i = 1;printf("i to %d\n", ++i); /* wypisuje "i to 2" */printf("i to %d\n", i); /* wypisuje "i to 2" */

Z kolei obliczenie wyra�enia i++ („po-inkrementacja”) daje warto�� i, alew ramach efektu ubocznego i jest zwi�kszane o 1:

4.3. Operatory inkrementacji i dekrementacji 95

i = 1;printf("i to %d\n", i++); /* wypisuje "i to 1" */printf("i to %d\n", i); /* wypisuje "i to 2" */

Pierwsza instrukcja z wywo�aniem printf wypisze na wyj�ciu pierwotn� war-to�� i, jeszcze sprzed inkrementacji. Drugie wywo�anie printf wypisze now�warto�� i. Jak wida�, ++i oznacza „zwi�ksz natychmiast warto�� i”, a i++ ozna-cza „daj bie��c� warto�� i, a potem zwi�ksz i”. Co to znaczy „potem”? Stan-dard j�zyka C nie precyzuje dok�adnego momentu wykonania inkrementacji ope-ratora przyrostkowego, ale mo�na bezpiecznie za�o�y�, �e i b�dzie mia�o now�warto�� jeszcze przed wykonaniem nast�pnej instrukcji.

Podobne w�a�ciwo�ci ma operator dekrementacji --:

i = 1;printf("i to %d\n", --i); /* wypisuje "i to 0" */printf("i to %d\n", i); /* wypisuje "i to 0" */

i = 1;printf("i to %d\n", i--); /* wypisuje "i to 1" */printf("i to %d\n", i); /* wypisuje "i to 0" */

Kiedy operator ++ albo -- wyst�pi wielokrotnie w jednym wyra�eniu, warto��wyra�enia i faktyczne warto�ci operandów mog� by� trudne do ustalenia. Wemynast�puj�cy przyk�ad:

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

Jakie s� warto�ci i, j i k po wykonaniu tych instrukcji? Poniewa� zmienna iby�a inkrementowana przed u�yciem jej warto�ci w wyra�eniu, a zmienna j by�ainkrementowana po u�yciu jej warto�ci w wyra�eniu, powy�sze instrukcje s�równowa�ne nast�puj�cym:

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

wi�c wynikowe warto�ci zmiennych i, j i k to odpowiednio 2, 3 i 4. Dla porów-nania wykonanie instrukcji:

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

zostawi zmienne i, j i k z warto�ciami (odpowiednio) 2, 3 i 3.Dla porz�dku wypada dopowiedzie�, �e przyrostkowe wersje operatorów ++

i -- maj� wy�szy priorytet ni� jednoargumentowe operatory +, - i s� ��czne lewo-stronnie. Wersje przedrostkowe maj� priorytet taki sam jak jednoargumentoweoperatory +, - i s� ��czne prawostronnie.

96 Rozdzia� 4. Wyra�enia

4.4. Obliczanie warto�ci wyraeW tabeli 4.2 zestawiono operatory omówione w poprzednich podrozdzia�ach(podobn� tabel�, ale z kompletem operatorów, zawiera dodatek A). Pierwszakolumna okre�la pierwsze�stwo operatorów wzgl�dem pozosta�ych operatoróww tabeli (1 to najwi�kszy priorytet, 5 to priorytet najmniejszy). Ostatnia kolumnaopisuje ��czno�� operatorów.

Priorytet Nazwa Symbol �czno��

1 inkrementacja (przyrostkowa)dekrementacja (przyrostkowa)

++

--

lewostronna

2 inkrementacja (przedrostkowa)dekrementacja (przedrostkowa)jednoargumentowy plusjednoargumentowy minus

++--+-

prawostronna

3 multiplikatywne * / % lewostronna

4 addytywne + - lewostronna

Tabela 4.2.Cz��ciowa lista

operatorów j�zyka C

5 przypisania = *= /= %=+= -=

prawostronna

Tabela 4.2 (albo jej uzupe�niony odpowiednik z dodatku A) b�dzie bardzoprzydatna. Za�ó�my, �e w kodzie ród�owym (na przyk�ad cudzym) napotkamy tak�instrukcj�:

a = b += c++ - d + --e / -f

Takie wyra�enie by�oby znacznie czytelniejsze, gdyby zawiera�o nawiasy gru-puj�ce podwyra�enia. Z pomoc� tabeli 4.2 mo�emy �atwo samodzielnie uzupe�ni�pogrupowa� wyra�enie — wystarczy znale� w wyra�eniu operator o najwy�-szym priorytecie i wraz z operandami uj�� go w nawias. Od tej pory b�dzie dla naspojedynczym operandem. Technik� powtarzamy do momentu rozpoznania wszyst-kich podwyra�e�.

W naszym przyk�adzie operatorem o najwy�szym priorytecie jest ++, wyst�-puj�cy tu jako operator przyrostkowy. Ujmujemy operator z operandami w nawias:

a = b += (c++) - d + --e / -f

Nast�pny wed�ug pierwsze�stwa jest operator -- (przedrostkowy) oraz jed-noargumentowy minus (oba maj� priorytet 2.):

a = b += (c++) - d + (--e) / (-f)

Drugi znak minusa w wyra�eniu ma operand po lewej stronie, wi�c jest operatoremodejmowania, a nie kolejnym jednoargumentowym minusem.

Nast�pnie odnajdujemy operator / (priorytet 3.):

a = b += (c++) - d + ((--e) / (-f))

4.4. Obliczanie warto�ci wyra�e 97

Wyra�enie zawiera jeszcze dwa operatory o priorytecie 4. (wed�ug tabeli 4.2):dodawanie i odejmowanie. Kiedy w wyra�eniu s�siaduj� ze sob� dwa operatoryo równym priorytecie, nale�y bardzo uwa�nie zastosowa� regu�� ��czno�ci.W naszym przyk�adzie - i + otaczaj� d; ��czno�� dwuargumentowych operatorów+ i - jest lewostronna, wi�c stawiamy nawiasy najpierw wokó� odejmowania,a potem wokó� dodawania:

a = b += (((c++) - d) + ((--e) / (-f)))

Zosta�y ju� tylko przypisania, oba przy operandzie b, wi�c znów trzeba si�posi�kowa� ��czno�ci�. Operatory przypisania s� ��czne prawostronnie (od prawejdo lewej), zatem najpierw ujmujemy w nawias podwyra�enie z operatorem +=,a potem podwyra�enie z operatorem =:

(a = (b += (((c++) - d) + ((--e) / (-f)))))

W ten sposób dokonali�my pe�nego pogrupowania wyra�enia.

Kolejno� obliczania podwyra�e�Regu�y pierwsze�stwa i ��czno�ci operatorów pozwalaj� na skuteczne rozbiciedowolnie skomplikowanego wyra�enia na podwyra�enia — mo�emy dzi�ki nimpogrupowa� poszczególne podwyra�enia w nawiasy. Paradoksalnie obie regu�ynie zawsze pozwalaj� na okre�lenie warto�ci wyra�enia, która mo�e zale�e� odkolejno�ci obliczania warto�ci poszczególnych podwyra�e�.

J�zyk C nie definiuje kolejno�ci obliczania warto�ci podwyra�e� (z wyj�tkiempodwyra�e� anga�uj�cych operatory logiczne and i or, operatory warunkowei operatory przecinka). Przez to w przypadku wyra�enia (a + b) * (c - d)nie mo�emy mie� pewno�ci, czy jako pierwsze zostanie obliczone podwyra�enie(a + b), czy mo�e (c -d).

Wi�kszo�� wyra�e� ma t� sam� warto�� niezale�nie od tego, w jakiej kolej-no�ci zosta�y obliczone ich podwyra�enia. Bywa jednak, �e podwyra�enie mody-fikuje jeden ze swoich operandów. Wemy poni�szy przyk�ad:

a = 5;c = (b = a + 2) - (a = 1);

Efekt wykonania drugiej instrukcji jest niezdefiniowany. Standard j�zyka C niemówi nic jednoznacznego na temat warto�ci tak zbudowanego wyra�enia. W przy-padku wi�kszo�ci kompilatorów zmienna c b�dzie mia�a warto�� 6 albo 2. Je�lijako pierwsze zostanie obliczone podwyra�enie (b = a + 2), b otrzyma war-to�� 7, a c zostanie obliczone jako 6. Ale je�li pierwszym obliczonym podwyra�e-niem b�dzie (a = 1), wtedy b otrzyma warto�� 3, a w c znajdzie si� warto�� 2.

Nale�y unika� konstruowania wyra�e�, w których odwo�ujemy si� do warto�cizmiennej, a równocze�nie (w innym podwyra�eniu) bazujemy na tej warto�ci.Wyra�enie (b = a + 2) - (a = 1) zawiera odwo�anie do warto�ci aw pierwszym podwyra�eniu oraz modyfikacj� warto�ci do a w drugim podwyra�eniu(przez przypisanie jedynki). Niektóre kompilatory oznaczaj� takie wyra�enia komu-nikatami z ostrze�eniami w rodzaju „operacja na ‘a’ mo�e by� niezdefiniowana”.

operatory logiczne and i or � 5.1

operator warunkowy � 5.2

operator przecinka � 5.3

98 Rozdzia� 4. Wyra�enia

Aby zapobiec tego rodzaju problemom, najlepiej unika� stosowania operatoraprzypisania w podwyra�eniu. Zamiast tego nale�y wykona� przypisania jako osobneinstrukcje. Na przyk�ad nasze w�tpliwe wyra�enie mo�e zosta� rozbite nast�puj�co:

a = 5;b = a + 2;a = 1;c = b - a;

W ten sposób zagwarantujemy, �e zmienna c otrzyma warto�� 6.Poza operatorami przypisania mamy jeszcze inne operatory modyfikuj�ce

warto�� operandu — chodzi o operatory inkrementacji i dekrementacji. Przy korzy-staniu z tych operatorów równie� trzeba uwa�a�, aby nie doprowadzi� do powstaniawyra�enia, którego poprawna warto�� jest zale�na od konkretnej kolejno�ci obliczaniapodwyra�e�, jak tutaj, gdzie do j mo�e zosta� przypisana jedna z dwóch warto�ci:

i = 2;j = i * i++;

Pozornie jest oczywiste, �e j otrzyma warto�� 4. Niestety, w efekcie wykonaniatakich instrukcji zmienna j mo�e mie� równie� warto�� 6. Oto mo�liwy scena-riusz: (1) najpierw pobierany jest drugi operand (pierwotna warto�� i); nast�pnie ipodlega inkrementacji (2) i pobierany jest drugi operand (ju� nowa warto�� i);(3) obliczany jest iloczyn obu operandów: liczba 6. „Pobranie” warto�ci zmiennejoznacza odczytanie warto�ci z pami�ci skojarzonej ze zmienn�. Póniejsza zmianawarto�ci zmiennej w pami�ci nie wp�ywa ju� na warto�� pobran�, bo pobrana war-to�� jest kopiowana w specjalne miejsce (tzw. rejestr) wewn�trz procesora.

Niezdefiniowane zachowanie programuWed�ug standardu j�zyka C instrukcje takie jak c = (b = a + 2) - (a = 1)oraz j = i * i++ powoduj� tzw. niezdefiniowane zachowanie (ang. undefinedbehavior) programu (patrz podrozdzia� 4.1). Kiedy program zawiera zachowanianiezdefiniowane, nie mo�na ju� nic powiedzie o jego wykonaniu. W zale�no�ci odu�ytego kompilatora mo�e si� on zachowywa ró�nie, ale to nie wyczerpuje poj�cia„niezdefiniowanego zachowania”. Przede wszystkim program mo�e si� w ogóle nieskompilowa, skompilowany mo�e si� nie uruchomi, a uruchomiony mo�e si�wy�o�y, dzia�a niepoprawnie b�d dawa nieznacz�ce wyniki (np. za ka�dymuruchomieniem inne). Innymi s�owy, przestaje by „programem” — zachowa� nie-zdefiniowanych trzeba wi�c unika jak ognia.

4.5. Instrukcje wyraenioweJ�zyk C posiada niezwyk�� w�a�ciwo�� — tutaj kade wyra�enie mo�e by� u�ytejako instrukcja programu. Ka�de wyra�enie (niezale�nie od jego typu i od tego, cooblicza) mo�e zosta� zamienione na instrukcj� — wystarczy zako�czy� je �red-nikiem. Na przyk�ad na instrukcj� mo�emy zamieni� wyra�enie ++i:

++i;

Pytania i odpowiedzi 99

Kiedy dochodzi do wykonania tej instrukcji, nast�puje zwi�kszenie warto�cii o jeden, a póniej pobierana jest nowa warto�� i (tak jakby mia�a za chwil� zosta�wykorzystana przy obliczaniu wyra�enia nadrz�dnego). Ale skoro ++i nie jestcz��ci� wi�kszego wyra�enia, pobrana warto�� jest odrzucana, a program przecho-dzi do wykonania nast�pnej instrukcji (ale inkrementacja i jest oczywi�cie trwa�a).

Skoro warto�� instrukcji wyra�eniowej jest odrzucana, nie ma wi�kszego sensuwykorzystywanie wyra�e� jako instrukcji, chyba �e s� to wyra�enia z efektamiubocznymi; one zostan� przecie� wykonane mimo odrzucenia obliczonej warto�ciwyra�enia. Wemy trzy przyk�ady. W pierwszym do i przypisywana jest war-to�� 1 — nowa warto�� i jest pobierana, ale zaraz odrzucana:

i = 1;

W drugim przyk�adzie warto�� i jest pobierana, ale znów nie b�dzie nigdziewykorzystana. Za to samo i ju� po pobraniu warto�ci zostanie zwi�kszone o jeden:

i++;

W trzecim przyk�adzie zostanie obliczona warto�� wyra�enia i * j - 1, aleobliczona warto�� zaraz b�dzie odrzucona:

i * j - 1;

Taka instrukcja nie ma �adnego efektu ubocznego, nie zmienia �adnego z operan-dów, wi�c jest zwyczajnie bezcelowa.

Bezcelow�, a wi�c pust� instrukcj� wyra�eniow� mo�na �atwo pope�ni� przezprost� literówk�. Wystarczy, �e zamiast:

i = j;

przypadkiem napiszemy:

i + j;

(taki b��d jest prawdopodobny tym bardziej, �e znaki + i = zajmuj� ten sam klawiszna klawiaturze). Niektóre kompilatory wykrywaj� bezcelowe instrukcje wyra�e-niowe, generuj�c przy nich ostrze�enia w rodzaju „instrukcja bez efektu” („sta-tement with no effect”).

Pytania i odpowiedziP: Zauwa�y�em, �e j�zyk C nie posiada operatora pot�gowania. Jak mam pod-

nosi liczby do pot�gi?O: Je�li wyk�adnik pot�gi jest niewielk� dodatni� liczb� ca�kowit�, pot�gowanie naj-

lepiej zrealizowa� przez wielokrotne mno�enie (np. i * i * i dla obliczeniasze�cianu i). Do obliczania pot�g o wyk�adnikach nieca�kowitych najlepiej wyko-rzysta� funkcj� pow.funkcja pow � 23.3

100 Rozdzia� 4. Wyra�enia

P: Chcia�em zastosowa operator % przy operandzie typu float, ale programnie daje si� skompilowa. Co mog� zrobi (s. 86)?

O: Operator % wymaga operandów ca�kowitych. Spróbuj u�y� funkcji fmod.P: Dlaczego dzia�anie operatorów dzielenia (/) i reszty z dzielenia (%) dla ujem-

nych operandów jest tak zagmatwane (s. 87)?O: Zasady dzia�ania tych operatorów nie s� tak zagmatwane, jakby si� wydawa�o.

W obu wersjach standardu celem jest zapewnienie, �eby warto�� (a / b) *b + a % b zawsze by�a równa a (i faktycznie, oba standardy gwarantuj� tak�zale�no��, o ile tylko warto�� a / b jest warto�ci� „reprezentowaln�”). Problempolega na tym, �e za�o�on� zale�no�� mo�na spe�ni� na dwa sposoby, przy ró�nychmetodach obliczania a / b i a % b. Wed�ug C89 albo -9 / 7 to –1 i -9 % 7to –2 (równo�� jest spe�niona), albo -9 / 7 to –2 i -9 % 7 to 5 (i znów rów-no�� jest spe�niona). W pierwszym przypadku (-9 / 7) * 7 + -9 % 7 daje–1×7+–2 = –9, w drugim przypadku (-9 / 7) * 7 + -9 % 7 to –2×7+5 = –9. Do czasu pojawienia si� standardu C99 wi�kszo�� procesorów wykony-wa�a ju� dzielenie ca�kowite z obcinaniem w kierunku zera, wi�c tak� w�a�nieregu�� dzielenia zapisano w standardzie C99 jako jedyn� dozwolon� warto��ilorazu z operandem ujemnym.

P: Skoro w C s� l-warto�ci, czy s� te� r-warto�ci (s. 92)?O: W rzeczy samej. L-warto�� jest wyra�eniem, które jest dozwolone po lewej stronie

operatora przypisania; r-warto�� to wyra�enie, które jest dozwolone po prawejstronie. R-warto�� mo�e wi�c by� zmienn�, sta�� albo dowolnym wyra�eniem.W niniejszej ksi��ce, podobnie jak w standardzie j�zyka C, b�dziemy trzyma�si� okre�lenia „wyra�enie”, które jednak do�� dobrze oddaje istot� r-warto�ci.

*P: By�a mowa o tym, �e v += e nie jest odpowiednikiem v = v + e, je�li vma efekty uboczne. Jak to rozumie (s. 93)?

O: Obliczenie warto�ci v += e powoduje, �e warto�� v jest obliczana tylko raz.v w wyra�eniu v = v + e jest obliczane dwa razy. Wi�c je�li w tym drugimprzypadku v posiada efekt uboczny, zostanie on wykonany dwukrotnie. W tymprzyk�adzie i jest inkrementowane raz:

a[i++] += 2;

ale je�li zamiast += u�yjemy przypisania =, otrzymamy:

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

Warto�� i jest równocze�nie pobierana i modyfikowana w obr�bie jednej instruk-cji, wi�c wynik wykonania takiej instrukcji jest niezdefiniowany. Jest prawdopo-dobne, �e i zostanie zwi�kszone dwukrotnie, ale w istocie nie mo�na nic pewnegopowiedzie� o dzia�aniu takiego programu.

P: W jakim celu w C udost�pniono operatory ++ i --? Czy s� one szybsz� meto-d� inkrementacji i dekrementacji zmiennej, czy s� jedynie wygodniejsze(krótsze w zapisie) (s. 94)?

O: J�zyk C odziedziczy� operatory ++ i -- w spadku po j�zyku B Kena Thompsona.Thompson wprowadzi� te operatory, poniewa� jego kompilator B najwyraniejpotrafi� efektywniej przet�umaczy� zapis ++i ni� i = i + 1. Operatory te

funkcja fmod � 23.3

Pytania i odpowiedzi 101

sta�y si� sol� j�zyka C (bazuje na nich wiele jego s�awnych idiomów). W nowo-czesnych kompilatorach stosowanie ++ i -- zapewne ani bardzo nie przyspieszyprogramu, ani nie zmniejszy bardzo rozmiaru pliku wynikowego. Nieustaj�capopularno�� tych operatorów wynika chyba z ich zwarto�ci.

P: Czy operatory ++ i -- dzia�aj� ze zmiennymi typu float?O: Tak, operacje inkrementacji i dekrementacji mo�na stosowa� do warto�ci ca�kowi-

tych i zmiennoprzecinkowych, jednak w praktyce ma�o kto próbuje inkrementowa�albo dekrementowa� zmienn� typu float.

*P: Kiedy dok�adnie nast�puje zwi�kszenie warto�ci operandu w przypadku przy-rostkowych wersji ++ i -- (s. 95)?

O: �wietne pytanie. Niestety, nie mo�na na nie �atwo odpowiedzie�. Standard j�zykaC wprowadza poj�cie tak zwanego „punktu sekwencji” i mówi, �e „aktualizacjask�adowanej warto�ci operandu powinna odby� si� pomi�dzy poprzednim a nast�p-nym punktem sekwencji”. W j�zyku C okre�lono kilka ró�nych punktów sekwen-cji. Jednym z nich jest koniec instrukcji wyra�eniowej — na ko�cu instrukcjiwyra�eniowej wszystkie opónione inkrementacje i dekrementacje powinny zosta�wykonane; nie mo�e doj�� do rozpocz�cia wykonywania nast�pnej instrukcjiz pomini�ciem tego kroku.

Niektóre operatory, o których powiemy sobie w dalszej cz��ci ksi��ki (logicznyoperator and, logiczny operator or, operator warunkowy i operator przecinka),równie� stanowi� punkty sekwencji. To samo dotyczy wywo�a� funkcji — argu-menty wywo�ania funkcji musz� by� w pe�ni obliczone przed wykonaniem wywo-�ania. Je�li argument wywo�ania jest wyra�eniem zawieraj�cym przyrostkowyoperator ++ albo --, inkrementacja b�d dekrementacja musi zosta� wykonanajeszcze przed wykonaniem wywo�ania funkcji.

P: Co oznacza „odrzucenie” warto�ci instrukcji wyra�eniowej (s. 99)?O: Z definicji wyra�enie reprezentuje warto��. Je�li np. i ma warto�� 5, to obliczenie

wyra�enia i + 1 daje warto�� 6. Zamie�my to wyra�enie na instrukcj� wyra�e-niow�, dodaj�c �rednik na ko�cu:

i + 1;

W ramach wykonywania tej instrukcji dochodzi do obliczenia warto�ci wyra�eniai + 1. Poniewa� jednak ta warto�� nie jest nigdzie wykorzystywana (nie zosta�aprzypisana do zmiennej ani nie jest wykorzystywana jako podwyra�enie) —przepada.

P: A co z instrukcjami typu i = 1;? Nie widz�, �eby co� tu by�o tracone.O: Pami�tajmy, �e przypisanie jest w j�zyku C operatorem i jak ka�dy operator gene-

ruje warto��. Przypisanie:

i = 1;

powoduje zapisanie 1 w zmiennej i, ale jako wyra�enie ma warto�� przypisania (1)i ta w�a�nie warto�� jest odrzucana. Odrzucanie warto�ci wyra�enia nie jest jak��wielk� strat�, poniewa� w naszej instrukcji chodzi�o nam przede wszystkim o zmo-dyfikowanie zmiennej i.

102 Rozdzia� 4. Wyra�enia

�wiczenia 1. Napisz, co pojawi si� na wyj�ciu programu wykonuj�cego poni�sze 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 wyra�enie (-i)/j b�dzie mia�o zawsze t� sam� warto�� co -(i/j), je�li i i j s�dodatnimi warto�ciami ca�kowitymi? Uzasadnij odpowied.

3. Jaka b�dzie warto�� poni�szych wyra�e� wed�ug standardu C89 (je�li mo�liwa jest wi�cejni� jedna warto��, podaj wszystkie warianty):

(a) 8 / 5 (b) -8 / 5 (c) 8 / -5 (d) -8 / -5

4. Powtórz �wiczenie 3. dla wytycznych standardu C99.

5. Jaka b�dzie warto�� poni�szych wyra�e� wed�ug standardu C89 (je�li mo�liwa jest wi�cejni� jedna warto��, podaj wszystkie warianty):

(a) 8 % 5 (b) -8 % 5 (c) 8 % -5 (d) -8 % -5

6. Powtórz �wiczenie 5. dla wytycznych standardu C99.

7. Algorytm obliczania cyfry kontrolnej UPC ko�czy si� nast�puj�cymi krokami:

Odejmij 1 od sumy.Oblicz reszt� z dzielenia zmniejszonej sumy przez 10.Odejmij reszt� z dzielenia od 9.

A� korci, �eby ten algorytm upro�ci� nast�puj�co:

Oblicz reszt� z dzielenia sumy przez 10.Odejmij reszt� z dzielenia od 10.

Dlaczego taka poprawka nie zadzia�a?

8. Czy program upc.c b�dzie wci�� poprawny, je�li wyra�enie 9 – ((total – 1) % 10) zostaniezast�pione przez (10 - (total % 10)) % 10?

9. Napisz, co pojawi si� na wyj�ciu programu wykonuj�cego poni�sze instrukcje. Za�ó�, �e i,j i k s� zmiennymi typu int:

Podrozdzia� 4.2

Podrozdzia� 4.1

*

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

10. Napisz, co pojawi si� na wyj�ciu programu wykonuj�cego poni�sze instrukcje. Za�ó�, �eii 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 wyj�ciu programu wykonuj�cego poni�sze instrukcje. Za�ó�, �e i, ji 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 wyj�ciu programu wykonuj�cego poni�sze instrukcje. Za�ó�, �ei 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);

Podrozdzia� 4.3 *

104 Rozdzia� 4. Wyra�enia

(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. Które z wyra�e�: ++i czy i++ jest dok�adnie równowa�ne wyra�eniu (i += 1)? Uza-sadnij odpowied.

14. Pogrupuj podwyra�enia w nawiasy tak, aby zilustrowa� sposób interpretacji poni�szychwyra�e� z�o�onych:

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

15. Podaj warto�ci zmiennych i i j po wykonaniu ka�dej z poni�szych instrukcji (pocz�tkowawarto�� i to 1, a pocz�tkowa warto�� j to 2):

(a) i += j; (b) i--; (c) i * j / i; (d) i % ++j;

Zadania programistyczne 1. Napisz program, który b�dzie wymaga� od u�ytkownika wprowadzenia liczby dwucyfrowej,

a nast�pnie wypisze t� liczb� w odwróconej kolejno�ci cyfr. Sesja z programem powinnaprzebiega� tak:

Podaj liczb� dwucyfrow�: 28Wspak: 82

2. Rozbuduj program z zadania 1. tak, aby obs�ugiwa� liczby trzycyfrowe.

3. Przerób program z zadania 2. tak, �eby program wypisywa� odwrotny zapis liczby trzycy-frowej, bez u�ycia operacji arytmetycznych do podzia�u liczby na cyfry. Wskazówka: Zaj-rzyj do programu upc.c z podrozdzia�u 4.1.

4. Napisz program, który wczytuje liczb� wprowadzon� na wej�cie i wy�wietla j� w zapisieósemkowym:

Podaj liczb� pomi�dzy 0 i 32767: 1953W zapisie ósemkowym to: 03641

Wyj�cie programu powinno by� wy�wietlane z u�yciem pi�ciu cyfr, nawet je�li zapisliczby nie wymaga ich tylu. Wskazówka: Aby zamieni� liczb� na reprezentacj� ósemkow�,nale�y podzieli� j� przez osiem. Wynik to pierwsza cyfra zapisu ósemkowego (tutaj: 1).Reszt� z dzielenia nale�y znów podzieli� przez osiem i powtarza� proces tak d�ugo, jak d�ugoreszta b�dzie wi�ksza od 8. Ostatnia cyfra to reszta z ostatniego dzielenia (jest te� prostszysposób, bo funkcja printf potrafi wypisywa� liczby ca�kowite w zapisie ósemkowym —przekonasz si� o tym w rozdziale 7.).

Podrozdzia� 4.4

Podrozdzia� 4.5

Zadania programistyczne 105

5. Przerób program upc.c z podrozdzia�u 4.1 tak, aby u�ytkownik wprowadza� 11 cyfr koduUPC za jednym zamachem:

Podaj 11 cyfr kodu UPC: 01380015173Cyfra kontrolna: 5

6. W krajach europejskich stosuje si� kody kreskowe z 13 cyframi (tzw. kod EAN). Ka�dykod EAN ko�czy si� cyfr� kontroln� (tak jak UPC). Algorytm obliczania cyfry kontrolnejkodu EAN równie� jest do�� podobny:

Dodaj drug�, czwart�, szóst�, ósm�, dziesi�t� i dwunast� cyfr� kodu.Dodaj pierwsz�, trzeci�, pi�t�, siódm�, dziewi�t� 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 przyk�ad tureckie s�odycze Güllüoglu Turkish Delight Pistachio & Coconut maj� kodEAN 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 odj�ciu 1 zo-staje 81. Reszta z dzielenia 81 przez 10 to 1. 9 – 1 daje 8. Zgadza si�, cyfra kontrolna naszegokodu to dok�adnie 8. Zadanie polega na przerobieniu programu upc.c z podrozdzia�u 4.1tak, aby oblicza� cyfr� kontroln� kodu EAN. U�ytkownik powinien wprowadza� do programupierwsze 12 cyfr kodu EAN jednym ci�giem:

Podaj 12 cyfr kodu EAN: 869148426000Cyfra kontrolna: 8