You are on page 1of 10

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG

W potrzasku
jzyka C
Autor: Andrew Koenig
Tumaczenie: Przemysaw Szeremiota
ISBN: 83-7361-727-2
Tytu oryginau: C Traps and Pitfalls
Format: B5, stron: 152

TWJ KOSZYK
DODAJ DO KOSZYKA

CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK

CZYTELNIA
FRAGMENTY KSIEK ONLINE

Kady, nawet najbardziej dowiadczony programista, popenia bdy podczas pracy.


Niektre z nich wynikaj z popiechu, inne z uycia niewaciwych konstrukcji,
operatorw lub typw. Wikszo z nich mona wykry i usun po kilku minutach
uwanej lektury kodu. Zdarzaj si jednak i takie bdy, ktrych odnalezienie
i skorygowanie zajmuje kilka dni. Bdy te s z reguy atwe do uniknicia,
jeli zrozumie si przyczyny ich powstawania.
Ksika W potrzasku C zawiera omwienie najczciej spotykanych bdw
i przyczyn ich powstawania. Nie zawiera oglnikw - jej atutem s konkretne,
zaczerpnite z praktyki, przykady. Kady programista prdzej czy pniej natknie si
na jeden z prezentowanych w ksice bdw i, dziki zawartym w niej wiadomociom,
bdzie w stanie usun go i unikn w pniejszej pracy.
Bdy leksykalne i skadniowe
Przepenienie zakresu
Problemy z konsolidacj
Waciwe stosowanie funkcji bibliotecznych
Makrodefinicje
Przenono kodu
Nie tra czasu na usuwanie bdw dowiedz si co robi, eby w ogle nie
wystpoway.

Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl

Spis treci
.

Wstp ............................................................................................... 7
Wprowadzenie ................................................................................. 11

Rozdzia 1. Puapki leksykalne........................................................................... 15


1.1. Porwnanie a przypisanie......................................................................................... 16
1.2. & i | to nie to samo co && i || ................................................................................... 17
1.3. Zachanno analizatora leksykalnego...................................................................... 18
1.4. Literay staych cakowitych..................................................................................... 19
1.5. Cigi a znaki............................................................................................................. 20

Rozdzia 2. Puapki skadniowe .......................................................................... 23


2.1. Deklaracje funkcji .................................................................................................... 23
2.2. Priorytety operatorw............................................................................................... 26
2.3. Uwaga na redniki!................................................................................................... 30
2.4. Instrukcja wyboru switch.......................................................................................... 32
2.5. Wywoania funkcji ................................................................................................... 33
2.6. Klauzula else w zagniedonych instrukcjach if ...................................................... 34

Rozdzia 3. Puapki semantyczne ....................................................................... 37


3.1. Wskaniki i tablice ................................................................................................... 37
3.2. Wskaniki nie s tablicami ....................................................................................... 42
3.3. Deklaracje tablic w roli parametrw......................................................................... 43
3.4. Niebezpieczne synekdochy....................................................................................... 45
3.5. Wskaniki puste a cigi niepuste.............................................................................. 46
3.6. Zliczanie a asymetryczne granice zakresw ............................................................. 46
3.7. Kolejno obliczania w wyraeniu ........................................................................... 55
3.8. Operatory &&, || i !................................................................................................... 57
3.9. Przepenienie zakresu liczby cakowitej ................................................................... 58
3.10. Zwracanie wartoci przez funkcj main ................................................................... 59

Rozdzia 4. Konsolidacja.................................................................................... 63
4.1. Czym jest konsolidator? ........................................................................................... 63
4.2. Deklaracje a definicje ............................................................................................... 65
4.3. Kolizje nazw i sowo static....................................................................................... 66
4.4. Argumenty, parametry i wartoci funkcji ................................................................. 67
4.5. Kontrola typw obiektw zewntrznych .................................................................. 72
4.6. Pliki nagwkowe ..................................................................................................... 75

W potrzasku jzyka C

Rozdzia 5. Funkcje biblioteczne ........................................................................ 77


5.1. Funkcja getchar zwraca warto typu int.................................................................. 78
5.2. Aktualizacja pliku sekwencyjnego ........................................................................... 78
5.3. Buforowanie wyjcia i przydzia pamici................................................................. 80
5.4. Diagnostyka bdw funkcj errno........................................................................... 81
5.5. Funkcja signal .......................................................................................................... 82

Rozdzia 6. Preprocesor ..................................................................................... 85


6.1. Odstpy w makrodefinicjach .................................................................................... 86
6.2. Makrodefinicje a funkcje.......................................................................................... 86
6.3. Makrodefinicje a instrukcje ...................................................................................... 90
6.4. Makrodefinicje a definicje typw ............................................................................. 91

Rozdzia 7. Kwestie przenonoci ...................................................................... 93


7.1. W obliczu zmian....................................................................................................... 94
7.2. Co z nazwami? ......................................................................................................... 95
7.3. Rozmiar liczby cakowitej ........................................................................................ 96
7.4. Czy znaki maj znaki?.............................................................................................. 97
7.5. Operatory przesuni bitowych ................................................................................ 98
7.6. Zerowa komrka pamici ......................................................................................... 99
7.7. Obcinanie przy dzieleniu........................................................................................ 100
7.8. Rozmiar liczby losowej .......................................................................................... 101
7.9. Zamiana wielkoci liter .......................................................................................... 102
7.10. Najpierw zwalnia, potem przydziela ponownie?................................................. 103
7.11. Przykadowe problemy nieprzenonoci ................................................................ 104

Rozdzia 8. Porady i odpowiedzi do wicze...................................................... 109


8.1. Porady .................................................................................................................... 110
8.2. Odpowiedzi do wicze ......................................................................................... 113

Dodatek A Funkcja printf i zmienne listy argumentw ...................................... 129


A.1. Rodzina funkcji printf............................................................................................ 129
A.1.1. Proste specyfikatory formatu........................................................................ 131
A.1.2. Modyfikatory................................................................................................ 135
A.1.3. Znaczniki...................................................................................................... 138
A.1.4. Zmienna precyzja i szeroko pola............................................................... 140
A.1.5. Neologizmy .................................................................................................. 141
A.1.6. Anachronizmy .............................................................................................. 141
A.2. Zmienne listy argumentw varargs.h................................................................ 142
A.2.1. Implementacja varargs.h............................................................................... 146
A.3. Zmienne listy argumentw w wydaniu ANSI stdarg.h ..................................... 147

Skorowidz...................................................................................... 149

Rozdzia 1.

Puapki leksykalne
Czytajc zdanie, nie zastanawiamy si nad znaczeniem poszczeglnych liter, tworzcych
kolejne sowa. Litery same w sobie nios bowiem niewiele treci; grupujemy je wic
w sowa i im przypisujemy znaczenie.
Podobnie jest z programami w jzyku C i innych jzykach programowania. Pojedyncze
znaki programu nie znacz prawie nic, jeli rozpatrywa je osobno; znaczenia nabieraj
dopiero w otoczeniu innych znakw, czyli w pewnym kontekcie. Dlatego w wierszu
kodu:


oba wystpienia znaku  znacz zupenie co innego. Mwic precyzyjniej, oba znaki wystpuj w rnych elementach leksykalnych: pierwszy jest czci symbolu , drugi
czci cigu znakw. Dalej, znaczenie elementu leksykalnego  jest cakowicie rne
od znaczenia jego poszczeglnych znakw.
Pojcie symbolu czy te elementu leksykalnego odnosi si do jednostki programu, ktra
odgrywa w nim mniej wicej tak rol jak sowo w zdaniu element leksykalny ma
podobne znaczenie wszdzie, gdzie wystpuje. Identyczna sekwencja znakw moe
w jednym kontekcie nalee do jednego elementu leksykalnego, a w innym do
zupenie innego. Mechanizm kompilatora, ktry jest odpowiedzialny za podzia tekstu
programu na elementy leksykalne, nosi czsto nazw analizatora leksykalnego.
Rozwamy nastpujc instrukcj:

  


Pierwszym elementem tej instrukcji jest sowo . Nastpnym jest znak otwierajcy
nawias, dalej mamy identyfikator , symbol relacji wikszoci, identyfikator  itd.
W jzyku C pomidzy elementy leksykalne moemy wstawia dowoln liczb znakw
odstpw (spacji, tabulatorw i znakw nowego wiersza), wic instrukcj t moglibymy
zapisa rwnie tak:


16

W potrzasku jzyka C






W niniejszym rozdziale zajmiemy si szczegowo niektrymi najczciej popenianymi


bdami wynikajcymi z niezrozumienia znaczenia elementw leksykalnych i zalenoci pomidzy nimi a tworzcymi je znakami.

1.1. Porwnanie a przypisanie


W wikszoci jzykw programowania wywodzcych si z jzyka Algol, na przykad
w Pascalu czy Adzie, operacja przypisania reprezentowana jest znakami , a porwnania znakami . W jzyku C przypisanie reprezentuje znak , a porwnanie znaki
. Poniewa w programie czciej wystpuj przypisania ni porwnania, taka reprezentacja jest bardzo wygodna dla programisty, bo dla operacji czstszej stosuje krtszy
zapis. Co wicej, w jzyku C przypisanie jest operatorem, co umoliwia skadanie
wielu operacji przypisania w jednym wyraeniu.
Wygoda ta jest jednak rdem wielu bdw wynikajcych z omykowego zapisania
operatora przypisania w miejscu, gdzie powinien znajdowa si operator porwnania;
jak w poniszej instrukcji warunkowej, ktra powinna wykonywa instrukcj ,
kiedy  jest rwne :




W rzeczywistoci instrukcja ta przypisuje do  warto , a potem sprawdza, czy wynikiem operacji przypisania jest warto niezerowa. Nastpnym przykadem moe by
ptla, ktra miaa pomija w pliku znaki odstpw (spacji, tabulacji i nowego wiersza):
 
 
  

W ptli tej w pierwszym porwnaniu znaku


zamiast  zapisano . Poniewa operator
przypisania ma priorytet niszy od operatora , porwnanie w rzeczywistoci powoduje przypisanie do
wartoci wyraenia:


Poniewa warto jest rna od zera, cae wyraenie ma warto  niezalenie od


pierwotnej wartoci
. Dlatego ptla spowoduje przewinicie caego pliku. Co stanie si po wyczerpaniu znakw pliku, zaley od tego, czy implementacja jzyka pozwala na kontynuowanie odczytu po osigniciu koca pliku. Jeli tak, program wejdzie
w ptl nieskoczon.

Rozdzia 1. Puapki leksykalne

17

Niektre kompilatory jzyka C prbuj wspomaga uytkownikw (programistw),


prezentujc komunikaty ostrzegawcze o warunku w postaci w1 = w2. Kiedy wic
w programie zachodzi faktyczna potrzeba przypisania wartoci do zmiennej i ustalenia
wyniku operacji przypisania, to w celu uniknicia komunikatw ostrzegawczych naleaoby porwnanie zapisa jawnie, czyli zamiast:


 

napisa:


 

To upewni kompilator (i innych programistw) co do zamierze programisty. Przyczyna koniecznoci ujcia przypisania   w nawiasy zostaa omwiona w podrozdziale 2.2.
Czste s rwnie pomyki odwrotne, jak poniej:
     !"#
 

Funkcja  zastosowana w tym przykadzie zwraca , jeli wykryje bd otwarcia
pliku, oraz zero albo warto dodatni, kiedy operacja zakoczy si pomylnie. Powysza instrukcja ma wic zapisa wynik operacji otwarcia pliku w zmiennej 

i rwnoczenie dokona sprawdzenia wyniku operacji. Ale zamiast  napisano .


Z tego wzgldu kod ten faktycznie porwnuje warto funkcji  z wartoci
zmiennej 
i nastpnie sprawdza, czy wynik porwnania by wartoci ujemn.
Tymczasem porwnanie nigdy nie daje wartoci ujemnej, a jedynie albo  (dla rwnych operandw), albo  (jeli operandy s rne). Tak wic funkcja  nie zostanie
nigdy wywoana, niezalenie od wyniku operacji otwarcia pliku, a warto zmiennej

nie bdzie nijak odzwierciedla wartoci funkcji . Niektre kompilatory
ostrzegaj programistw o nieskutecznoci porwnywania z zerem, ale nie polegabym w takich przypadkach na kompilatorach.

1.2. & i | to nie to samo co && i ||


W przypadku operatorw porwnania i przypisania pomyki wynikaj zazwyczaj z nawykw wyniesionych z innych jzykw programowania, w ktrych porwnanie reprezentuje znak . Podobne nawyki dotycz rwnie operatorw  i  oraz i , a to dlatego, poniewa znaczenie  i jest w C odmienne ni w niektrych innych jzykach.
Dokadne znaczenie wszystkich wymienionych operatorw omawiane jest w podrozdziale 3.8.

18

W potrzasku jzyka C

1.3. Zachanno analizatora


leksykalnego
Niektre elementy leksykalne jzyka C, jak ,  czy  to cigi jednoznakowe. Inne z kolei
(,  czy wszelkiej maci identyfikatory) skadaj si z dwch i wikszej liczby
znakw. Kiedy kompilator napotyka w tekcie programu znak , a za nim znak , musi
zdecydowa, czy oba znaki traktowa jako niezalene elementy leksykalne czy te
poczy je w jeden element. W jzyku C wtpliwoci rozstrzygane s prost regu:
zawsze dopasowuj najduszy moliwy cig. Konwersja programu w jzyku C odbywa si od lewej do prawej, a za kadym razem z tekstu programu izolowany jest
element o najwikszej moliwej liczbie znakw. Taka strategia dopasowania nosi nazw
zachannej. Twrcy jzyka C, Kernighan i Ritchie, wyrazili t zasad nastpujco:
jeli cig wejciowy zosta do danego znaku podzielony na elementy leksykalne, nastpnym elementem leksykalnym jest najduszy cig znakw, ktry mona uzna za
element leksykalny. Elementy leksykalne, z wyjtkiem literaw znakowych i acuchowych (cigw), nie mog zawiera adnych znakw odstpw.
Jeli uwzgldni zasad zachannoci analizy leksykalnej, znaki  interpretuje si jako
pojedynczy element leksykalny, znaki   tworz dwa odrbne elementy, a zapis:


znaczy dokadnie tyle, co:




nigdy za:


Podobnie, jeli pierwszym znakiem elementu leksykalnego jest , a nastpnym ,


kompilator rozpoznaje dwuznakowy symbol rozpoczynajcy komentarz niezalenie
od kontekstu.
Ponisze wyraenie z pozoru ustawia zmienn wartoci  podzielon przez warto
wskazywan przez :

$%$%&'()*(  '%$

W rzeczywistoci znaki  rozpoczynaj komentarz, wic kompilator zignoruje cao


tekstu programu za tymi znakami, a do znakw symbolu koca komentarza . Innymi sowy, powysza instrukcja po prostu przypisuje  do , ignorujc zupenie
warto . Aby przed przypisaniem wykona dzielenie (jak stoi w komentarzu), naleaoby zapisa instrukcj tak:

$%$%&'()*(  '%$

albo choby tak:



$ %$%&'()*(  '%$

Tego rodzaju niejednoznacznoci powoduj problemy rwnie w innych kontekstach.

Rozdzia 1. Puapki leksykalne

19

Na przykad niegdy w jzyku C operacja przypisania zoonego, reprezentowana


dzi operatorem w rodzaju , reprezentowana bya znakami . Niektre kompilatory
do dzi akceptuj t archaiczn skadni, traktujc zapis:
+

jako:
+

To z kolei znaczy tyle co:


+

co z pewnoci byoby pewnym zaskoczeniem dla programisty, ktry najwyraniej


chcia wykona operacj:
+

Archaiczne kompilatory, o ktrych mowa, potraktowayby rwnie zapis:


$% 

jako:
$% 

pomimo e znaki  z pozoru zapowiadaj komentarz.


Starsze kompilatory interpretuj jako przypisania zoone rwnie rozczne elementy
leksykalne, przyjmujc bez protestw do kompilacji zapis:
+

ktry to zapis zostaby przez kompilator ANSI C odrzucony.

1.4. Literay staych cakowitych


Jeli pierwszy znak literau staej cakowitej to cyfra 0, to staa jest interpretowana jako
liczba semkowa. Std znaczenie literaw  i  jest odmienne. Co gorsza, wiele
kompilatorw C przyjmuje bez protestw literay staych semkowych zawierajce
cyfry semkowe  i . Warto takich literaw obliczana jest zgodnie z definicj
liczby semkowej, wic np. 0195 to tyle, co 182+981+580, czyli dziesitnie 141,
a semkowo: 0215. Oczywicie takie stosowanie literaw staych semkowych gorco odradzam. W jzyku ANSI C jest ono niedozwolone.
Niewaciwe wartoci staych semkowych daj si we znaki w kontekstach takich jak ten:
  ,
 -). 
 %  
/ !,
01"& 2*&"
03"& 2*&"
+45" "
/

20

W potrzasku jzyka C

1.5. Cigi a znaki


W jzyku C znaki pojedynczych i podwjnych cudzysoww maj zasadniczo rne
znaczenie, a ich mylenie w niektrych kontekstach powoduje bdy objawiajce si nie
tyle komunikatami kompilatora, co wanie niespodziewanym dziaaniem programu.
Znak ujty w znaki pojedynczego cudzysowu to po prostu alternatywny zapis liczby
cakowitej reprezentujcej w znak w zbiorze znakw przyjtym w danej implementacji. Jeli jest to zbir ASCII, to  oznacza tyle co   albo !.
Cig ujty w znaki cudzysoww podwjnych jest z kolei skrtowym zapisem wskanika
pierwszego znaku nienazwanej tablicy inicjalizowanej znakami skadajcymi si na cig
i uzupenianej dodatkowym znakiem o wartoci zerowej.
Zapis:
  62*"( 

jest rwnoznaczny zapisowi:


 2 !,6"2""*""""
""("" """""/
  2

Poniewa znak w pojedynczych cudzysowach reprezentuje warto cakowit, a znak


w cudzysowach podwjnych reprezentuje wskanik, kompilator, kontrolujc typy, wychwytuje zwykle miejsca, w ktrych wartoci te s stosowane odwrotnie. Std zapis:
 %2$

spowoduje bd kompilacji, poniewa  nie jest wskanikiem znaku. W niektrych


implementacjach jzyka kontrola typw nie jest jednak do szczegowa; dotyczy to
zwaszcza argumentw funkcji ". Zapis:
  

zamiast:
  

moe wtedy powodowa niespodzianki podczas wykonania programu mimo bezbdnej


kompilacji. Inne tego rodzaju przypadki zostay szczegowo omwione w podrozdziale 4.4.
Poniewa liczba cakowita jest w jzyku C z reguy na tyle pojemna, e mieci kilka
wartoci znakowych, niektre kompilatory jzyka C dopuszczaj stosowanie wieloznakowych staych znakowych; oznacza to, e mona zapisa "# w miejsce
$"#$ i kompilator nie zgosi bdu. Jednak naley pamita, e drugi z tych zapisw
oznacza adres pierwszego z czterech kolejnych komrek pamici zawierajcych
znaki ",  i #, podczas gdy znaczenie zapisu "# (cho niezdefiniowane dokadnie)
w wielu implementacjach jest rwnoznaczne liczbie cakowitej zoonej w jaki sposb z wartoci znakw ",  i #.

Rozdzia 1. Puapki leksykalne

21

wiczenie 1.1
Niektre z kompilatorw jzyka C pozwalaj na stosowanie komentarzy zagniedonych. Napisz program w jzyku C, ktry potrafi wykry, za pomoc ktrego z kompilatorw zosta skompilowany, nie prowokujc przy tym adnych bdw kompilacji.
Innymi sowy, kod programu powinien by poprawny dla obu regu interpretacji komentarzy, ale dziaa rnie dla rnych interpretacji. Wskazwka: symbol komentarza
wewntrz cigu znakw jest interpretowany jako fragment cigu znakw; znaki $$
wewntrz komentarza to cz komentarza.

wiczenie 1.2
Gdyby pisa kompilator jzyka C, czy pozwoliby jego uytkownikom na zagniedanie komentarzy? Gdyby dysponowa kompilatorem dajcym moliwo zagniedania
komentarzy, czy korzystaby z tej moliwoci? Czy odpowied na drugie z tych pyta
wpywa na odpowied na pytanie pierwsze?

wiczenie 1.3
Dlaczego  oznacza   , a nie   ?

wiczenie 1.4
Co oznacza zapis ?

You might also like