Jezyk C Nowoczesne programowanie Wydanie II 2

background image
background image

Idź do

• Spis treści
• Przykładowy rozdział

• Katalog online

• Dodaj do koszyka

• Zamów cennik

• Zamów informacje

o nowościach

• Fragmenty książek

online

Helion SA

ul. Kościuszki 1c

44-100 Gliwice

tel. 32 230 98 63

e-mail: helion@helion.pl

© Helion 1991–2010

Katalog książek

Twój koszyk

Cennik i informacje

Czytelnia

Kontakt

• Zamów drukowany

katalog

Język C. Nowoczesne
programowanie. 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!

background image

5

SPIS TRECI

Wstp

19

1.

WPROWADZENIE

29

1.1.

Historia jzyka C

29

Pocztki

29

Standaryzacja

30

Jzyki oparte na C

31

1.2.

Mocne i sabsze strony jzyka C

32

Mocne strony

33

Saboci

33

Efektywne stosowanie jzyka C

34

2.

FUNDAMENTY JZYKA C

39

2.1.

Piszemy prosty program

39

Wywietlamy cytat

40

Kompilacja i konsolidacja

40

Zintegrowane rodowiska programistyczne

41

2.2.

Ogólna posta prostego programu w C

42

Dyrektywy preprocesora

43

Funkcje

43

Instrukcje

44

Wypisywanie cigów znakowych

45

2.3.

Komentarze

46

2.4.

Zmienne i przypisania

48

Typy

48

Deklaracje

48

Przypisania

49

Wypisywanie wartoci zmiennej

50

Obliczanie gabarytu przesyki

51

Inicjalizacja

52

Wypisywanie wartoci wyrae

53

background image

6

Spis treci

2.5.

Wczytywanie danych

53

Obliczanie gabarytu przesyki (podejcie drugie)

54

2.6.

Definiowanie nazw dla staych

55

Konwersja skali Fahrenheita na skal Celsjusza

55

2.7.

Identyfikatory

57

Sowa kluczowe

58

2.8.

Ogólny ukad programu C

58

3.

FORMATOWANIE WEJCIA-WYJCIA

69

3.1.

Funkcja printf

69

Specyfikatory konwersji

70

Wykorzystanie printf do formatowania liczb

72

Znaki sterujce

73

3.2.

Funkcja scanf

74

Dziaanie funkcji scanf

76

Zwyke znaki w cigu formatujcym funkcji scanf

78

Skutki mylenia printf ze scanf

79

Dodawanie uamków

79

4.

WYRAENIA

85

4.1.

Operatory arytmetyczne

86

Pierwszestwo i czno operatorów

87

Obliczanie cyfry kontrolnej kodu kreskowego

88

4.2.

Operatory przypisania

91

Przypisania proste

91

L-wartoci

92

Przypisania zoone

93

4.3.

Operatory inkrementacji i dekrementacji

94

4.4.

Obliczanie wartoci wyrae

96

Kolejno obliczania podwyrae

97

4.5.

Instrukcje wyraeniowe

98

5.

INSTRUKCJE WYBORU

107

5.1.

Wyraenia logiczne

108

Operatory relacji

108

Operatory porówna

109

Operatory logiczne

109

5.2.

Instrukcja if

111

Instrukcje blokowe

112

Klauzula else

112

Kaskadowe instrukcje if

114

Obliczanie prowizji brokera giedowego

115

Problem „bezpaskiego” else

116

Wyraenia warunkowe

117

Wartoci boolowskie w C89

118

Wartoci boolowskie w C99

120

5.3.

Instrukcja switch

120

Rola instrukcji break

123

Wypisywanie daty w zapisie urzdowym

124

background image

Spis treci

7

6.

INSTRUKCJE PTLI

133

6.1.

Instrukcja while

134

Ptle nieskoczone

135

Wypisywanie tabeli kwadratów liczb

136

Obliczanie sumy szeregu liczb

137

6.2.

Instrukcja do

137

Obliczanie liczby cyfr w liczbie cakowitej

138

6.3.

Instrukcja for

139

Idiomy instrukcji for

141

Pomijanie wyrae w instrukcji for

142

Instrukcje for w C99

143

Operator przecinka

143

Wypisywanie tabeli kwadratów liczb (podejcie drugie)

144

6.4.

Przerywanie ptli

146

Instrukcja break

146

Instrukcja continue

147

Instrukcja goto

148

Saldo konta

149

6.5.

Instrukcja pusta

151

7.

PODSTAWOWE TYPY C

161

7.1.

Typy cakowite

161

Typy cakowite w C99

164

Literay cakowite

164

Literay cakowite w C99

166

Przepenienie zakresu

166

Wczytywanie i wypisywanie wartoci cakowitych

166

Sumowanie szeregu liczb cakowitych (podejcie drugie)

167

7.2.

Typy zmiennoprzecinkowe

168

Literay zmiennoprzecinkowe

170

Wczytywanie i wypisywanie wartoci zmiennoprzecinkowych

170

7.3.

Typy znakowe

171

Operacje na znakach

172

Znaki ze znakiem i bez znaku

173

Typy arytmetyczne

173

Znaki sterujce

174

Funkcje do manipulowania znakami

176

Wczytywanie i wypisywanie znaków funkcjami scanf i printf

176

Wczytywanie i wypisywanie znaków funkcjami getchar i putchar 177
Okrelanie dugoci komunikatu

179

7.4.

Konwersja typów

180

Zwyczajne konwersje arytmetyczne

181

Konwersja przy przypisaniu

183

Niejawne konwersje w C99

184

Rzutowanie

185

7.5.

Definicje typów

186

Zalety definicji typów

187

Definicje typów a przenono programów

188

7.6.

Operator sizeof

189

background image

8

Spis treci

8.

TABLICE

199

8.1.

Tablice jednowymiarowe

199

Indeksowanie tablic

200

Odwracanie szeregu liczbowego

202

Inicjalizacja tablicy

203

Inicjalizatory desygnowane

203

Sprawdzanie, czy liczba zawiera powtarzajce si cyfry

204

Operator sizeof dla tablic

205

Naliczanie odsetek

206

8.2.

Tablice wielowymiarowe

208

Inicjalizowanie tablic wielowymiarowych

209

Stae tablicowe

210

Rozdawanie kart

211

8.3.

Tablice o zmiennej liczbie elementów (C99)

212

9.

FUNKCJE

223

9.1.

Definiowanie i wywoywanie funkcji

223

Obliczanie rednich

224

Odliczanie

225

Wywietlanie napisu (kolejne podejcie)

226

Definicja funkcji

227

Wywoanie funkcji

229

Sprawdzanie, czy podana liczba jest liczb pierwsz

230

9.2.

Deklaracja funkcji

231

9.3.

Argumenty

233

Konwersje argumentów

234

Argumenty tablicowe

235

Parametry tablicowe o zmiennym rozmiarze

238

Deklaracje parametrów tablicowych ze sowem static

240

Literay tablicowe

241

9.4.

Instrukcja return

242

9.5.

Zakoczenie programu

243

Funkcja exit

243

9.6.

Rekurencja

244

Algorytm quicksort

246

quicksort

248

10. ORGANIZACJA PROGRAMU

261

10.1.

Zmienne lokalne

261

Zmienne statyczne funkcji

262

Parametry

263

10.2.

Zmienne zewntrzne

263

Przykad. Stos implementowany na zmiennych zewntrznych

263

Zalety i wady zmiennych zewntrznych

264

Zgadywanka liczbowa

266

10.3.

Bloki

270

10.4.

Zasig zmiennych

271

10.5.

Organizacja programu w C

272

Sia rozdania pokerowego

273

background image

Spis treci

9

11. WSKANIKI

283

11.1.

Zmienne wskanikowe

283

Deklarowanie zmiennych wskanikowych

284

11.2.

Operator adresu i wyuskania

285

Operator adresu

285

Operator wyuskania

286

11.3.

Przypisania a wskaniki

287

11.4.

Wskaniki jako argumenty funkcji

289

Wyszukiwanie najwikszego i najmniejszego elementu tablicy

291

Ochrona argumentów za pomoc const

293

11.5.

Wskaniki jako wartoci zwracane

293

12. WSKANIKI A TABLICE

301

12.1.

Arytmetyka wskaników

302

Dodawanie liczby do wskanika

303

Odejmowanie liczby od wskanika

303

Odejmowanie wskanika od wskanika

304

Porównywanie wskaników

304

Wskaniki do literaów tablicowych

304

12.2.

Przetwarzanie tablic na bazie wskaników

305

czenie operatorów * i ++

306

12.3.

Nazwa tablicy jako wskanik

307

Odwracanie szeregu liczbowego

308

Argumenty tablicowe (ponownie)

309

Wskanik jako nazwa tablicy

311

12.4.

Wskaniki a tablice wielowymiarowe

311

Przetwarzanie elementów tablicy wielowymiarowej

311

Przetwarzanie wierszy tablicy wielowymiarowej

312

Przetwarzanie kolumn tablicy wielowymiarowej

313

Nazwa tablicy wielowymiarowej jako wskanik

314

12.5.

Wskaniki a tablice o zmiennym rozmiarze (C99)

314

13. CIGI ZNAKÓW

323

13.1.

Literay napisowe

323

Znaki sterujce w literaach napisowych

324

Kontynuacja literau napisowego w nowym wierszu

324

Literay napisowe a pami programu

325

Operacje na literaach napisowych

326

Literay napisowe a literay znakowe

326

13.2.

Zmienne napisowe

327

Inicjalizowanie zmiennej napisowej

328

Tablice znaków a wskaniki do znaków

329

13.3.

Wczytywanie i wypisywanie napisów

330

Wypisywanie napisów funkcjami printf i puts

330

Wczytywanie cigów znaków funkcjami scanf i gets

331

Wczytywanie napisów znak po znaku

333

13.4.

Odwoania do pojedynczych znaków w cigu

334

13.5.

Funkcje biblioteczne jzyka C

335

Funkcja strcpy (kopiowanie cigów)

336

Funkcja strlen (dugo cigu)

338

Funkcja strcat (czenie cigów)

338

background image

10

Spis treci

Funkcja strcmp (porównywanie cigów)

339

Wypisywanie notatek kalendarzowych

340

13.6.

Idiomy

343

Szukanie koca cigu

343

Kopiowanie cigu

345

13.7.

Tablice cigów znaków

347

Argumenty wywoania programu

349

Weryfikacja nazw planet

351

14. PREPROCESOR

363

14.1.

Jak dziaa preprocesor

363

14.2.

Dyrektywy preprocesora

366

14.3.

Makrodefinicje

367

Makrodefinicje proste

367

Makrodefinicje sparametryzowane

370

Operator #

373

Operator ##

374

Ogólne waciwoci makrodefinicji

375

Nawiasy w makrodefinicjach

376

Tworzenie dugich makrodefinicji

377

Makrodefinicje predefiniowane

379

Dodatkowe makrodefinicje predefiniowane w C99

380

Puste argumenty makrodefinicji

381

Makrodefinicje o zmiennej liczbie argumentów

382

Identyfikator __func__

383

14.4.

Warunkowa kompilacja kodu

384

Dyrektywy #if i #endif

384

Operator defined

385

Dyrektywy #ifdef i #ifndef

386

Dyrektywy #elif i #else

386

Zastosowania warunkowej kompilacji kodu

387

14.5.

Inne dyrektywy

388

Dyrektywa #error

389

Dyrektywa #line

390

Dyrektywa #pragma

391

Operator _Pragma

391

15. DUE PROGRAMY

401

15.1.

Pliki ródowe

401

15.2.

Pliki nagówkowe

403

Dyrektywa #include

403

Wspólne makrodefinicje i synonimy typów

405

Wspólne prototypy funkcji

406

Wspólne deklaracje zmiennych

407

Zagniedone dyrektywy #include

409

Ochrona plików nagówkowych

410

Dyrektywy #error w plikach nagówkowych

411

15.3.

Podzia programu na pliki

411

Formatowanie tekstu

412

15.4.

Budowanie programu z wielu plików

419

Pliki Makefile

419

Bdy konsolidowania programu

422

background image

Spis treci

11

Przebudowa programu

422

Definiowanie makrodefinicji na zewntrz programu

425

16. STRUKTURY, UNIE I WYLICZENIA

431

16.1.

Zmienne strukturalne

431

Deklarowanie zmiennych strukturalnych

432

Inicjalizowanie zmiennych strukturowych

433

Inicjalizatory desygnowane

434

Operacje na strukturach

435

16.2.

Typy strukturowe

436

Deklarowanie znacznika struktury

437

Definiowanie typu strukturowego

438

Struktury jako argumenty i wartoci zwracane funkcji

439

Literay strukturowe

440

16.3.

Tablice i struktury zagniedone

441

Struktury struktur

441

Tablice struktur

442

Inicjalizowanie tablic struktur

443

Zarzdzanie baz danych magazynu

444

16.4.

Unie

450

Unie dla oszczdnoci

452

Unie jako mieszane struktury danych

454

Pole „wyrónika” w unii

455

16.5.

Wyliczenia

456

Znaczniki i typy wyliczeniowe

457

Wyliczenia jako liczby cakowite

458

Wyliczenia jako wyróniki unii

459

17. ZAAWANSOWANE ZASTOSOWANIA WSKANIKÓW

469

17.1.

Dynamiczny przydzia pamici

470

Funkcje przydziau pamici

470

Wskaniki puste

471

17.2.

Dynamiczny przydzia cigów znaków

472

Przydzia pamici dla cigu znaków za pomoc funkcji malloc

472

Przydziay dynamiczne

w funkcjach operujcych na cigach znaków

473

Tablice cigów przydzielanych dynamicznie

474

Wypisywanie notatek kalendarzowych (podejcie drugie)

475

17.3.

Tablice przydzielane dynamicznie

476

Przydzia pamici dla cigu znaków za pomoc funkcji malloc

477

Funkcja calloc

478

Funkcja realloc

478

17.4.

Zwalnianie pamici

479

Funkcja free

480

Problem „wiszcych” wskaników

481

17.5.

Listy elementów

481

Deklarowanie typu wza

482

Tworzenie wza listy

483

Operator ->

484

Wstawianie wza na pocztek listy

484

Przeszukiwanie listy

487

Usuwanie wza z listy

488

background image

12

Spis treci

Listy uporzdkowane

490

Zarzdzanie baz danych magazynu (drugie podejcie)

491

17.6.

Wskaniki do wskaników

496

17.7.

Wskaniki do funkcji

497

Wskaniki do funkcji w roli argumentów

497

Funkcja qsort

498

Inne zastosowania wskaników do funkcji

501

Tablice funkcji trygonometrycznych

502

17.8.

Wskaniki zastrzeone (C99)

503

17.9.

Elastyczne skadowe tablicowe (C99)

505

18. DEKLARACJE

517

18.1.

Skadnia deklaracji

517

18.2.

Klasy przydziau

519

Wasnoci zmiennych

519

Klasa przydziau auto

520

Klasa przydziau static

521

Klasa przydziau extern

522

Klasa przydziau register

523

Klasa przydziau funkcji

524

Podsumowanie

525

18.3.

Kwalifikatory typów

526

18.4.

Deklaratory

528

Rozszyfrowywanie zawiych deklaracji

529

Stosowanie synonimów typów dla uproszczenia deklaracji

531

18.5.

Inicjalizatory

531

Zmienne niezainicjalizowane

533

18.6.

Funkcje inline (C99)

533

Definicje rozwijane w miejscu wywoania

534

Ograniczenia funkcji rozwijanych w miejscu wywoania

536

Funkcje inline w GCC

536

19. PROJEKT PROGRAMU

545

19.1.

Moduy

546

Spójno i wspózaleno

548

Rodzaje moduów

548

19.2.

Ukrywanie informacji

549

Modu obsugi stosu

550

19.3.

Abstrakcyjne typy danych

553

Hermetyzacja

554

Typy niepene

554

19.4.

Stos jako abstrakcyjny typ danych (ADT)

555

Definiowanie interfejsu stosu w wersji ADT

555

Implementacja stosu w wersji ADT (na bazie tablicy)

557

Zmiana typu elementu w stosie w wersji ADT

559

Implementowanie stosu ADT (na bazie tablicy dynamicznej)

560

Implementowanie stosu ADT (na bazie listy)

562

19.5.

Problemy projektowe przy ADT

564

Nomenklatura

564

Obsuga bdów

565

Uniwersalny typ ADT

565

ADT w nowszych jzykach programowania

566

background image

Spis treci

13

20. PROGRAMOWANIE NISKOPOZIOMOWE

571

20.1.

Operatory bitowe

571

Operatory przesuni bitowych

572

Negacja, iloczyn, suma i suma wyczajca

573

Operatory bitowe w odwoaniach

do poszczególnych bitów wartoci liczbowych

574

Operatory bitowe w odwoaniach do pól bitowych

576

Szyfrowanie XOR

577

20.2.

Pola bitowe w strukturach

578

Reprezentacja pól bitowych

580

20.3.

Inne niskopoziomowe techniki programistyczne

581

Definiowanie typów maszynowych

581

Unie jako perspektywy

582

Wskaniki jako adresy

584

Podgld pamici

584

Kwalifikator typu volatile

586

21. BIBLIOTEKA STANDARDOWA

593

21.1.

Stosowanie biblioteki standardowej

593

Nazewnictwo w bibliotece standardowej

594

Funkcje ukrywane przez makrodefinicje

595

21.2.

Przegld biblioteki standardowej C89

596

Diagnostyka

596

Obsuga znaków

596

Bdy

596

Cechy typów zmiennoprzecinkowych

596

Rozmiary typów cakowitoliczbowych

596

Lokalizacja programów

597

Matematyka

597

Skoki nielokalne

597

Obsuga sygnaów

597

Zmienne listy argumentów

597

Podstawowe definicje

597

Wejcie-wyjcie

597

Narzdzia

598

Obsuga cigów znaków

598

Daty i godziny

598

21.3.

Uzupenienia i zmiany w C99

598

Arytmetyka liczb zespolonych

599

rodowisko implementacji zmiennoprzecinkowej

599

Znakowe konwersje typów cakowitoliczbowych

599

Alternatywny zapis skadni C

599

Wartoci i typy logiczne

599

Typy cakowitoliczbowe

599

Matematyka na uniwersalnych typach

599

Operacje na znakach wielobajtowych

600

Narzdzia mapowania i klasyfikacji znaków wielobajtowych

600

21.4.

Nagówek <stddef.h> — definicje podstawowe

600

21.5.

Nagówek <stdbool.h> (C99) — typy i wartoci logiczne

601

background image

14

Spis treci

22. WEJCIE-WYJCIE

605

22.1.

Strumienie

606

Wskaniki plikowe

606

Strumienie standardowe a przekierowania

607

Pliki tekstowe i pliki binarne

608

22.2.

Operacje na plikach

609

Otwieranie pliku

610

Tryby dostpu do plików

611

Zamykanie pliku

612

Doczanie pliku do otwartego strumienia

613

Pobieranie nazw plików z wiersza polecenia

613

Sprawdzanie moliwoci otwarcia pliku

614

Pliki tymczasowe

615

Buforowanie plików

616

Inne operacje na plikach

618

22.3.

Formatowanie wejcia-wyjcia

619

Funkcje …printf

619

Specyfikatory konwersji dla funkcji …printf

620

Zmiany specyfikatorów konwersji w C99

622

Przykady specyfikacji konwersji dla funkcji …printf

624

Funkcje …scanf

626

Cigi formatujce funkcji …scanf

627

Specyfikacje konwersji funkcji …scanf

628

Zmiany specyfikatorów konwersji w C99

631

Przykady dla funkcji scanf

631

Wykrywanie koca strumienia wejciowego i bdów

632

22.4.

Wejcie-wyjcie znakowe

635

Funkcje wyjcia

635

Funkcje wejcia

636

Kopiowanie pliku

637

22.5.

Wierszowe wejcie-wyjcie

638

Funkcje wyjcia

638

Funkcje wejcia

639

22.6.

Blokowe wejcie-wyjcie

640

22.7.

Pozycjonowanie w plikach

641

Modyfikowanie pliku rekordów bazy danych

643

22.8.

Funkcje wejcia-wyjcia w pamici

644

Funkcje wyjcia

645

Funkcje wejcia

646

23. OBSUGA LICZB I DANYCH ZNAKOWYCH

659

23.1.

Nagówek <float.h> — cechy typów zmiennoprzecinkowych 659

23.2.

Nagówek <limits.h> — rozmiary typów cakowitych

662

23.3.

Nagówek <math.h> — matematyka

664

Bdy

664

Funkcje trygonometryczne

665

Funkcje hiperboliczne

666

Funkcje wykadnicze i logarytmiczne

666

Funkcje potgowe

667

Najblisza liczba cakowita, warto bezwzgldna,

reszta z dzielenia

668

background image

Spis treci

15

23.4.

Nagówek <math.h> — matematyka (C99)

669

Standard zmiennoprzecinkowy IEEE

669

Typy

671

Makrodefinicje

671

Bdy

672

Funkcje

673

Makrodefinicje klasyfikujce

674

Funkcje trygonometryczne

675

Funkcje hiperboliczne

675

Funkcje wykadnicze i logarytmiczne

676

Funkcje potgowe i funkcje wartoci bezwzgldnej

677

Funkcje bdów i funkcje gamma

678

Funkcje zaokrglania

679

Funkcje reszty z dzielenia

680

Funkcja manipulacji

680

Funkcje maksimum, minimum i rónicy dodatniej

681

Zmiennoprzecinkowy iloczyn-suma

682

Makrodefinicje porówna

683

23.5.

Nagówek <ctype.h> — obsuga znaków

684

Funkcje klasyfikacji znaków

684

Test funkcji klasyfikacji znaków

685

Funkcje mapowania wielkoci liter

686

Test funkcji zmiany wielkoci liter

687

23.6.

Nagówek <string.h> — obsuga cigów znaków

687

Funkcje kopiujce

688

Funkcje czenia cigów

689

Funkcje porówna

690

Funkcje wyszukujce

691

Róne

695

24. OBSUGA BDÓW

699

24.1.

Nagówek <assert.h> — diagnostyka

700

24.2.

Nagówek <errno.h> — bdy

701

Funkcje perror i strerror

702

24.3.

Nagówek <signal.h> — obsuga sygnaów

703

Makrodefinicje sygnaów

704

Funkcja signal

704

Predefiniowane funkcje obsugi sygnaów

705

Funkcja raise

707

Testowanie mechanizmu sygnaów

707

24.4.

Nagówek <setjmp.h> — skoki nielokalne

708

Testowanie setjmp/longjmp

709

25. „MIDZYNARODÓWKA”

715

25.1.

Nagówek <locale.h> — rodowiska jzykowe

716

Kategorie

716

Funkcja setlocale

717

Funkcja localeconv

719

25.2.

Znaki wielobajtowe i znaki poszerzone

722

Znaki wielobajtowe

723

Znaki poszerzone

724

Unicode i uniwersalny zestaw znaków UCS

724

background image

16

Spis treci

Kodowanie Unicode

725

Funkcje konwersji znaków poszerzonych i wielobajtowych

727

Funkcje konwersji cigów znaków poszerzonych i wielobajtowych 729

25.3.

Dwuznaki i trójznaki

729

Trójznaki

730

Dwuznaki

731

Nagówek <iso646.h> — symbole alternatywne

731

25.4.

Uniwersalne nazwy znaków (C99)

732

25.5.

Nagówek <wchar.h> (C99) — dodatkowe narzdzia
dla znaków poszerzonych i wielobajtowych

733

Orientacja strumienia

734

Funkcje formatowanego wejcia-wyjcia

dla znaków poszerzonych

735

Funkcje wejcia-wyjcia dla znaków poszerzonych

737

Obsuga cigów znaków poszerzonych

738

Funkcja konwersji dat i godzin na cigi znaków poszerzonych

743

Dodatkowe funkcje konwersji znaków poszerzonych

i wielobajtowych

743

25.6.

Nagówek <wctype.h> (C99) — klasyfikacja
znaków poszerzonych

747

Funkcje klasyfikacji znaków poszerzonych

747

Rozszerzalne funkcje klasyfikacji znaków poszerzonych

748

Funkcje zmiany wielkoci liter dla znaków poszerzonych

749

Rozszerzalne funkcje zmiany wielkoci liter

znaków poszerzonych

750

26. RÓNE

755

26.1.

Nagówek <stdarg.h> — zmienna liczba argumentów

755

Wywoanie funkcji o zmiennej liczbie argumentów

758

Funkcje v…printf

758

Funkcje v…scanf

759

26.2.

Nagówek <stdlib.h> — inne narzdzia

760

Funkcje konwersji liczbowych

761

Testowanie funkcji konwersji liczbowych

762

Funkcje sekwencji pseudolosowych

764

Testowanie funkcji generowania liczb pseudolosowych

765

Komunikacja ze rodowiskiem wykonawczym

766

Wyszukiwanie i sortowanie

768

Okrelanie odlegoci

769

Funkcje arytmetyki liczb cakowitych

770

26.3.

Nagówek <time.h> — daty i godziny

771

Funkcje operujce na datach i godzinach

772

Funkcje konwersji dat i godzin

774

Wypisywanie daty i godziny

778

27. ROZSZERZONE OPERACJE MATEMATYCZNE W C99

787

27.1.

Nagówek <stdint.h> — typy cakowite

788

Typy nagówka <stdint.h>

788

Ograniczenia typów o okrelonym rozmiarze

790

Ograniczenia pozostaych typów cakowitych

790

Makrodefinicje dla staych cakowitych

791

background image

Spis treci

17

27.2.

Nagówek <inttypes.h> — konwersje typów cakowitych

792

Makrodefinicje dla specyfikatorów konwersji

792

Funkcje obsugi najszerszych typów

793

27.3.

Liczby zespolone (C99)

795

Definicja liczb zespolonych

795

Arytmetyka liczb zespolonych

797

Typy zespolone w C99

797

Operacje na wartociach zespolonych

798

Reguy konwersji dla typów zespolonych

798

27.4.

Nagówek <complex.h> (C99) — arytmetyka
liczb zespolonych

800

Makrodefinicje nagówka <complex.h>

800

CX_LIMITED_RANGE

801

Funkcje nagówka <complex.h>

802

Funkcje trygonometryczne

802

Funkcje hiperboliczne

803

Funkcje wykadnicze i logarytmiczne

804

Funkcje potgowe i funkcje wartoci bezwzgldnych

804

Inne

805

Szukanie pierwiastków równania kwadratowego

805

27.5.

Nagówek <tgmath.h> (C99) — matematyka bez typów

806

Makrodefinicje rozprowadzajce wywoania

funkcji matematycznych

807

Wywoania makrodefinicji rozprowadzajcych

807

27.6.

Nagówek <fenv.h> (C99)
— rodowisko zmiennoprzecinkowe

810

Stany i tryby jednostki zmiennoprzecinkowej

810

Makrodefinicje nagówka <fenv.h>

811

FENV_ACCESS

811

Funkcje wyjtków zmiennoprzecinkowych

813

Funkcje zaokrglania

814

Funkcje rodowiska

815

Dodatek A

Operatory jzyka C

819

Dodatek B

C99 kontra C89

821

Dodatek C

C89 kontra K&R

827

Dodatek D

Funkcje biblioteki standardowej

831

Dodatek E

Zestaw znaków ASCII

893

Bibliografia

895

Skorowidz

899

background image

69

3

Formatowanie
wejcia-wyjcia

W poszukiwaniu nieosigalnego na przeszkodzie staje tylko prostota.

Do najczciej wykorzystywanych funkcji bibliotecznych jzyka C nale printf
i scanf, suce do obsugi formatowanego wejcia i wyjcia programu. W tym
rozdziale przekonasz si o moliwociach tych funkcji, ale te o koniecznej ostro-
noci w ich stosowaniu. W podrozdziale 3.1 zajmiemy si funkcj printf. Gów-
nym zagadnieniem podrozdziau 3.2 bdzie funkcja scanf. W adnym z pod-
rozdziaów nie zgbimy jednak wszystkich detali — niektóre bd musiay
poczeka do rozdziau 22.

3.1.

Funkcja printf

Funkcja printf suy do wypisywania na wyjciu programu zawartoci cigu
znaków, okrelanego mianem cigu formatujcego, który moe zawiera sym-
bole zastpcze dla wartoci zmiennych wstawianych do cigu wypisywanego.
W wywoaniu funkcji printf musi si znajdowa cig formatujcy, uzupeniony
wartociami, które maj by podstawione w odpowiednie miejsca cigu na wyjciu
programu:

printf(

cig-formatujcy

,

wyraenie1

,

wyraenie1

,

);

Wartoci przekazywane do podstawienia do cigu formatujcego mog by sta-
ymi, zmiennymi albo caymi wyraeniami. Nie istnieje ograniczenie liczby war-
toci wypisywanych w ramach pojedynczego wywoania funkcji printf.

Cig formatujcy moe zawiera zarówno zwyczajne znaki drukowalne, jak

i tak zwane specyfikatory konwersji, rozpoczynajce si od znaku %. Specyfi-
kator konwersji to symbol zastpczy reprezentujcy warto, która ma zosta
wstawiona w dane miejsce cigu formatujcego, wraz z opisem sposobu wypisa-
nia wartoci. Informacje znajdujce si za znakiem % okrelaj sposób konwersji

background image

70

Rozdzia 3. Formatowanie wejcia-wyjcia

przekazanej wartoci z jej reprezentacji wewntrznej (binarnej) na reprezentacj
drukowan (znakow) — std pojcie „specyfikatora konwersji”. Na przykad
specyfikator konwersji %d mówi, e printf ma zamieni przekazan warto
typu int z jej reprezentacji binarnej na cig znaków kolejnych cyfr. Podobnie
specyfikator %f nakazuje zamian wartoci zmiennoprzecinkowej (typu float)
na znakow.

Zwyczajne znaki zawarte w cigu formatujcym s wypisywane bez modyfi-

kacji. Specyfikatory konwersji s natomiast zastpowane przez znakowe repre-
zentacje przekazanych wartoci. Wemy nastpujcy przykad:

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 wywoanie funkcji printf spowoduje wypisanie na wyjciu:

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

Zwyczajne znaki w cigu formatujcym zostay po prostu skopiowane na wyjcie.
Cztery specyfikatory konwersji zostay za zastpione przez odpowiednio repre-
zentowane wartoci zmiennych i, j, x i y (w tej kolejnoci).

Specyfikatory konwersji

Specyfikatory konwersji pozwalaj programistom zachowa du doz kontroli
nad wygldem (formatem) wypisywanych cigów i wartoci. Z drugiej strony
bywaj skomplikowane i trudne do ogarnicia. Istotnie, szczegóowe opisywanie
specyfikatorów konwersji byoby na tym wstpnym etapie omówienia przed-
wczesne. Zapoznamy si wic tylko z najwaniejszymi cechami i moliwociami
dawanymi przez specyfikatory.

W rozdziale 2. zauwaylimy, e specyfikator konwersji moe zawiera infor-

macje sterujce formatowaniem wartoci. W szczególnoci zastosowalimy spe-
cyfikator %.1f, aby ograniczy liczb wypisywanych cyfr po przecinku w war-
toci typu float. Ogólniej rzecz biorc, specyfikator konwersji moe przyj
posta %

m

.

pX

albo %-

m

.

pX

, gdzie

m

i

p

to stae cakowite, a

X

to litera. Wartoci

m

i

p

s opcjonalne. W przypadku nieobecnoci

p

nie stosuje si równie kropki

oddzielajcej

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

pominite, a

X

to f. Za to w %.2f

m

zostao pominite, a

p

wynosi 2.

Minimalna szeroko pola

m

okrela minimaln liczb znaków, jaka zostanie

wypisana na wyjciu przy wypisywaniu wartoci. Jeli wypisywana warto jest
w reprezentacji znakowej krótsza ni

m

znaków, zostanie wyrównana do minimal-

nej szerokoci pola i do prawej strony pola (innymi sowy, przed waciw war-
to wstawiona bdzie odpowiednia liczba spacji). Na przykad specyfikator %4d

background image

3.1. Funkcja printf

71

Standard nie wymaga od kompilatorów jzyka C sprawdzania, czy liczba specy-
fikatorów konwersji okrelona w cigu formatujcym odpowiada liczbie przeka-
zanych wartoci. Ponisze wywoanie funkcji printf posiada wicej specyfika-
torów konwersji ni wartoci do wypisania:

printf("%d %d\n", i);

/*** LE ***/

Funkcja printf wypisze poprawnie warto zmiennej i, a nastpnie wypisze
drug — oczekiwan, ale nieokrelon — warto liczbow. Podobnie kopotliwy
jest nadmiar wartoci w stosunku do specyfikatorów konwersji:

printf("%d\n", i, j);

/*** LE ***/

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

Kompilatory nie s te zobligowane do sprawdzania, czy specyfikatory kon-

wersji odpowiadaj typom przekazywanych wartoci. Jeli programista zastosuje
nieodpowiednie specyfikatory, program moe wypisa na wyjciu kompletne
bzdury. Wemy dla przykadu wywoanie funkcji printf, w którym zmienna i
typu int i zmienna x typu float zostan przekazane w zej kolejnoci:

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

/*** LE ***/

Poniewa funkcja printf realizuje wytyczne z cigu formatujcego, wypisze
na wyjciu warto float, a za ni warto int. Niestety, obie wartoci jako le
zinterpretowane bd niepoprawne.

spowoduje wypisanie wartoci 123 jako ·123 (w caym biecym rozdziale
w miejsce niewidocznych spacji ilustrujcych formatowanie wartoci bd wsta-
wiane znaki ·). Z kolei kiedy wypisywana warto bdzie dusza ni

m

znaków,

pole zostanie automatycznie poszerzone do szerokoci potrzebnej do zmieszczenia
wartoci: specyfikator %4d dla wartoci 12345 spowoduje wypisanie na wyjciu
12345

— bez ucinania cyfr. Znak minusa przed

m

wymusza wyrównanie wartoci

w polu do lewej strony: specyfikator %4d dla wartoci 123 da na wyjciu 123·.

Znaczenie specyfikatora precyzji

p

jest trudniejsze do opisania, poniewa jego

dziaanie jest zalene od

X

, czyli waciwego specyfikatora konwersji.

X

okrela

rodzaj konwersji do przeprowadzenia przed wypisaniem wartoci. Do najpopu-
larniejszych konwersji wartoci liczbowych nale:

Q

d

— konwersja wartoci cakowitej do postaci dziesitnej. W takim ukadzie

p

oznacza minimaln liczb cyfr do wypisania (w razie potrzeby przed wypi-

sywan liczb dopisywane s zera). Przy braku

p

uznaje si, e ma ono warto

1 (innymi sowy, %d jest tym samym co %.1d).

Q

e

— konwersja wartoci zmiennoprzecinkowej do postaci wykadnikowej

(w tzw. notacji naukowej). W takim ukadzie

p

okrela liczb cyfr do wypi-

sania po przecinku dziesitnym (warto domylna to 6). Dla

p

równego 0

cz po przecinku nie jest wywietlana w ogóle.

Q

f

— konwersja wartoci zmiennoprzecinkowej do postaci dziesitnej, bez

wykadnika. W tym ukadzie

p

ma takie samo znaczenie jak przy konwersji e.

background image

72

Rozdzia 3. Formatowanie wejcia-wyjcia

Q

g

— konwersja wartoci zmiennoprzecinkowej do postaci wykadnikowej albo

dziesitnej, zalenie od rozmiaru liczby. W tym ukadzie

p

oznacza maksy-

maln liczb cyfr znaczcych (nie cyfr po przecinku) do wypisania. Inaczej ni
przy konwersji f, konwersja g nie bdzie wypisywaa zer po prawej stronie
wartoci. Co wicej, jeli konwertowana warto nie ma cyfr po przecinku,
konwersja g nie wypisze take symbolu przecinka.

Specyfikator konwersji g jest przydatny zwaszcza do wypisywania wartoci, dla
których rozmiar reprezentacji nie da si ustali na etapie pisania programu, oraz
wartoci z bardzo szerokich dziedzin. Konwersja g dla niezbyt wielkich i niezbyt
maych wartoci bdzie owocowaa zapisem dziesitnym. Dla wartoci bardzo
maych i bardzo duych zastosowany bdzie zapis wykadnikowy, wymagajcy
mniejszej liczby znaków.

%d

, %e, %f i %g to bynajmniej nie wszystkie specyfikatory konwersji. Pozo-

stae bd stopniowo wprowadzane przy okazji omawiania kolejnych zagadnie.
Pena lista specyfikatorów wraz z objanieniem ich znaczenia i dziaania znajduje
si w podrozdziale 22.3.

Wykorzystanie printf do formatowania liczb

Poniszy program ilustruje sposób wykorzystania funkcji printf do wypisy-
wania wartoci cakowitych i zmiennoprzecinkowych w rozmaitych formatach:

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

#include <stdio.h>

int main(void)

{
int i;

float x;

i = 40;
x = 839.21f;

printf("|%d|%5d|%-5d|%5.3d|\n", i, i, i, i);
printf("|%10.3f|%10.3e|%-10g|\n", x, x, x);

return 0;

}

Znaki | w cigach formatujcych dla funkcji printf su jedynie do roz-

dzielenia wypisywanych wartoci i zilustrowania szerokoci pól, w których s
wypisywane. W przeciwiestwie do znaków % i \ znak | nie ma specjalnego zna-
czenia dla funkcji printf. Program wypisuje na wyjciu co takiego:

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

Spróbujmy przeanalizowa znaczenie i dziaanie specyfikatorów konwersji

wykorzystanych w tym programie:

specyfikatory

dla wartoci cakowitych

¥ 7.1

specyfikatory dla wartoci

zmiennoprzecinkowych

¥ 7.2

specyfikatory

dla wartoci znakowych

¥ 7.3

specyfikatory

dla cigów znaków

¥ 13.3

PROGRAM

tprintf.c

background image

3.1. Funkcja printf

73

Q

%d

— wypisanie i w zapisie dziesitnym przy jak najmniejszej liczbie znaków.

Q

%5d

— wypisanie wartoci i w zapisie dziesitnym w polu o szerokoci co

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

Q

%-5d

— wypisanie wartoci i w zapisie dziesitnym w polu o szerokoci co

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

Q

%5.3d

— wypisanie wartoci i w zapisie dziesitnym w polu o szerokoci

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

Q

%10.3f

— wypisanie wartoci x w zapisie dziesitnym z przecinkiem w polu

o szerokoci 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 uzupeniana trzema spacjami z lewej strony.

Q

%10.3e

— wypisanie wartoci x w zapisie wykadnikowym w polu o sze-

rokoci co najmniej 10 znaków, z trzema cyframi po przecinku. Tak zapisana
warto x zajmuje 9 znaków, wic jest uzupeniona spacj z lewej strony.

Q

%-10g

— wypisanie wartoci x w zapisie wykadnikowym albo dziesitnym

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

Znaki sterujce

Symbol \n, wykorzystywany ju w poprzednich cigach formatujcych, to przy-
kad tak zwanego znaku sterujcego (ang. escape sequence). Znaki sterujce
pozwalaj na osadzanie w cigach znaków, które zapisane inaczej byyby kopo-
tliwe dla kompilatora. Dotyczy to przede wszystkim znaków niedrukowalnych
terminali znakowych oraz znaków posiadajcych specjalne znaczenie dla samego
kompilatora (jak "). Kompletn list znaków sterujcych zamiecimy póniej, na
razie wystarczy wykaz najwaniejszych:

\a

sygna dzwonka,

\b

kasowanie poprzedniego znaku (ang. backspace),

\n

nowy wiersz,

\t

tabulator poziomy.

Takie symbole w cigu formatujcym reprezentuj czynnoci do wykonania w cza-
sie wypisywania cigu na wyjciu programu. Otó wypisanie \a spowoduje na
wikszoci maszyn wygenerowanie dwiku brzczyka terminala; wypisanie \b

znaki sterujce

¥ 7.3

background image

74

Rozdzia 3. Formatowanie wejcia-wyjcia

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

Cig formatujcy moe zawiera dowoln liczb znaków sterujcych. Spójrzmy

na kolejny przykad wywoania printf z cigiem formatujcym zawierajcym
cznie sze znaków sterujcych:

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

Wykonanie tej instrukcji spowoduje wypisanie na wyjciu dwóch wierszy:

Towar Cena Data
jed. zakupu

Innym popularnym znakiem sterujcym jest \", który reprezentuje w cigu

znak podwójnego cudzysowu ". Poniewa sam znak " oznacza pocztek albo
koniec literau napisowego, nie moe w takiej postaci pojawi si wewntrz lite-
rau — musi zosta oznaczony jako znak sterujcy znakiem ukonika. Oto przykad:

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

Taka instrukcja spowoduje wypisanie na wyjciu komunikatu:

"Ahoj!"

Podobna sytuacja dotyczy znaku lewego ukonika. Jeli zechcemy umieci

taki znak w wypisywanym cigu, nie moemy go wstawi wprost do literau napi-
sowego, bo kompilator zakada, e znak ukonika jest zapowiedzi znaku steru-
jcego. Aby wypisa znak \, trzeba go równie poprzedzi znakiem \, a wic
wstawi do cigu par \\:

printf("\\");

/* wypisuje na wyjciu pojedynczy znak \ */

3.2.

Funkcja scanf

Tak jak funkcja printf suy do formatowania wyjcia programu, tak funkcja
scanf

obsuguje formatowane wejcie. Cig formatujcy dla funkcji scanf rów-

nie moe zawiera zwyczajne znaki i specyfikatory konwersji. Konwersje obsu-
giwane przez funkcj scanf pokrywaj si z grubsza z konwersjami funkcji
printf

.

W wielu przypadkach cig formatujcy funkcji scanf zawiera wycznie

specyfikatory konwersji, jak w poniszym przykadzie:

int i, j;
float x, y;

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

Zaómy, e uytkownik wprowadza na wejcie programu nastpujcy wiersz:

1 -20 .3 -4.0e3

background image

3.2. Funkcja scanf

75

Funkcja scanf wczyta taki wiersz i rozpocznie stosowanie specyfikatorów kon-
wersji i podstawianie uzyskanych wartoci pod zmienne przekazane w wywoaniu:
1

pod i, -20 pod j, 0.3 pod x i -4000.0 pod y. Takie „upakowane” cigi

sterujce s dla funkcji scanf typowe. W przypadku funkcji printf czciej
stosuje si rozmaite „dekoracje” i napisy objaniajce, otaczajce wyprowadzane
wartoci.

Funkcja scanf (tak jak printf zreszt) zastawia na niewiadomych i nie-

ostronych uytkowników kilka wnyków. Programista stosujcy funkcj scanf
musi starannie sprawdza, czy liczba specyfikatorów konwersji odpowiada liczbie
zmiennych przekazanych w wywoaniu i czy poszczególne konwersje odpowiadaj
typom przekazanych zmiennych — podobnie jak w przypadku printf, kom-
pilator nie ma obowizku przeprowadzania takiej kontroli i wykrywania ewentu-
alnych niezgodnoci. Kolejna puapka czai si w znaku &, który zazwyczaj poprze-
dza kad zmienn przekazywan do scanf. Znak & jest zazwyczaj (cho nie
zawsze) konieczny i to programista ma obowizek pamita o jego stosowaniu.

Pominicie symbolu & przy zmiennej przekazywanej do funkcji scanf prowadzi
do nieprzewidywalnych wyników dziaania programu. Potencjalnie s to efekty
katastrofalne. Najczciej taki bd prowadzi do wyoenia si programu. W naj-
lepszym przypadku podstawienie wartoci pod zmienn bdzie nieskuteczne —
zmienna zachowa swoj poprzedni warto (co nie oznacza, e bdzie miaa jak-
kolwiek okrelon warto, jeli np. nie zostaa zainicjalizowana!). Pominicie
znaku & jest niestety czstym bdem. Niektóre kompilatory wykrywaj taki bd
i generuj komunikat z ostrzeeniem w rodzaju „argument nie jest wskanikiem”
(o wskanikach powiemy sobie w rozdziale 11.; to wanie znak & tworzy wska-
nik do zmiennej). Ostrzeenia zwizane z wywoaniami funkcji scanf trzeba trak-
towa bardzo powanie.

Wywoania funkcji scanf s efektywnym, ale potencjalnie niebezpiecznym

sposobem wczytywania danych do programów. Wielu zawodowych programi-
stów C unika funkcji scanf. Wol oni wczyta do programu cao danych
wejciowych w postaci znakowej i dopiero póniej zamieni j na oczekiwane
wartoci liczbowe. My natomiast bdziemy korzysta ze scanf cakiem sporo,
zwaszcza w pocztkowych rozdziaach ksiki — jest to zwyczajnie najprostszy
sposób wczytywania wartoci liczbowych do programu. Trzeba tylko mie wia-
domo, e wiele programów zachowa si niepoprawnie, kiedy uytkownik wpro-
wadzi do nich nieodpowiednie dane wejciowe. Wkrótce si przekonamy, e mona
zreszt skutecznie sprawdza, czy funkcji scanf udao si wczyta z wejcia
oczekiwane dane (a jeli nie, odpowiednio zareagowa, zamiast brn dalej w pro-
gram z niepoprawnymi danymi). Takie sprawdziany s jednak mao zasadne
w zakresie pocztkowych przykadów prezentowanych w ksice — nadmiernie
rozbudowayby program, który ma przecie przede wszystkim ilustrowa biece
zagadnienie.

wykrywanie bdów

w scanf

¥ 22.3

background image

76

Rozdzia 3. Formatowanie wejcia-wyjcia

Dziaanie funkcji scanf

Funkcja scanf robi w istocie znacznie wicej, ni dotychczas powiedziano. Jest to
w zasadzie mechanizm dopasowywania wzorców w cigach znaków, który pró-
buje pogrupowa znaki wejciowe i dopasowa je do specyfikatorów konwersji.

Dziaaniem funkcji scanf sterujemy za pomoc cigu formatujcego. Funkcja

scanf

analizuje zawarto tego cigu, od lewej do prawej strony. Dla kadego

napotkanego w cigu specyfikatora konwersji próbuje w cigu wejciowym pro-
gramu zlokalizowa warto odpowiedniego typu. W czasie tego wyszukiwania
automatycznie pomija znaki odstpów. Znaleziony podcig pasujcy do specyfi-
katora konwersji jest wczytywany a do miejsca, w którym wystpi znak niepasu-
jcy do wymaga konwersji. Jeli udao si wczyta taki podcig, funkcja scanf
dokonuje konwersji i przechodzi do analizy reszty cigu formatujcego. Pierwsze
nieudane dopasowanie specyfikatora konwersji do danych wczytywanych z wej-
cia koczy dziaanie caej funkcji, bez rozpatrywania reszty cigu formatujcego
(i bez uwzgldniania reszty danych wejciowych).

W toku poszukiwania pierwszego znaku liczby funkcja scanf ignoruje

wszystkie znaki odstpów (spacje, znaki tabulacji poziomej i pionowej, znaki
wysuwu formularza i znaki nowego wiersza). Dziki temu na wejciu dane mona
podawa w jednym wierszu albo rozproszy je pomidzy rónymi wierszami. Przy
danym wywoaniu funkcji scanf:

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

uytkownik moe wprowadzi na wejcie np. trzy wiersze danych:

1
-20 .3

-4.0e3

dla funkcji scanf wejcie jest widoczne jako cigy strumie znaków:

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

(w celu uwidocznienia znaków odstpów zastosowalimy znak · dla spacji i znak
¤

dla nowego wiersza). Poniewa funkcja scanf pomija znaki odstpów i szuka

przede wszystkim pocztku liczby i podcigu znaków nadajcych si do konwersji,
takie dane wejciowe mog by za jednym zamachem wczytane przez funkcj
scanf

. Poniszy schemat ilustruje dziaanie funkcji scanf. Znak p oznacza tu

pomijanie biecego znaku wejcia, znak w oznacza wczytywanie znaków do bie-
cej konwersji:

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

ppwpwwwpppwwppppwwwwww

Ostatni znak strumienia wejciowego, czyli pierwszy znak za ostatni skonwerto-
wan grup znaków, jest przez funkcj scanf „podgldany”, ale nie jest wczy-
tywany. Zostanie on wczytany i pominity bd skonwertowany przy nastpnym
wywoaniu scanf.

Wedug jakich regu scanf rozpoznaje liczb cakowit albo zmiennoprze-

cinkow w cigu danych wejciowych? Otó kiedy scanf ma wczyta liczb
cakowit, szuka w cigu wejciowym znaku cyfry, ewentualnie znaku + albo -.

background image

3.2. Funkcja scanf

77

Po znalezieniu takiego znaku wczytuje wszystkie kolejne znaki a do napotkania
znaku niebdcego cyfr. Natomiast w przypadku liczby zmiennoprzecinkowej
scanf

szuka:

znaku + albo - (opcjonalnie), a za nim

cigu cyfr (zawierajcego ewentualnie znak przecinka dziesitnego), a za nim

cigu wykadnika (opcjonalnie); cig wykadnika skada si z litery e (albo E),

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

W przypadku funkcji scanf konwersje %e, %f i %g mona stosowa zamiennie —
wszystkie trzy reprezentuj te same reguy dopasowania wartoci zmiennoprze-
cinkowej.

Kiedy scanf napotyka znak niezgodny z regu dopasowania dla biecej kon-

wersji, znak ten jest „odkadany” z powrotem do strumienia wejciowego, aby by
dostpny przy obsudze nastpnych specyfikatorów konwersji albo dla kolejnych
wywoa scanf wczytujcych kolejne wartoci. Wemy na przykad nastpujce
(niewtpliwie patologiczne) rozmieszczenie naszych czterech liczb wejciowych:

1-20.3-4.0e3¤

Zastosujemy takie same wywoanie scanf jak poprzednio:

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

Funkcja scanf bdzie przetwarza taki cig wejciowy nastpujco:

Q

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

Q

Bieca konwersja: %d. Pierwszy niepusty znak wejcia to - (dozwolony);
kolejne znaki to 2, 0 i . (kropka). Liczba cakowita nie moe zawiera kropki,
wic znak jest odkadany z powrotem do strumienia wejciowego, a funkcja
podstawia pod kolejn zmienn (j) warto –20.

Q

Bieca konwersja: %f. Pierwszy niepusty znak wejcia to . (dozwolony);
kolejne znaki to 3 i - (minus). Liczba zmiennoprzecinkowa nie zawiera znaku
-

po znaku cyfry, wic znak - jest odkadany z powrotem do strumienia wej-

ciowego, a funkcja podstawia pod kolejn zmienn (x) warto 0.3.

Q

Bieca konwersja: %f. Pierwszy niepusty znak wejcia to - (dozwolony);
kolejne znaki to 4, ., 0, e, 3 i ¤ (nowy wiersz). Liczba zmiennoprzecinkowa
nie moe zawiera znaku nowego wiersza, wic znak jest odkadany z powro-
tem do strumienia wejciowego, a funkcja podstawia pod kolejn zmienn
(y) warto –4.0×10

3

.

W tym przykadzie funkcja scanf moga skutecznie dopasowa wszystkie kolejne
specyfikatory konwersji do cigu wejciowego. Poniewa znak nowego wiersza
nie zosta „zuyty”, bdzie pierwszym znakiem wczytywanym przy nastpnym
wywoaniu scanf.

background image

78

Rozdzia 3. Formatowanie wejcia-wyjcia

Zwyke znaki w cigu formatujcym funkcji scanf

Zasad dopasowywania wzorców do podcigów strumienia wejciowego mona
rozszerzy, zapisujc cig formatujcy zawierajcy poza specyfikatorami konwer-
sji równie zwyke napisy. W takim przypadku pomidzy specyfikatorami kon-
wersji funkcja scanf bdzie porównywaa kolejne znaki wejcia ze znakami
cigu formatujcego. Porównanie takie odbywa si rónie, zalenie od tego, czy
cig formatujcy zawiera znaki odstpów:

Q

Znaki odstpów w cigu formatujcym. Kiedy w cigu formatujcym znaj-
duj si znaki odstpu, funkcja scanf bdzie w strumieniu wejciowym wczy-
tywaa kolejne znaki tak dugo, a napotka pierwszy znak niebdcy znakiem
odstpu (ten znak zostanie „odoony” z powrotem do strumienia wejciowego).
Liczba znaków odstpu w cigu formatujcym nie musi dokadnie odpowiada
liczbie takich znaków w cigu wejciowym. Pojedynczy znak odstpu w cigu
formatujcym zostanie dopasowany do dowolnie dugiego podcigu takich
znaków w cigu wejciowym (co wicej, obecno znaku odstpu w cigu
formatujcym nie wymusza obecnoci takiego znaku w cigu wejciowym —
znak odstpu w cigu formatujcym odpowiada dowolnie dugiemu podcigowi
takich znaków w cigu wejciowym, a wic równie podcigowi zerowemu).

Q

Pozostae znaki. Kiedy w cigu formatujcym znajduje si znak nienalecy
do specyfikatorów konwersji i niebdcy znakiem odstpu, funkcja scanf
porównuje go wprost z nastpnym znakiem wejcia. Jeli znaki s zgodne,
funkcja przechodzi do przetwarzania nastpnego znaku cigu wejciowego.
Przy braku zgodnoci funkcja odkada niepasujcy znak z powrotem do cigu
wejciowego i przerywa dalsze przetwarzanie cigu formatujcego.

Dla przykadu niech funkcja scanf otrzyma cig formatujcy w postaci

"%d/%d"

. Jeli na wejciu programu pojawi si cig:

·5/·96

funkcja scanf pominie pierwszy znak odstpu, szukajc liczby cakowitej, nastp-
nie dopasuje do %d podcig 5, dopasuje bezporednio znak /, pominie spacj
w poszukiwaniu kolejnej liczby cakowitej, a póniej dopasuje do %d podcig 96.
Ale jeli na wejciu pojawi si:

·5·/·96

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

background image

3.2. Funkcja scanf

79

Skutki mylenia printf ze scanf

Wywoania funkcji printf i scanf bywaj bardzo podobne, ale w ich dziaa-
niu zachodz daleko idce rónice. Zignorowanie tych rónic moe by bardzo nie-
bezpieczne dla dziaania programu.

Jedn z czstych pomyek jest umieszczenie & przed zmienn w wywoaniu

funkcji printf:

printf("%d %d\n", &i, &j);

/*** LE ***/

Na szczcie taka omyka jest stosunkowo prosta do wytropienia — w toku
dziaania programu funkcja printf wywietli „mieci” zamiast oczekiwanych
wartoci i i j.

Poniewa funkcja scanf normalnie pomija znaki odstpów przy poszuki-

waniu podcigu do dopasowania do wartoci liczbowej, rzadko kiedy pojawia si
potrzeba umieszczania w cigu formatujcym scanf czegokolwiek poza specy-
fikatorami konwersji. Nieuprawnione zaoenie, e cig formatujcy scanf
powinien cile odpowiada analogicznemu cigowi formatujcemu printf —
to kolejny czsty bd — moe doprowadzi do niepoprawnego dziaania funkcji
scanf

. Zobaczmy, co si stanie, jeli w programie znajdzie si nastpujca in-

strukcja:

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

Funkcja scanf bdzie najpierw szuka podcigu wartoci typu int i wpisze t
warto pod zmienn i. Potem bdzie próbowaa dopasowa w cigu wejciowym
znak przecinka. Jeli w cigu wejciowym zamiast przecinka pojawi si spacja,
dziaanie funkcji zostanie przerwane i zmienna j nie otrzyma oczekiwanej wartoci.

Cigi formatujce dla funkcji printf czsto kocz si znakiem sterujcym \n,
wymuszajcym wstawienie do wyjcia nowego wiersza. Taki sam znak na kocu
cigu formatujcego scanf to zazwyczaj zy pomys. Dla scanf znak nowego
wiersza w cigu formatujcym jest równowany ze znakiem spacji; pomija go,
szukajc nastpnego znaku niebdcego znakiem odstpu. Jeli na przykad cig
formatujcy ma posta "%d\n", scanf pominie ewentualne pocztkowe znaki
odstpu, wczyta warto cakowit, a nastpnie bdzie w cigu wejciowym szuka
nastpnego znaku innego ni odstp. W przypadku programu interaktywnego moe
to doprowadzi do „zawieszenia” programu do czasu, kiedy uytkownik wprowa-
dzi na wejcie znak inny ni odstp.

Dodawanie uamków

Aby zilustrowa zdolno funkcji scanf do dopasowywania wzorców w cigach
znaków, wemiemy na warsztat problem wczytania uamka wprowadzanego przez
uytkownika. Uamki s zwyczajowo reprezentowane przez zapis licznik/mianownik.
Zamiast zmusza uytkownika do nieintuicyjnego wprowadzania uamka jako
dwóch osobnych liczb cakowitych, dziki funkcji scanf pozwolimy mu wpro-
wadzi uamek w klasycznej postaci. Oto program ilustrujcy t technik przy
dodawaniu dwóch uamków:

PROGRAM

background image

80

Rozdzia 3. Formatowanie wejcia-wyjcia

/* Dodawanie dwóch uamków zwykych */

#include <stdio.h>

int main(void)

{

int num1, denom1, num2, denom2, result_num, result_denom;

printf("Podaj pierwszy uamek: ");

scanf("%d/%d", &num1, &denom1);

printf("Podaj drugi uamek: ");

scanf("%d/%d", &num2, &denom2);

result_num = num1 * denom2 + num2 * denom1;

result_denom = denom1 * denom2;

printf("Suma uamków wynosi %d/%d\n",

result_num, result_denom);

return 0;

}

Przykadowa sesja z takim programem wygldaa tak:

Podaj pierwszy uamek: 5/6
Podaj drugi uamek: 3/4

Suma uamków wynosi 38/24

Zauwamy, e program nie przewiduje znormalizowania wynikowego uamka.

Pytania i odpowiedzi

*P:

Widywaem specyfikatory w postaci %i, przeznaczone do wczytywania i wy-
pisywania liczb cakowitych. Czym róni si od
%d (s. 71)?

O:

W cigu formatujcym funkcji printf nie ma pomidzy nimi adnej rónicy.
Za to w funkcji scanf %d dopasuje wycznie liczb cakowit zapisan w postaci
dziesitnej, natomiast %i moe dopasowa take liczby cakowite zapisane
w innych systemach liczbowych (przy innych podstawach), na przykad liczby
ósemkowe czy szesnastkowe. Otó jeli cig wejciowy zawiera przedrostek
0

(jak w 056), przy konwersji %i taki cig zostanie potraktowany jako ósemkowy.

Przedrostek 0x albo 0X (jak w 0x56) przy konwersji %i spowoduje konwersj
liczby jako wartoci szesnastkowej. Zastosowanie %i zamiast %d do wczytywania
liczb moe da zaskakujce wyniki, jeli uytkownik omykowo wprowadzi 0 przed
waciw liczb; z tego powodu zaleca si trzymanie si konwersji %d.

P: Skoro

printf

traktuje znak % jako pocztek specyfikatora konwersji, jak

mona wypisa na wyjciu programu znak procenta?

O: Funkcja

printf

wypisze na wyjciu znak % tam, gdzie w cigu formatujcym

pojawi si para przylegajcych znaków procenta (%%). Na przykad instrukcja:

addfrac.c

liczby ósemkowe

¥ 7.1

liczby szesnastkowe

¥ 7.1

background image

Pytania i odpowiedzi

81

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

moe spowodowa wypisanie:

Zysk netto: 10%

P: Znak

sterujcy

\t

ma wymusi na printf przesunicie kursora do nastp-

nej pozycji tabulacji. Skd wiadomo, jaka jest szeroko tabulatora (s. 74)?

O:

Nie wiadomo. Efekt wypisania na wyjciu znaku \t nie jest cile zdefiniowany
w standardzie jzyka C. Jest on zaleny od reakcji systemu operacyjnego na 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, jeli bdzie miaa wczyta liczb, a uytkownik
wprowadzi cig nieliczbowy?

O:

Wyjanimy to na przykadzie:

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

Zaómy, e uytkownik wprowadzi poprawn warto liczbow, za któr umieci
znaki inne ni cyfry:

Podaj liczb: 23bla

W takim przypadku funkcja scanf wczyta znaki 2 oraz 3 i w zmiennej i umieci
warto 23. Reszta znaków (bla) bdzie w strumieniu wejciowym czekaa na
kolejne wywoanie scanf (albo innej funkcji obsugujcej wejcie). Jeli natomiast
uytkownik wprowadzi cig niepoprawny od pierwszego znaku:

Podaj liczb: bla

zmienna i nie otrzyma wartoci wczytywanej z wejcia programu, a w strumieniu
wejciowym cig bla poczeka na kolejne wywoania funkcji wejcia.

Co mona zrobi w takich smutnych przypadkach? Póniej dowiesz si, jak

sprawdzi skuteczno wywoania funkcji scanf. Jeli wywoanie bdzie nie-
skuteczne, moemy zakoczy program albo spróbowa naprawi sytuacj, na przy-
kad odrzucajc niepoprawne wejcie i ponownie proszc uytkownika o wpro-
wadzenie danych (sposoby odrzucania danych wejciowych bd omawiane
w sekcji pyta i odpowiedzi rozdziau 22.).

P:

Nie rozumiem, w jaki sposób funkcja scanf „odkada” znak z powrotem
do cigu wejciowego i potem ponownie wczytuje go z wejcia (s. 77)?

O:

Okazuje si, e program nie wczytuje danych wejciowych bezporednio przy ich
wprowadzaniu. Cig wejciowy programu jest przechowywany w ukrytym buforze,
do którego odwouje si funkcja scanf. W takim ukadzie funkcja scanf moe
„odoy” znak z powrotem do bufora. Buforowanie wejcia bdzie omawiane
w rozdziale 22.

P: Jak

zadziaa

funkcja

scanf

, kiedy uytkownik umieci pomidzy liczbami

znaki przestankowe (np. przecinki)?

O:

Zobaczmy to na prostym przykadzie. Zaómy, e zamierzamy wczyta funkcj
scanf

par liczb:

wykrywanie bdów

w scanf

¥ 22.3

background image

82

Rozdzia 3. Formatowanie wejcia-wyjcia

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

Jeli uytkownik wprowadzi:

4,28

funkcja scanf wczyta znak 4 i podstawi warto 4 do zmiennej i. W poszuki-
waniu nastpnego podcigu reprezentujcego liczb cakowit znajdzie przecinek.
Poniewa przecinek nie moe rozpoczyna zapisu liczby, funkcja przerwie dzia-
anie. Sam przecinek i druga wprowadzona liczba zostan do dyspozycji nastp-
nych wywoa scanf.

Oczywicie mona temu atwo zaradzi, instruujc uytkownika co do ocze-

kiwanego formatu danych wejciowych, np. nakazujc mu zawsze oddzielanie liczb
przecinkiem:

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

wiczenia

1.

Co pojawi si na wyjciu po wykonaniu poniszych wywoa printf?

(a) printf("%6d,%d4", 86, 1040);

(b) printf("%12.5e", 86, 30.253);

(c) printf("%.4f", 83.162);

(d) printf("%-6.2g", .0000009979);

2.

Napisz wywoanie funkcji printf wywietlajcej warto zmiennej typu float w nast-
pujcych formatach:

(a) w zapisie wykadnikowym, wyrównan do lewej w polu o szerokoci 8, z jedn cyfr

po przecinku;

(b) w zapisie wykadnikowym, wyrównan do prawej w polu o szerokoci 10, z szecioma

cyframi po przecinku;

(c) w zapisie dziesitnym z przecinkiem, wyrównan do lewej w polu o szerokoci 8, z trzema

cyframi po przecinku;

(d) w zapisie dziesitnym z przecinkiem, wyrównan do prawej w polu o szerokoci 6,

bez cyfr po przecinku.

3.

Dla kadej z poniszych par cigów formatujcych scanf wska, czy oba cigi pary s sobie
równowane, czy nie. Jeli nie s, wska rónic w ich dziaaniu.

(a) "%d"

" %d"

(b) "%d-%d-%d"

" %d -%d -%d"

(c) "%f"

"%f "

(d) "%f,%f"

"%f, %f"

4.

Wemy nastpujce wywoanie funkcji scanf:

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

od odpowiedzi narzucajcej si na pierwszy rzut oka. Przeczytaj pytanie uwanie, dokadnie
przeled kod, w razie potrzeby powtórz lektur odpowiedniego podrozdziau. Powodze-
nia! — przyp. autora.

Podrozdzia 3.1

Podrozdzia 3.2

*

background image

Zadania programistyczne

83

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

Jakie bd wartoci zmiennych i, x i j po wykonaniu wywoania (zakadamy, e zmienne
i

i j s zmiennymi typu int, a x jest zmienn typu float), jeli uytkownik wprowadzi:

10.3 5 6

5.

Wemy nastpujce wywoanie funkcji scanf:

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

Jakie bd wartoci zmiennych x, i i y po wykonaniu wywoania (zakadamy, e zmienne
x

i y s zmiennymi typu float, a i jest zmienn typu int), jeli uytkownik wprowadzi:

12.3 45.6 789

6.

Poka, jak mona przerobi program addfrac.c z podrozdziau 3.2, aby uytkownik móg
wprowadza uamki ze spacjami wokó znaku dzielenia /.

Zadania programistyczne

1.

Napisz program, który przyjmuje na wejcie dat w postaci

dd/mm/rrrr

, a nastpnie

wypisuje j w formacie

rrrrmmdd

:

Podaj dat (dd/mm/rrrr): 17/2/2011
Podae dat 20110217

2.

Napisz program formatujcy informacje o produkcie wprowadzone przez uytkownika. Sesja
z programem powinna wyglda tak:

Podaj numer towaru: 583
Podaj cen jednostkow: 13.5
Podaj dat zakupu (dd/mm/rrrr): 24/10/2010

Towar Cena Data

jed. zakupu
583 $ 13.50 24/10/2010

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

3.

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

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

Identyfikator grupy: 0

*

background image

84

Rozdzia 3. Formatowanie wejcia-wyjcia

Kod wydawcy: 393

Numer publikacji: 97950
Suma kontrolna: 3

Uwaga: Liczba cyfr w kadej z grup moe by róna. Nie mona zaoy, e grupy maj
akurat takie rozmiary jak w tym przykadzie. Przetestuj program na innych, prawdziwych
numerach ISBN (znajdziesz je na okadkach ksiek i na stronach z informacjami o wydaniu).

4.

Napisz program, który zapyta uytkownika o numer telefoniczny w formacie (

xx

)

xxx

-

xxxx

, a potem wywietli ten numer w zapisie 0-

xx

xxx

-

xx

-

xx

:

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

5.

Napisz program, który bdzie monitowa, aby uytkownik wprowadzi liczby od 1 do 16
(w dowolnej kolejnoci), potem wywietli te liczby czwórkami (w postaci macierzy 4×4),
a nastpnie wypisze sumy w wierszach, kolumnach i po przektnych:

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

16 3 2 13
5 10 11 8

9 6 7 12

4 15 14 1

Sumy w wierszach: 34 34 34 34

Sumy w kolumnach: 34 34 34 34

Sumy po przektnych: 34 34

Jeli sumy w wierszach, kolumnach i po przektnych s identyczne (jak w przykadzie),
to takie liczby stanowi tak zwany magiczny kwadrat. Ten magiczny kwadrat pojawi
si w 1514 roku na rycinie artysty i matematyka Albrechta Dürera (zauwa, e rodkowe
liczby w ostatnim wierszu skadaj si na dat ryciny).

6.

Przerób program addfrac.c z podrozdziau 3.2 tak, aby uytkownik za jednym zamachem
wprowadza oba uamki oddzielone znakiem +:

Podaj dwa uamki oddzielone znakiem plusa: 5/6+3/4
Suma uamków wynosi 38/24

background image

85

4

Wyraenia

Uywanie kalkulatorka to jeszcze nie programowanie,

a ju nie matematyka.

Jedn z waniejszych cech jzyka C jest nacisk, jaki jest w nim kadziony na wyra-
enia — inaczej „wzory”, które mówi o sposobie obliczania wartoci. Wyraenia
s tu waniejsze ni instrukcje. Najprostsze wyraenia to zmienne i stae programu.
Zmienna reprezentuje warto, która jest obliczana w czasie dziaania programu
poprzez pobranie wartoci z pamici skojarzonej ze zmienn. Staa reprezentuje
warto znan ju w czasie kompilacji. Bardziej rozbudowane wyraenia obejmuj
operandy i operatory (przy czym operatory same w sobie s równie wyraeniami).
W wyraeniu a + (b * c) widzimy zastosowanie operatora + do operandów
a

oraz (b + c), natomiast obie strony operatora + (oba operandy) s penopraw-

nymi wyraeniami.

Operatory s podstawowymi narzdziami budowania wyrae, a jzyk C

posiada bardzo bogaty zbiór operatorów. Przede wszystkim C obsuguje podsta-
wowe operatory obecne w wikszoci innych jzyków programowania:

Q

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

Q

Operatory relacji do obliczania wartoci wyrae logicznych, takich jak „i jest
wiksze od 0”.

Q

Operatory logiczne do budowania warunków logicznych, jak „i jest wiksze
od 0 i jest mniejsze ni 10”.

Na tym jednak nie koniec. W jzyku C mamy do dyspozycji dziesitki innych
operatorów. Jest ich tak wiele, e bdziemy zmuszeni do ich stopniowego wpro-
wadzania na przestrzeni a dwudziestu rozdziaów tej ksiki. Opanowanie takiej
liczby operatorów wydaje si zadaniem niepomiernym, ale w istocie wikszo
z nich ma zasadnicze znaczenie dla efektywnoci programowania w jzyku C.

W tym rozdziale zajmiemy si jednymi z najwaniejszych operatorów jzyka C,

mianowicie wemiemy na warsztat operatory arytmetyczne (podrozdzia 4.1), ope-
ratory przypisania (podrozdzia 4.2) oraz operatory inkrementacji i dekrementacji

background image

86

Rozdzia 4. Wyraenia

(podrozdzia 4.3). W podrozdziale 4.1 zajmiemy si te pierwszestwem i cznoci
operatorów — te cechy operatorów s niezwykle istotne dla poprawnoci wyrae
obejmujcych wicej ni jeden operator. W podrozdziale 4.4 opisany zostanie spo-
sób, w jaki nastpuje obliczanie wyrae w jzyku C. Wreszcie w podrozdziale 4.5
zajmiemy si tak zwan instrukcj wyraeniow — konstrukcj sprawiajc,
e dowolne wyraenie moe odgrywa rol instrukcji w programie.

4.1.

Operatory arytmetyczne

Operatory arytmetyczne — czyli operatory realizujce operacje dodawania,
odejmowania, mnoenia i dzielenia — to istne woy robocze w wikszoci jzyków
programowania. C nie jest tu wyjtkiem. List operatorów arytmetycznych jzyka
C wymienia tabela 4.1.

Z jednym operandem

Z dwoma operandami

Addytywne

Multiplikatywne

Tabela 4.1.

Operatory

arytmetyczne

+

zachowanie znaku liczby

+

dodawanie

*

mnoenie

-

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 dziaania. W rzeczy samej w spe-
cyfikacji K&R w ogóle go nie uwzgldniono. Suy gównie do zaznaczenia, e
staa liczbowa jest dodatnia.

Operatory dwuargumentowe nie powinny budzi wtpliwoci. Moe poza ope-

ratorem %, który oblicza reszt z dzielenia cakowitego. Warto wyraenia i %
j

jest to reszta z cakowitego dzielenia i przez j. Na przykad wartoci wyrae-

nia 10 % 3 jest 1, a wyraenie 12 % 4 ma warto 0.

Operatory dwuargumentowe z tabeli 4.1 — z wyjtkiem operatora % — mog

by stosowane zarówno do operandów bdcych liczbami cakowitymi, jak i do
operandów zmiennoprzecinkowych; dopuszczalne jest te mieszanie typów operan-
dów. Kiedy w zasigu dziaania operatora wystpuj operandy typu int i float,
wynikiem dziaania operatora jest warto typu float. Dlatego wyraenie 9 +
2.5f

daje 11,5, a 6.7f / 2 to 3.35.

Przy stosowaniu operatorów / i % naley zachowa pewn ostrono:

Q

Operator dzielenia / moe dawa nieoczekiwane wyniki. Otó kiedy oba ope-
randy s wartociami cakowitymi, operator / obcina iloraz do najbliszej mniej-
szej wartoci cakowitej. Dlatego wyraenie 1 / 2 da warto 0.

background image

4.1. Operatory arytmetyczne

87

Q

Operator % wymaga, aby operandy byy wartociami cakowitymi. Jeli któ-
rykolwiek z operandów % jest wartoci inn ni cakowita, program nie da
si skompilowa.

Q

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

Q

Najwiksze puapki czaj si przy stosowaniu operatorów / i % z operandami
ujemnymi. Standard C89 stwierdza, e kiedy którykolwiek z operandów jest
ujemny, wynik dzielenia moe zosta zaokrglony w gór albo w dó (np. war-
toci wyraenia -9 / 7 moe by albo –1, albo –2). Równie kiedy ope-
rand i albo j wyraenia i % j jest ujemny, wedug standardu C89 wynik
dziaania operatora jest zaleny od implementacji (np. wyraenie -9 % 7
moe da warto –2 albo 5). Z kolei w C99 wynik dzielenia cakowitego jest
zawsze zaokrglany w kierunku zera (-9 / 7 da wynik –1), a warto wyra-
enia i % j bdzie miaa znak taki jak operand i (czyli -9 % 7 to –2).

Zachowanie zalene od implementacji

Pojcie zachowania albo dziaania zalenego od implementacji bdzie si poja-
wia w naszym omówieniu na tyle czsto, e warto przedstawi je zawczasu.
W standardzie jzyka C celowo pozostawiono specyfikacj dziaania niektórych
elementów jzyka jako nie do koca sprecyzowan, z zaoeniem, e „implemen-
tacja” — czyli konkretne wcielenie kompilatora i programu konsolidujcego dla
konkretnej platformy maszynowej i systemowej — wypeni luk po swojemu.
W efekcie zachowanie programu w toku wykonania moe by róne na rónych
platformach. Przykadem zachowania zalenego od implementacji jest wanie
dziaanie operatorów / i % wedug standardu C89.

Niedoprecyzowanie niektórych elementów jzyka wydaje si dziwactwem,

a nawet dziaaniem szkodliwym, ale w istocie dobrze odzwierciedla filozofi przy-
wiecajc twórcom jzyka C. Jednym z zaoe projektowych bya przecie mo-
liwie dua wydajno programów pisanych w tym jzyku, co czsto oznacza
konieczno dostosowania implementacji poszczególnych konstrukcji do zachowa-
nia danej maszyny. Tak wic niektóre procesory, wykonujc operacj dzielenia
cakowitego –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 zalenych

od implementacji. Jeli nie da si takiej zalenoci unikn, warto przynajmniej
„rcznie” sprawdzi wyniki wtpliwych operacji — standard C wymaga na szczcie,
aby sposób realizacji zachowa zalenych od implementacji by udokumentowany.

Pierwszestwo i czno operatorów

Kiedy wyraenie zawiera wicej ni jeden operator, interpretacja wyraenia moe
by wtpliwa. Czy na przykad i + j * k oznacza „dodanie i do j i pomno-
enie sumy przez k”, czy moe „pomnoenie j przez k i dodanie do iloczynu i”?
Wtpliwo mona usun, stosujc nawiasy, a wic zapisujc jawnie albo (i + j)
* k

, albo i + (j * k). Jzyk C co do zasady pozwala na grupowanie podwy-

rae w wyraeniach wanie za pomoc nawiasów.

niezdefiniowane

zachowanie programu

¥ 4.4

background image

88

Rozdzia 4. Wyraenia

No dobrze, ale jak zinterpretowa wyraenie bez nawiasów? Czy dla kompi-

latora i + j * k to jest (i + j) * k, czy moe jednak i + (j *k)? Tak
jak w wielu innych jzykach, w C potencjalne wtpliwoci tego rodzaju rozstrzyga
si na bazie regu pierwszestwa (albo inaczej priorytetów) operatorów (ang.
precedence). Dla operatorów arytmetycznych pierwszestwo przedstawia si tak
(od najwyszego priorytetu):

+

-

(jednoargumentowe)

*

/

%

+

-

(dwuargumentowe)

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

Kiedy w jednym wyraeniu wystpuje wiele operatorów, moemy okreli

sposób ich interpretowania (kolejno obliczania podwyrae) przez kompilator,
posikujc si nawiasami grupujcymi podwyraenia, od operatorów o najwy-
szym priorytecie po operatory o priorytecie najniszym. Oto przykady:

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)

Reguy pierwszestwa operatorów nie s jednak wystarczajce do rozstrzy-

gnicia wtpliwoci co do kolejnoci obliczania podwyrae, kiedy w wyraeniu
wystpuje wiele operatorów o tym samym priorytecie. W takiej sytuacji zastoso-
wanie ma regua cznoci 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, wic:

i - j - k

interpretuje si jako

(i - j) - k

i * j / k

interpretuje si jako

(i * j) / k

Operator jest prawostronnie czny, kiedy grupuje operandy od prawej strony do
lewej. Prawostronnie cznymi operatorami s jednoargumentowe operatory aryt-
metyczne (+ i -), wic:

- + i

interpretuje si jako

-(+i)

Reguy pierwszestwa i cznoci operatorów s bardzo wane take w innych

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

Obliczanie cyfry kontrolnej kodu kreskowego

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

tabela operatorów

¥ Dodatek A

PROGRAM

background image

4.1. Operatory arytmetyczne

89

identyfikuje zarówno producenta, jak i konkretny produkt. Kady kod kreskowy
reprezentuje dwunastocyfrow liczb (wypisan zreszt najczciej pod kodem
kreskowym). Oto przykadowy kod kreskowy pizzy Stouffer:

Cyfry:

0 13800 15173 5

s jawnie wypisane pod waciwym kodem kreskowym. Pierwsza z nich okrela
typ towaru (dla wikszoci towarów jest to 0 albo 7, dla towarów waonych 2,
dla lekarstw i produktów farmaceutycznych 3, a dla kuponów 5). Pierwsza gru-
pa piciu cyfr identyfikuje producenta towaru (13800 to kod Nestle USA Frozen
Food Division, czyli dziau mroonek amerykaskiego koncernu Nestle). Druga
grupa cyfr (znów pi) to identyfikator produktu (okrelajcy midzy innymi
rozmiar opakowania). Ostatnia cyfra to cyfra kontrolna, której jedynym zadaniem
jest pomoc w weryfikowaniu poprawnoci kodów kreskowych (poprawnoci
poprzednich cyfr). Kiedy taki kod zostanie niepoprawnie zeskanowany, cyfra kon-
trolna nie bdzie si zgadza z jedenastoma pierwszymi cyframi, wic skaner
kasowy odrzuci kod.

Oto jedna z metod obliczania cyfry kontrolnej kodu kreskowego UPC:

Dodaj cyfr pierwsz, trzeci, pit, siódm, dziewit i jedenast.
Dodaj cyfr drug, czwart, szóst, ósm i dziesit.
Pomnó pierwsz sum przez 3 i dodaj j do drugiej sumy.
Od wyniku odejmij 1.
Oblicz reszt z dzielenia pomniejszonego wyniku przez 10.
Odejmij reszt z dzielenia od 9.

Na przykadzie pizzy Stouffer otrzymamy pierwsz sum o wartoci (0+3+0+1+
1+3) = 8; druga suma powinna wynosi (1+8+0+5+7) = 21. Suma iloczynu pierw-
szej sumy przez 3 i drugiej sumy daje 45. 45 odj 1 daje 44. Reszta z dzielenia
44 przez 10 to 4. Rónica 9 – 4 wynosi 5. Oto kilka innych przykadów liczbowych
kodów kreskowych UPC, które moemy 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 dou strony

1

.

1

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

background image

90

Rozdzia 4. Wyraenia

Napiszmy program, który bdzie oblicza cyfr kontroln dla dowolnego kodu

UPC. Program bdzie wymaga od uytkownika wprowadzenia pierwszych 11
cyfr kodu UPC, a nastpnie wypisze brakujc cyfr kodu. Aby unikn omyek,
nakaemy uytkownikowi wprowadzanie cyfr kodu partiami: najpierw wprowadzi
samotn cyfr z lewej, potem grup piciu cyfr kodu producenta, a na koniec pitk
cyfr kodu produktu. Sesja z programem bdzie wygldaa mniej wicej tak:

Podaj pierwsz cyfr kodu: 0
Podaj pierwsz grup piciu cyfr kodu: 13800

Podaj nastpn grup piciu cyfr kodu: 15173
Cyfra kontrolna: 5

Zamiast wczytywa poszczególne grupy cyfr jako liczby piciocyfrowe,

bdziemy wczytywa je jako pitk liczb jednocyfrowych. Wczytanie cyfr jako
osobnych liczb bdzie dla nas potem znacznie wygodniejsze. Nie trzeba bdzie
si te martwi na przykad o to, e jedna z dwóch piciocyfrowych liczb nie
zmieci 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, odpowiadajcymi liczbom jednocyfro-

wym (w zapisie dziesitnym).

/* Obliczanie cyfry kontrolnej kodu kreskowego UPC */

#include <stdio.h>

int main(void)
{

int d, i1, i2, i3, i4, i5, j1, j2, j3, j4, j5,

first_sum, second_sum, total;

printf("Podaj pierwsz cyfr kodu: ");

scanf("%1d", &d);

printf("Podaj pierwsz grup piciu cyfr kodu: ");
scanf("%1d%1d%1d%1d%1d", &i1, &i2, &i3, &i4, &i5);

printf("Podaj drug grup piciu cyfr kodu: ");

scanf("%1d%1d%1d%1d%1d", &j1, &j2, &j3, &j4, &j5);

first_sum = d + i2 + i4 + j1 + j3 + j5;

second_sum = i1 + i3 + i5 + j2 + j4;
total = 3 * first_sum + second_sum;

printf("Cyfra kontrolna: %d\n", 9 - ((total - 1) % 10));

return 0;

}

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

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

upc.c

background image

4.2. Operatory przypisania

91

4.2.

Operatory przypisania

Po obliczeniu wartoci wyraenia czsto chcemy zachowa t warto w zmiennej
do póniejszego uycia. W jzyku C suy do tego celu operator przypisania
(ang. assignment) w postaci symbolu =. Aby dao si wygodnie aktualizowa
warto zmiennej wartoci wyraenia, C oferuje równie zestaw tak zwanych
zoonych operatorów przypisania (ang. compound assignment operators).

Przypisania proste

Efektem przypisania

v

=

e

jest obliczenie wyraenia

e

i skopiowanie wartoci

wyraenia do

v

. Jak wida na przykadach poniej,

e

moe by zmienn, sta albo

dowolnym wyraeniem:

i = 5;

/* i ma teraz warto 5 */

j = i;

/* j ma teraz warto 5 */

k = 10 * i + j;

/* k ma teraz warto 55 */

Jeli

e

i

v

nie s wartociami 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 wartoci przypisywanej wrócimy nieco póniej.

W wielu innych jzykach programowania przypisanie jest instrukcj. W jzyku

C przypisanie jest jednak operatorem, tak jak +. Innymi sowy, akt przypisania
jest wyraeniem posiadajcym warto, tak samo jak akt dodawania jest wyrae-
niem posiadajcym warto (tu równ sumie operandów). Wartoci przypisania

v

=

e

jest warto

v

, obliczana ju po wykonaniu przypisania. Wartoci przy-

pisania i = 72.99f jest wic nie 72,99, ale 72.

Efekty uboczne

Zazwyczaj nie oczekuje si od operatorów, aby modyfikoway wartoci operan-
dów — w matematyce operatory nie maj przecie takich waciwoci. Dziaanie
i + j

nie modyfikuje ani i, ani j, a jedynie oblicza sum i oraz j.

Wikszo operatorów jzyka C równie nie modyfikuje operandów, ale nie-

które to robi. O takich operatorach mówimy, e maj efekty uboczne, poniewa ich
dziaanie nie ogranicza si tylko do jawnego wyliczenia wartoci. Pierwszym ope-
ratorem, jaki poznajemy od strony efektów ubocznych, jest prosty operator przypi-
sania — nie tylko oblicza warto wyraenia prawego operandu, ale take mody-
fikuje warto lewego operandu. Obliczenie wartoci wyraenia i = 0 daje warto
0, a efektem ubocznym obliczenia wyraenia jest przypisanie 0 do i.

przypisania z konwersj

¥ 7.4

background image

92

Rozdzia 4. Wyraenia

Poniewa przypisanie jest operatorem, moemy konstruowa acuchowe wyra-

enia z operatorami przypisania:

i = j = k = 0;

Operator = jest czny prawostronnie, wic takie przypisanie jest interpretowane
jako:

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

W efekcie w pierwszej kolejnoci nastpi przypisanie 0 do k, nastpnie wynik
przypisania zostanie przypisany do j, a wynik tego przypisania — do i.

Naley si wystrzega nieoczekiwanych wyników w acuchowych 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 wszdzie tam,

gdzie byaby dozwolona warto typu

v

. W poniszym przykadzie wyraenie

j = i

kopiuje warto i do zmiennej j. Nowa warto j jest potem dodawana do

1 i tak obliczona warto jest przypisywana do k:

i = 1;
k = 1 + (j = i);

printf("%d %d %d\n", i, j, k);

/* wypisuje "1 1 2" */

Takie stosowanie operatora przypisania trudno jednak uzna za dobr praktyk
programistyczn. Przede wszystkim dlatego, e „zagniedone przypisania” zmniej-
szaj czytelno programu. Mog by równie przyczyn subtelnych bdów,
o których bdzie mowa w podrozdziale 4.4.

L-wartoci

Wikszo operatorów jzyka C pozwala, aby w roli operandów wystpoway
zmienne, stae albo wyraenia (równie zawierajce inne operatory). Operator
przypisania jest o tyle wyjtkowy, e wymaga, aby lewy operand by tak zwan
l-wartoci (ang. lvalue). L-warto reprezentuje obiekt przechowywany w pamici
komputera. Nie moe to by staa ani na przykad wynik porównania. L-warto-
ciami s wszystkie zmienne. Wyraenia w rodzaju 10 albo 2 * i l-wartociami
nie s. Na razie jedyne l-wartoci, które znamy, to wanie zmienne. W dalszych
rozdziaach powiemy sobie take o innych l-wartociach.

background image

4.2. Operatory przypisania

93

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

wynika, e po lewej stronie operatora przypisania nie wolno stosowa adnych
wyrae:

12 = i;

/*** LE ***/

i + j = 0;

/*** LE ***/

-i = j;

/*** LE ***/

Takie bdne przypisania s wykrywane przez kompilator — próba skompilowa-
nia powyszych instrukcji zaowocuje bdem kompilacji z komunikatem „invalid
lvalue in assignment” („niepoprawna l-warto w przypisaniu”).

Przypisania zoone

W programach pisanych w jzyku C czsto widzi si przypisania, które przy obli-
czaniu wartoci przypisania bazuj na poprzedniej wartoci modyfikowanej zmien-
nej. Oto przykadowe przypisanie dodajce 2 do biecej wartoci zmiennej i:

i = i + 2;

Takie i tym podobne instrukcje mona w jzyku C skraca do postaci przypisa
zoonych
(ang. compound assignments). Moemy wic do analogicznej operacji
wykorzysta operator zoony +=:

i += 2;

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

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

W jzyku C mamy do dyspozycji dziewi zoonych operatorów przypisania,

w tym:

-= *= /= %=

(o pozostaych zoonych operatorach przypisania powiemy sobie w jednym
z dalszych rozdziaów). Wszystkie operatory przypisa zoonych dziaaj na
podobnych zasadach:

v

+=

e

dodaje

e

do

v

i zapisuje sum w

v

v

-=

e

odejmuje

e

do

v

i zapisuje rónic w

v

v

*=

e

mnoy

v

przez

e

i zapisuje iloczyn w

v

v

/=

e

dzieli

v

przez

e

i zapisuje iloraz w

v

v

%=

e

oblicza reszt z dzielenia

v

przez

e

i zapisuje wynik

v

Wcale nie oznacza to, e zapis

v

+=

e

jest zupenie „tosamy” z

v

=

v

+

e

.

Przede wszystkim ze wzgldu na pierwszestwo operatorów i *= j + k to nie
to samo co i = i * j + k. S te rzadkie przypadki, kiedy

v

+=

e

jest

róne od

v

=

v

+

e

, gdy

v

posiada efekty uboczne. Podobne zastrzeenia doty-

cz równie pozostaych operatorów przypisania.

Przy stosowaniu przypisa zoonych trzeba uwaa, eby nie zamieni miejscami
znaków w symbolu operatora. Przestawienie znaków moe doprowadzi do utwo-
rzenia wyraenia, które bdzie poprawne z punktu widzenia skadni programu

inne operatory

przypisania

¥ 20.1

background image

94

Rozdzia 4. Wyraenia

(kompilator nie zgosi bdu), ale niepoprawne ze wzgldu na oczekiwane dzia-
anie. Jeli na przykad zamierzamy napisa i += j, ale omykowo napiszemy
i =+ j

, program jak najbardziej si skompiluje. Niestety, zamiast doda do i

warto j, wymusimy wykonanie wyraenia i = (+j), czyli skopiujemy j do i.

Operatory przypisa zoonych maj te same waciwoci co operatory przy-

pisa prostych. W szczególnoci s prawostronnie czne, wic instrukcja:

i += j += k;

oznacza:

i += (j += k);

4.3.

Operatory inkrementacji i dekrementacji

Do najczciej uywanych operatorów jzyka C nale operatory inkrementacji
(dodania jedynki) i dekrementacji (odjcia jedynki). Tego rodzaju operacje mona,
rzecz jasna, zapisa za pomoc zwyczajnych operatorów arytmetycznych:

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

Mona te wykorzysta operatory przypisa zoonych:

i += 1;
j -= 1;

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

Na pierwszy rzut oka operatory inkrementacji i dekrementacji to wcielona

prostota: ++ dodaje jeden, a -- odejmuje jeden od operandu. Niestety, prostota
jest tu mylca. Operatory inkrementacji i dekrementacji bywaj nieoczywiste
w uyciu. Wynika to z tego, e operatory inkrementacji (++) i dekrementacji (--)
mog wystpowa w postaci przedrostkowej (np. ++i, --i) albo przyrostkowej
(np. i++, i--). Waciwy wybór rodzaju operatora silnie wpywa na poprawno
programu.

Kolejna komplikacja tkwi w fakcie, e operatory ++ i -- posiadaj efekty

uboczne (tak jak operatory przypisania) — modyfikuj warto operandu. Oblicze-
nie wyraenia ++i (inkrementacja przedrostkowa albo tzw. „przed-inkrementacja” i)
daje warto i + 1 oraz — w ramach efektu ubocznego — zwiksza i o jeden:

i = 1;

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

/* wypisuje "i to 2" */

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

/* wypisuje "i to 2" */

Z kolei obliczenie wyraenia i++ („po-inkrementacja”) daje warto i, ale
w ramach efektu ubocznego i jest zwikszane o 1:

background image

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 wywoaniem printf wypisze na wyjciu pierwotn war-
to i, jeszcze sprzed inkrementacji. Drugie wywoanie printf wypisze now
warto i. Jak wida, ++i oznacza „zwiksz natychmiast warto i”, a i++ ozna-
cza „daj biec warto i, a potem zwiksz i”. Co to znaczy „potem”? Stan-
dard jzyka C nie precyzuje dokadnego momentu wykonania inkrementacji ope-
ratora przyrostkowego, ale mona bezpiecznie zaoy, e i bdzie miao now
warto jeszcze przed wykonaniem nastpnej instrukcji.

Podobne waciwoci ma operator dekrementacji --:

i = 1;

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

/* 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 -- wystpi wielokrotnie w jednym wyraeniu, warto

wyraenia i faktyczne wartoci operandów mog by trudne do ustalenia. Wemy
nastpujcy przykad:

i = 1;
j = 2;

k = ++i + j++;

Jakie s wartoci i, j i k po wykonaniu tych instrukcji? Poniewa zmienna i
bya inkrementowana przed uyciem jej wartoci w wyraeniu, a zmienna j bya
inkrementowana po uyciu jej wartoci w wyraeniu, powysze instrukcje s
równowane nastpujcym:

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

j = j + 1;

wic wynikowe wartoci zmiennych i, j i k to odpowiednio 2, 3 i 4. Dla porów-
nania wykonanie instrukcji:

i = 1;
j = 2;

k = i++ + j++;

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

Dla porzdku wypada dopowiedzie, e przyrostkowe wersje operatorów ++

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

background image

96

Rozdzia 4. Wyraenia

4.4.

Obliczanie wartoci wyrae

W tabeli 4.2 zestawiono operatory omówione w poprzednich podrozdziaach
(podobn tabel, ale z kompletem operatorów, zawiera dodatek A). Pierwsza
kolumna okrela pierwszestwo operatorów wzgldem pozostaych operatorów
w tabeli (1 to najwikszy priorytet, 5 to priorytet najmniejszy). Ostatnia kolumna
opisuje czno operatorów.

Priorytet

Nazwa

Symbol

czno

1

inkrementacja (przyrostkowa)

dekrementacja (przyrostkowa)

++

--

lewostronna

2

inkrementacja (przedrostkowa)

dekrementacja (przedrostkowa)

jednoargumentowy plus

jednoargumentowy minus

++

--

+

-

prawostronna

3

multiplikatywne

* / %

lewostronna

4

addytywne

+ -

lewostronna

Tabela 4.2.

Czciowa lista

operatorów jzyka C

5

przypisania

= *= /= %=
+= -=

prawostronna

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

przydatna. Zaómy, e w kodzie ródowym (na przykad cudzym) napotkamy tak
instrukcj:

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

Takie wyraenie byoby znacznie czytelniejsze, gdyby zawierao nawiasy gru-
pujce podwyraenia. Z pomoc tabeli 4.2 moemy atwo samodzielnie uzupeni
pogrupowa wyraenie — wystarczy znale w wyraeniu operator o najwy-
szym priorytecie i wraz z operandami uj go w nawias. Od tej pory bdzie dla nas
pojedynczym operandem. Technik powtarzamy do momentu rozpoznania wszyst-
kich podwyrae.

W naszym przykadzie operatorem o najwyszym priorytecie jest ++, wyst-

pujcy tu jako operator przyrostkowy. Ujmujemy operator z operandami w nawias:

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

Nastpny wedug pierwszestwa jest operator -- (przedrostkowy) oraz jed-

noargumentowy minus (oba maj priorytet 2.):

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

Drugi znak minusa w wyraeniu ma operand po lewej stronie, wic jest operatorem
odejmowania, a nie kolejnym jednoargumentowym minusem.

Nastpnie odnajdujemy operator / (priorytet 3.):

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

background image

4.4. Obliczanie wartoci wyrae

97

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

dodawanie i odejmowanie. Kiedy w wyraeniu ssiaduj ze sob dwa operatory
o równym priorytecie, naley bardzo uwanie zastosowa regu cznoci.
W naszym przykadzie - i + otaczaj d; czno dwuargumentowych operatorów
+

i - jest lewostronna, wic stawiamy nawiasy najpierw wokó odejmowania,

a potem wokó dodawania:

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

Zostay ju tylko przypisania, oba przy operandzie b, wic znów trzeba si

posikowa cznoci. Operatory przypisania s czne prawostronnie (od prawej
do lewej), zatem najpierw ujmujemy w nawias podwyraenie z operatorem +=,
a potem podwyraenie z operatorem =:

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

W ten sposób dokonalimy penego pogrupowania wyraenia.

Kolejno obliczania podwyrae

Reguy pierwszestwa i cznoci operatorów pozwalaj na skuteczne rozbicie
dowolnie skomplikowanego wyraenia na podwyraenia — moemy dziki nim
pogrupowa poszczególne podwyraenia w nawiasy. Paradoksalnie obie reguy
nie zawsze pozwalaj na okrelenie wartoci wyraenia, która moe zalee od
kolejnoci obliczania wartoci poszczególnych podwyrae.

Jzyk C nie definiuje kolejnoci obliczania wartoci podwyrae (z wyjtkiem

podwyrae angaujcych operatory logiczne and i or, operatory warunkowe
i operatory przecinka). Przez to w przypadku wyraenia (a + b) * (c - d)
nie moemy mie pewnoci, czy jako pierwsze zostanie obliczone podwyraenie
(a + b)

, czy moe (c -d).

Wikszo wyrae ma t sam warto niezalenie od tego, w jakiej kolej-

noci zostay obliczone ich podwyraenia. Bywa jednak, e podwyraenie mody-
fikuje jeden ze swoich operandów. Wemy poniszy przykad:

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

Efekt wykonania drugiej instrukcji jest niezdefiniowany. Standard jzyka C nie
mówi nic jednoznacznego na temat wartoci tak zbudowanego wyraenia. W przy-
padku wikszoci kompilatorów zmienna c bdzie miaa warto 6 albo 2. Jeli
jako pierwsze zostanie obliczone podwyraenie (b = a + 2), b otrzyma war-
to 7, a c zostanie obliczone jako 6. Ale jeli pierwszym obliczonym podwyrae-
niem bdzie (a = 1), wtedy b otrzyma warto 3, a w c znajdzie si warto 2.

Naley unika konstruowania wyrae, w których odwoujemy si do wartoci
zmiennej, a równoczenie (w innym podwyraeniu) bazujemy na tej wartoci.
Wyraenie (b = a + 2) - (a = 1) zawiera odwoanie do wartoci a
w pierwszym podwyraeniu oraz modyfikacj wartoci do a w drugim podwyraeniu
(przez przypisanie jedynki). Niektóre kompilatory oznaczaj takie wyraenia komu-
nikatami z ostrzeeniami w rodzaju „operacja na ‘a’ moe by niezdefiniowana”.

operatory logiczne and i or

¥ 5.1

operator warunkowy

¥ 5.2

operator przecinka

¥ 5.3

background image

98

Rozdzia 4. Wyraenia

Aby zapobiec tego rodzaju problemom, najlepiej unika stosowania operatora

przypisania w podwyraeniu. Zamiast tego naley wykona przypisania jako osobne
instrukcje. Na przykad nasze wtpliwe wyraenie moe zosta rozbite nastpujco:

a = 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 modyfikujce

warto operandu — chodzi o operatory inkrementacji i dekrementacji. Przy korzy-
staniu z tych operatorów równie trzeba uwaa, aby nie doprowadzi do powstania
wyraenia, którego poprawna warto jest zalena od konkretnej kolejnoci obliczania
podwyrae, jak tutaj, gdzie do j moe zosta przypisana jedna z dwóch wartoci:

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

Pozornie jest oczywiste, e j otrzyma warto 4. Niestety, w efekcie wykonania
takich instrukcji zmienna j moe mie równie warto 6. Oto moliwy scena-
riusz: (1) najpierw pobierany jest drugi operand (pierwotna warto i); nastpnie i
podlega inkrementacji (2) i pobierany jest drugi operand (ju nowa warto i);
(3) obliczany jest iloczyn obu operandów: liczba 6. „Pobranie” wartoci zmiennej
oznacza odczytanie wartoci z pamici skojarzonej ze zmienn. Póniejsza zmiana
wartoci zmiennej w pamici nie wpywa ju na warto pobran, bo pobrana war-
to jest kopiowana w specjalne miejsce (tzw. rejestr) wewntrz procesora.

Niezdefiniowane zachowanie programu

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

4.5.

Instrukcje wyraeniowe

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

++i;

background image

Pytania i odpowiedzi

99

Kiedy dochodzi do wykonania tej instrukcji, nastpuje zwikszenie wartoci
i

o jeden, a póniej pobierana jest nowa warto i (tak jakby miaa za chwil zosta

wykorzystana przy obliczaniu wyraenia nadrzdnego). Ale skoro ++i nie jest
czci wikszego wyraenia, pobrana warto jest odrzucana, a program przecho-
dzi do wykonania nastpnej instrukcji (ale inkrementacja i jest oczywicie trwaa).

Skoro warto instrukcji wyraeniowej jest odrzucana, nie ma wikszego sensu

wykorzystywanie wyrae jako instrukcji, chyba e s to wyraenia z efektami
ubocznymi; one zostan przecie wykonane mimo odrzucenia obliczonej wartoci
wyraenia. Wemy trzy przykady. W pierwszym do i przypisywana jest war-
to 1 — nowa warto i jest pobierana, ale zaraz odrzucana:

i = 1;

W drugim przykadzie warto i jest pobierana, ale znów nie bdzie nigdzie
wykorzystana. Za to samo i ju po pobraniu wartoci zostanie zwikszone o jeden:

i++;

W trzecim przykadzie zostanie obliczona warto wyraenia i * j - 1, ale
obliczona warto zaraz bdzie odrzucona:

i * j - 1;

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

Bezcelow, a wic pust instrukcj wyraeniow mona atwo popeni przez
prost literówk. Wystarczy, e zamiast:

i = j;

przypadkiem napiszemy:

i + j;

(taki bd jest prawdopodobny tym bardziej, e znaki + i = zajmuj ten sam klawisz
na klawiaturze). Niektóre kompilatory wykrywaj bezcelowe instrukcje wyrae-
niowe, generujc przy nich ostrzeenia w rodzaju „instrukcja bez efektu” („sta-
tement with no effect”).

Pytania i odpowiedzi

P:

Zauwayem, e jzyk C nie posiada operatora potgowania. Jak mam pod-
nosi liczby do potgi?

O:

Jeli wykadnik potgi jest niewielk dodatni liczb cakowit, potgowanie naj-
lepiej zrealizowa przez wielokrotne mnoenie (np. i * i * i dla obliczenia
szecianu i). Do obliczania potg o wykadnikach niecakowitych najlepiej wyko-
rzysta funkcj pow.

funkcja pow

¥ 23.3

background image

100

Rozdzia 4. Wyraenia

P:

Chciaem zastosowa operator % przy operandzie typu float, ale program
nie daje si skompilowa. Co mog zrobi (s. 86)?

O: Operator

%

wymaga operandów cakowitych. Spróbuj uy funkcji fmod.

P:

Dlaczego dziaanie operatorów dzielenia (/) i reszty z dzielenia (%) dla ujem-
nych operandów jest tak zagmatwane (s. 87)?

O:

Zasady dziaania tych operatorów nie s tak zagmatwane, jakby si wydawao.
W obu wersjach standardu celem jest zapewnienie, eby warto (a / b) *
b + a % b

zawsze bya równa a (i faktycznie, oba standardy gwarantuj tak

zaleno, o ile tylko warto a / b jest wartoci „reprezentowaln”). Problem
polega na tym, e zaoon zaleno mona speni na dwa sposoby, przy rónych
metodach obliczania a / b i a % b. Wedug C89 albo -9 / 7 to –1 i -9 % 7
to –2 (równo jest speniona), albo -9 / 7 to –2 i -9 % 7 to 5 (i znów rów-
no jest speniona). 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 wikszo procesorów wykony-
waa ju dzielenie cakowite z obcinaniem w kierunku zera, wic tak wanie
regu dzielenia zapisano w standardzie C99 jako jedyn dozwolon warto
ilorazu z operandem ujemnym.

P:

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

O: W

rzeczy

samej.

L-warto jest wyraeniem, które jest dozwolone po lewej stronie

operatora przypisania; r-warto to wyraenie, które jest dozwolone po prawej
stronie. R-warto moe wic by zmienn, sta albo dowolnym wyraeniem.
W niniejszej ksice, podobnie jak w standardzie jzyka C, bdziemy trzyma
si okrelenia „wyraenie”, które jednak do dobrze oddaje istot r-wartoci.

*P:

Bya mowa o tym, e

v

+=

e

nie jest odpowiednikiem

v

=

v

+

e

, jeli

v

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

O: Obliczenie

wartoci

v

+=

e

powoduje, e warto

v

jest obliczana tylko raz.

v

w wyraeniu

v

=

v

+

e

jest obliczane dwa razy. Wic jeli w tym drugim

przypadku

v

posiada efekt uboczny, zostanie on wykonany dwukrotnie. W tym

przykadzie i jest inkrementowane raz:

a[i++] += 2;

ale jeli zamiast += uyjemy przypisania =, otrzymamy:

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

Warto i jest równoczenie pobierana i modyfikowana w obrbie jednej instruk-
cji, wic wynik wykonania takiej instrukcji jest niezdefiniowany. Jest prawdopo-
dobne, e i zostanie zwikszone dwukrotnie, ale w istocie nie mona nic pewnego
powiedzie o dziaaniu takiego programu.

P:

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

O:

Jzyk C odziedziczy operatory ++ i -- w spadku po jzyku B Kena Thompsona.
Thompson wprowadzi te operatory, poniewa jego kompilator B najwyraniej
potrafi efektywniej przetumaczy zapis ++i ni i = i + 1. Operatory te

funkcja fmod

¥ 23.3

background image

Pytania i odpowiedzi

101

stay si sol jzyka C (bazuje na nich wiele jego sawnych idiomów). W nowo-
czesnych kompilatorach stosowanie ++ i -- zapewne ani bardzo nie przyspieszy
programu, ani nie zmniejszy bardzo rozmiaru pliku wynikowego. Nieustajca
popularno tych operatorów wynika chyba z ich zwartoci.

P: Czy

operatory

++

i -- dziaaj ze zmiennymi typu float?

O:

Tak, operacje inkrementacji i dekrementacji mona stosowa do wartoci cakowi-
tych i zmiennoprzecinkowych, jednak w praktyce mao kto próbuje inkrementowa
albo dekrementowa zmienn typu float.

*P:

Kiedy dokadnie nastpuje zwikszenie wartoci operandu w przypadku przy-
rostkowych wersji
++ i -- (s. 95)?

O:

wietne pytanie. Niestety, nie mona na nie atwo odpowiedzie. Standard jzyka
C wprowadza pojcie tak zwanego „punktu sekwencji” i mówi, e „aktualizacja
skadowanej wartoci operandu powinna odby si pomidzy poprzednim a nastp-
nym punktem sekwencji”. W jzyku C okrelono kilka rónych punktów sekwen-
cji. Jednym z nich jest koniec instrukcji wyraeniowej — na kocu instrukcji
wyraeniowej wszystkie opónione inkrementacje i dekrementacje powinny zosta
wykonane; nie moe doj do rozpoczcia wykonywania nastpnej instrukcji
z pominiciem tego kroku.

Niektóre operatory, o których powiemy sobie w dalszej czci ksiki (logiczny

operator and, logiczny operator or, operator warunkowy i operator przecinka),
równie stanowi punkty sekwencji. To samo dotyczy wywoa funkcji — argu-
menty wywoania funkcji musz by w peni obliczone przed wykonaniem wywo-
ania. Jeli argument wywoania jest wyraeniem zawierajcym przyrostkowy
operator ++ albo --, inkrementacja bd dekrementacja musi zosta wykonana
jeszcze przed wykonaniem wywoania funkcji.

P:

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

O:

Z definicji wyraenie reprezentuje warto. Jeli np. i ma warto 5, to obliczenie
wyraenia i + 1 daje warto 6. Zamiemy to wyraenie na instrukcj wyrae-
niow, dodajc rednik na kocu:

i + 1;

W ramach wykonywania tej instrukcji dochodzi do obliczenia wartoci wyraenia
i + 1

. Poniewa jednak ta warto nie jest nigdzie wykorzystywana (nie zostaa

przypisana do zmiennej ani nie jest wykorzystywana jako podwyraenie) —
przepada.

P:

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

O:

Pamitajmy, e przypisanie jest w jzyku C operatorem i jak kady operator gene-
ruje warto. Przypisanie:

i = 1;

powoduje zapisanie 1 w zmiennej i, ale jako wyraenie ma warto przypisania (1)
i ta wanie warto jest odrzucana. Odrzucanie wartoci wyraenia nie jest jak
wielk strat, poniewa w naszej instrukcji chodzio nam przede wszystkim o zmo-
dyfikowanie zmiennej i.

background image

102

Rozdzia 4. Wyraenia

wiczenia

1.

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

, j i k s zmiennymi typu int:

(a) i = 5; j = 3;

printf("%d %d", i / j, i % j);

(b) i = 2; j = 3;

printf("%d", (i + 10) % j);

(c) i = 7; j = 8; k = 9;

printf("%d", (i + 10) % k / j);

(d) i = 1; j = 2; k = 3;

printf("%d", (i + 5) % (j + 2) / k);

2.

Czy wyraenie (-i)/j bdzie miao zawsze t sam warto co -(i/j), jeli i i j s
dodatnimi wartociami cakowitymi? Uzasadnij odpowied.

3.

Jaka bdzie warto poniszych wyrae wedug standardu C89 (jeli moliwa jest wicej
ni jedna warto, podaj wszystkie warianty):

(a) 8 / 5

(b) -8 / 5

(c) 8 / -5

(d) -8 / -5

4.

Powtórz wiczenie 3. dla wytycznych standardu C99.

5.

Jaka bdzie warto poniszych wyrae wedug standardu C89 (jeli moliwa jest wicej
ni jedna warto, podaj wszystkie warianty):

(a) 8 % 5

(b) -8 % 5

(c) 8 % -5

(d) -8 % -5

6.

Powtórz wiczenie 5. dla wytycznych standardu C99.

7.

Algorytm obliczania cyfry kontrolnej UPC koczy si nastpujcymi krokami:

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

A korci, eby ten algorytm uproci nastpujco:

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

Dlaczego taka poprawka nie zadziaa?

8.

Czy program upc.c bdzie wci poprawny, jeli wyraenie 9 – ((total – 1) % 10) zostanie
zastpione przez (10 - (total % 10)) % 10?

9.

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

i k s zmiennymi typu int:

Podrozdzia 4.2

Podrozdzia 4.1

*

background image

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 wyjciu programu wykonujcego ponisze instrukcje. Zaó, e
i

i j s zmiennymi typu int:

(a) i = 6;

j = i += i;

printf("%d %d", i, j);

(b) i = 5;

j = (i -= 2) + 1;

printf("%d %d", i, j);

(c) i = 7;

j = 6 + (i = 2.5);

printf("%d %d", i, j);

(d) i = 2; j = 8;

j = (i = 6) + (j = 3);

printf("%d %d", i, j);

11.

Napisz, co pojawi si na wyjciu programu wykonujcego ponisze instrukcje. Zaó, e i, j
i k s zmiennymi typu int:

(a) i = 1;

printf("%d ", i++ - 1);

printf("%d", i);

(b) i = 10; j = 5;

printf("%d ", i++ - ++j);

printf("%d %d", i, j);

(c) i = 7; j = 8;

printf("%d ", i++ - --j);

printf("%d %d", i, j);

(d) i = 3; j = 4; k = 5;

printf("%d ", i++ - j++ + --k);

printf("%d %d %d", i, j, k);

12.

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

i j s zmiennymi typu int:

(a) i = 5;

j = ++i * 3 - 2;

printf("%d %d", i, j);

(b) i = 5;

j = 3 - 2 * i++;

printf("%d %d", i, j);

Podrozdzia 4.3

*

background image

104

Rozdzia 4. Wyraenia

(c) i = 7;

j = 3 * i-- + 2;

printf("%d %d", i, j);

(d) i = 7;

j = 3 + --i * 2;

printf("%d %d", i, j);

13.

Które z wyrae: ++i czy i++ jest dokadnie równowane wyraeniu (i += 1)? Uza-
sadnij odpowied.

14.

Pogrupuj podwyraenia w nawiasy tak, aby zilustrowa sposób interpretacji poniszych
wyrae zoonych:

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

(b) a / b % c / d

(c) - a - b + c - + d

(d) a * - b / c - d

15.

Podaj wartoci zmiennych i i j po wykonaniu kadej z poniszych instrukcji (pocztkowa
warto i to 1, a pocztkowa warto j to 2):

(a) i += j;

(b) i--;

(c) i * j / i;

(d) i % ++j;

Zadania programistyczne

1.

Napisz program, który bdzie wymaga od uytkownika wprowadzenia liczby dwucyfrowej,
a nastpnie wypisze t liczb w odwróconej kolejnoci cyfr. Sesja z programem powinna
przebiega tak:

Podaj liczb dwucyfrow: 28
Wspak: 82

2.

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

3.

Przerób program z zadania 2. tak, eby program wypisywa odwrotny zapis liczby trzycy-
frowej, bez uycia operacji arytmetycznych do podziau liczby na cyfry. Wskazówka: Zaj-
rzyj do programu upc.c z podrozdziau 4.1.

4.

Napisz program, który wczytuje liczb wprowadzon na wejcie i wywietla j w zapisie
ósemkowym:

Podaj liczb pomidzy 0 i 32767: 1953
W zapisie ósemkowym to: 03641

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

Podrozdzia 4.4

Podrozdzia 4.5

background image

Zadania programistyczne

105

5.

Przerób program upc.c z podrozdziau 4.1 tak, aby uytkownik wprowadza 11 cyfr kodu
UPC za jednym zamachem:

Podaj 11 cyfr kodu UPC: 01380015173
Cyfra kontrolna: 5

6.

W krajach europejskich stosuje si kody kreskowe z 13 cyframi (tzw. kod EAN). Kady
kod EAN koczy si cyfr kontroln (tak jak UPC). Algorytm obliczania cyfry kontrolnej
kodu EAN równie jest do podobny:

Dodaj drug, czwart, szóst, ósm, dziesit i dwunast cyfr kodu.
Dodaj pierwsz, trzeci, pit, siódm, dziewit i jedenast cyfr kodu.
Pomnó pierwsz sum przez 3 i dodaj j do drugiej sumy.
Od sumy odejmij 1.
Oblicz reszt z dzielenia pomniejszonej sumy przez 10.
Odejmij wynik od 9.

Na przykad tureckie sodycze Güllüoglu Turkish Delight Pistachio & Coconut maj kod
EAN 8691484260008. Pierwsza suma to 6+1+8+2+0+0 = 17, a druga suma to 8+9+4+4+
6+0 = 31. Suma iloczynu pierwszej sumy przez 3 i drugiej sumy daje 82. Po odjciu 1 zo-
staje 81. Reszta z dzielenia 81 przez 10 to 1. 9 – 1 daje 8. Zgadza si, cyfra kontrolna naszego
kodu to dokadnie 8. Zadanie polega na przerobieniu programu upc.c z podrozdziau 4.1
tak, aby oblicza cyfr kontroln kodu EAN. Uytkownik powinien wprowadza do programu
pierwsze 12 cyfr kodu EAN jednym cigiem:

Podaj 12 cyfr kodu EAN: 869148426000
Cyfra kontrolna: 8


Wyszukiwarka

Podobne podstrony:
Jezyk C Nowoczesne programowanie Wydanie II jcnpr2
Jezyk C Nowoczesne programowanie Wydanie II jcnpr2
Jezyk C Nowoczesne programowanie Wydanie II
Jezyk ANSI C Programowanie Wydanie II jansic
Perl Zaawansowane programowanie Wydanie II perlz2
Asembler Sztuka programowania Wydanie II asesz2
Perl Zaawansow programowanie Wydanie II
Jezyk C Szkola programowania Wydanie V
Jezyk C Szkola programowania Wydanie V jcszpr
Java Efektywne programowanie Wydanie II javep2
Perl Zaawansowane programowanie Wydanie II perlz2
SZTUKA PERSWAZJI czyli jezyk wplywu i manipulacji Wydanie II rozszerzone eBook Pdf persw2 p
SZTUKA PERSWAZJI czyli jezyk wplywu i manipulacji Wydanie II rozszerzone eBook ePub persw2 e
Język C Szkoła programowania Wydanie V

więcej podobnych podstron