Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00058 009754 11018350 na godz. na dobę w sumie
W potrzasku języka C - książka
W potrzasku języka C - książka
Autor: Liczba stron: 152
Wydawca: Helion Język publikacji: polski
ISBN: 83-7361-727-2 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c - programowanie
Porównaj ceny (książka, ebook, audiobook).

Każdy, nawet najbardziej doświadczony programista, popełnia błędy podczas pracy. Niektóre z nich wynikają z pośpiechu, inne -- z użycia niewłaściwych konstrukcji, operatorów lub typów. Większość z nich można wykryć i usunąć po kilku minutach uważnej lektury kodu. Zdarzają się jednak i takie błędy, których odnalezienie i skorygowanie zajmuje kilka dni. Błędy te są z reguły łatwe do uniknięcia, jeśli zrozumie się przyczyny ich powstawania.

Książka 'W potrzasku języka C' zawiera omówienie najczęściej spotykanych błędów i przyczyn ich powstawania. Nie zawiera ogólników -- jej atutem są konkretne, zaczerpnięte z praktyki, przykłady. Każdy programista prędzej czy później natknie się na jeden z prezentowanych w książce błędów i, dzięki zawartym w niej wiadomościom, będzie w stanie usunąć go i uniknąć w późniejszej pracy.

Nie trać czasu na usuwanie błędów --
dowiedz się, co robić, żeby w ogóle nie występowały.

O autorze:
Andrew Koenig jest członkiem działu badającego systemy oprogramowania w Shannon Laboratory firmy AT&T oraz redaktorem projektu komitetów standaryzacyjnych języka C++. [więcej...\

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

W potrzasku jêzyka C Autor: Andrew Koenig T³umaczenie: Przemys³aw Szeremiota ISBN: 83-7361-727-2 Tytu³ orygina³u: C Traps and Pitfalls Format: B5, stron: 152 Ka¿dy, nawet najbardziej doġwiadczony programista, pope³nia b³êdy podczas pracy. Niektóre z nich wynikaj¹ z poġpiechu, inne — z u¿ycia niew³aġciwych konstrukcji, operatorów lub typów. Wiêkszoġæ z nich mo¿na wykryæ i usun¹æ po kilku minutach uwa¿nej lektury kodu. Zdarzaj¹ siê jednak i takie b³êdy, których odnalezienie i skorygowanie zajmuje kilka dni. B³êdy te s¹ z regu³y ³atwe do unikniêcia, jeġli zrozumie siê przyczyny ich powstawania. Ksi¹¿ka „W potrzasku C” zawiera omówienie najczêġciej spotykanych b³êdów i przyczyn ich powstawania. Nie zawiera ogólników - jej atutem s¹ konkretne, zaczerpniête z praktyki, przyk³ady. Ka¿dy programista prêdzej czy póĥniej natknie siê na jeden z prezentowanych w ksi¹¿ce b³êdów i, dziêki zawartym w niej wiadomoġciom, bêdzie w stanie usun¹æ go i unikn¹æ w póĥniejszej pracy. • B³êdy leksykalne i sk³adniowe • Przepe³nienie zakresu • Problemy z konsolidacj¹ • W³aġciwe stosowanie funkcji bibliotecznych • Makrodefinicje • Przenoġnoġæ kodu Nie traæ czasu na usuwanie b³êdów — dowiedz siê co robiæ, ¿eby w ogóle nie wystêpowa³y. IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TREĎCI SPIS TREĎCI KATALOG KSI¥¯EK KATALOG KSI¥¯EK KATALOG ONLINE KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK TWÓJ KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE ZAMÓW INFORMACJE O NOWOĎCIACH O NOWOĎCIACH ZAMÓW CENNIK ZAMÓW CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl Spis treści . Wstęp ...................................................o............................................ 7 Wprowadzenie ...................................................o.............................. 11 Rozdział 1. Pułapki leksykalne...................................................o........................ 15 1.1. Porównanie a przypisanie...................................................a...................................... 16 1.2. i | to nie to samo co i || ...................................................a................................ 17 1.3. Zachłanność analizatora leksykalnego...................................................a................... 18 1.4. Literały stałych całkowitych...................................................a.................................. 19 1.5. Ciągi a znaki...................................................a...................................................a.......20 Rozdział 2. Pułapki składniowe ...................................................o....................... 23 2.1. Deklaracje funkcji ...................................................a................................................. 23 2.2. Priorytety operatorów ...................................................a............................................ 26 2.3. Uwaga na średniki!...................................................a................................................ 30 2.4. Instrukcja wyboru switch...................................................a....................................... 32 2.5. Wywołania funkcji ...................................................a................................................ 33 2.6. Klauzula else w zagnieżdżonych instrukcjach if ...................................................a... 34 Rozdział 3. Pułapki semantyczne ...................................................o.................... 37 3.1. Wskaźniki i tablice ...................................................a................................................ 37 3.2. Wskaźniki nie są tablicami ...................................................a.................................... 42 3.3. Deklaracje tablic w roli parametrów...................................................a...................... 43 3.4. Niebezpieczne synekdochy...................................................a.................................... 45 3.5. Wskaźniki puste a ciągi niepuste ...................................................a........................... 46 3.6. Zliczanie a asymetryczne granice zakresów ...................................................a.......... 46 3.7. Kolejność obliczania w wyrażeniu ...................................................a........................ 55 3.8. Operatory , || i !...................................................a................................................ 57 3.9. Przepełnienie zakresu liczby całkowitej ...................................................a................ 58 3.10. Zwracanie wartości przez funkcję main ...................................................a................ 59 Rozdział 4. Konsolidacja...................................................o................................. 63 4.1. Czym jest konsolidator? ...................................................a........................................ 63 4.2. Deklaracje a definicje ...................................................a............................................ 65 4.3. Kolizje nazw i słowo static ...................................................a.................................... 66 4.4. Argumenty, parametry i wartości funkcji ...................................................a.............. 67 4.5. Kontrola typów obiektów zewnętrznych ...................................................a............... 72 4.6. Pliki nagłówkowe ...................................................a.................................................. 75 6 W potrzasku języka C Rozdział 5. Funkcje biblioteczne ...................................................o..................... 77 5.1. Funkcja getchar zwraca wartość typu int...................................................a............... 78 5.2. Aktualizacja pliku sekwencyjnego ...................................................a........................ 78 5.3. Buforowanie wyjścia i przydział pamięci...................................................a.............. 80 5.4. Diagnostyka błędów funkcją errno ...................................................a........................ 81 5.5. Funkcja signal ...................................................a...................................................a.... 82 Rozdział 6. Preprocesor ...................................................o.................................. 85 6.1. Odstępy w makrodefinicjach ...................................................a................................. 86 6.2. Makrodefinicje a funkcje...................................................a....................................... 86 6.3. Makrodefinicje a instrukcje ...................................................a................................... 90 6.4. Makrodefinicje a definicje typów ...................................................a.......................... 91 Rozdział 7. Kwestie przenośności ...................................................o................... 93 7.1. W obliczu zmian...................................................a...................................................a. 94 7.2. Co z nazwami? ...................................................a...................................................a... 95 7.3. Rozmiar liczby całkowitej ...................................................a..................................... 96 7.4. Czy znaki mają znaki?...................................................a........................................... 97 7.5. Operatory przesunięć bitowych ...................................................a............................. 98 7.6. Zerowa komórka pamięci ...................................................a...................................... 99 7.7. Obcinanie przy dzieleniu ...................................................a..................................... 100 7.8. Rozmiar liczby losowej ...................................................a....................................... 101 7.9. Zamiana wielkości liter ...................................................a....................................... 102 7.10. Najpierw zwalniać, potem przydzielać ponownie?................................................. 103 7.11. Przykładowe problemy nieprzenośności ...................................................a............. 104 Rozdział 8. Porady i odpowiedzi do ćwiczeń...................................................o... 109 8.1. Porady ...................................................a...................................................a..............110 8.2. Odpowiedzi do ćwiczeń ...................................................a...................................... 113 Dodatek A Funkcja printf i zmienne listy argumentów ...................................... 129 A.1. Rodzina funkcji printf...................................................a......................................... 129 A.1.1. Proste specyfikatory formatu...................................................a..................... 131 A.1.2. Modyfikatory...................................................a............................................. 135 A.1.3. Znaczniki...................................................a...................................................a 138 A.1.4. Zmienna precyzja i szerokość pola...................................................a............ 140 A.1.5. Neologizmy ...................................................a............................................... 141 A.1.6. Anachronizmy ...................................................a........................................... 141 A.2. Zmienne listy argumentów — varargs.h...................................................a............. 142 A.2.1. Implementacja varargs.h...................................................a............................ 146 A.3. Zmienne listy argumentów w wydaniu ANSI — stdarg.h ..................................... 147 Skorowidz...................................................o................................... 149 Rozdział 1. Pułapki leksykalne Czytając zdanie, nie zastanawiamy się nad znaczeniem poszczególnych liter, tworzących kolejne słowa. Litery same w sobie niosą bowiem niewiele treści; grupujemy je więc w słowa i im przypisujemy znaczenie. Podobnie jest z programami w języku C i innych językach programowania. Pojedyncze znaki programu nie znaczą prawie nic, jeśli rozpatrywać je osobno; znaczenia nabierają dopiero w otoczeniu innych znaków, czyli w pewnym kontekście. Dlatego w wierszu kodu: R U  oba wystąpienia znaku  znaczą zupełnie co innego. Mówiąc precyzyjniej, oba znaki wy- stępują w różnych elementach leksykalnych: pierwszy jest częścią symbolu  , drugi częścią ciągu znaków. Dalej, znaczenie elementu leksykalnego  jest całkowicie różne od znaczenia jego poszczególnych znaków. Pojęcie symbolu czy też elementu leksykalnego odnosi się do jednostki programu, która odgrywa w nim mniej więcej taką rolę jak słowo w zdaniu — element leksykalny ma podobne znaczenie wszędzie, gdzie występuje. Identyczna sekwencja znaków może w jednym kontekście należeć do jednego elementu leksykalnego, a w innym — do zupełnie innego. Mechanizm kompilatora, który jest odpowiedzialny za podział tekstu programu na elementy leksykalne, nosi często nazwę analizatora leksykalnego. Rozważmy następującą instrukcję: KH Z DKI DKIZ Pierwszym elementem tej instrukcji jest słowo KH. Następnym jest znak otwierający nawias, dalej mamy identyfikator Z, symbol relacji większości, identyfikator DKI itd. W języku C pomiędzy elementy leksykalne możemy wstawiać dowolną liczbę znaków odstępów (spacji, tabulatorów i znaków nowego wiersza), więc instrukcję tę moglibyśmy zapisać również tak: KH Z 16 W potrzasku języka C DKI DKI  Z  W niniejszym rozdziale zajmiemy się szczegółowo niektórymi najczęściej popełnianymi błędami wynikającymi z niezrozumienia znaczenia elementów leksykalnych i zależ- ności pomiędzy nimi a tworzącymi je znakami. 1.1. Porównanie a przypisanie W większości języków programowania wywodzących się z języka Algol, na przykład w Pascalu czy Adzie, operacja przypisania reprezentowana jest znakami , a porówna- nia — znakami . W języku C przypisanie reprezentuje znak , a porównanie — znaki . Ponieważ w programie częściej występują przypisania niż porównania, taka repre- zentacja jest bardzo wygodna dla programisty, bo dla operacji częstszej stosuje krótszy zapis. Co więcej, w języku C przypisanie jest operatorem, co umożliwia składanie wielu operacji przypisania w jednym wyrażeniu. Wygoda ta jest jednak źródłem wielu błędów wynikających z omyłkowego zapisania operatora przypisania w miejscu, gdzie powinien znajdować się operator porównania; jak w poniższej instrukcji warunkowej, która powinna wykonywać instrukcję DTGCM, kiedy Z jest równe [: KH Z[ DTGCM W rzeczywistości instrukcja ta przypisuje do Z wartość [, a potem sprawdza, czy wyni- kiem operacji przypisania jest wartość niezerowa. Następnym przykładem może być pętla, która miała pomijać w pliku znaki odstępów (spacji, tabulacji i nowego wiersza): YJKNG E  ^^E V ^^E P  EIGVE H  W pętli tej w pierwszym porównaniu znaku E zamiast  zapisano . Ponieważ operator przypisania ma priorytet niższy od operatora ^^, „porównanie” w rzeczywistości po- woduje przypisanie do E wartości wyrażenia:  ^^E V ^^E P Ponieważ wartość  jest różna od zera, całe wyrażenie ma wartość  niezależnie od pierwotnej wartości E. Dlatego pętla spowoduje „przewinięcie” całego pliku. Co sta- nie się po wyczerpaniu znaków pliku, zależy od tego, czy implementacja języka po- zwala na kontynuowanie odczytu po osiągnięciu końca pliku. Jeśli tak, program wejdzie w pętlę nieskończoną. Rozdział 1. ♦ Pułapki leksykalne 17 Niektóre kompilatory języka C próbują wspomagać użytkowników (programistów), prezentując komunikaty ostrzegawcze o warunku w postaci w1 = w2. Kiedy więc w programie zachodzi faktyczna potrzeba przypisania wartości do zmiennej i ustalenia wyniku operacji przypisania, to w celu uniknięcia komunikatów ostrzegawczych na- leżałoby porównanie zapisać jawnie, czyli zamiast: KH Z[ HQQ  napisać: KH Z[  HQQ  To upewni kompilator (i innych programistów) co do zamierzeń programisty. Przy- czyna konieczności ujęcia przypisania Z[ w nawiasy została omówiona w podroz- dziale 2.2. Częste są również pomyłki odwrotne, jak poniżej: KH HKNGFGUEQRGP CTIX=K?  GTTQT  Funkcja QRGP zastosowana w tym przykładzie zwraca , jeśli wykryje błąd otwarcia pliku, oraz zero albo wartość dodatnią, kiedy operacja zakończy się pomyślnie. Powyż- sza instrukcja ma więc zapisać wynik operacji otwarcia pliku w zmiennej HKNGFGUE i równocześnie dokonać sprawdzenia wyniku operacji. Ale zamiast  napisano . Z tego względu kod ten faktycznie porównuje wartość funkcji QRGP z wartością zmiennej HKNGFGUE i następnie sprawdza, czy wynik porównania był wartością ujemną. Tymczasem porównanie nigdy nie daje wartości ujemnej, a jedynie albo  (dla rów- nych operandów), albo  (jeśli operandy są różne). Tak więc funkcja GTTQT nie zostanie nigdy wywołana, niezależnie od wyniku operacji otwarcia pliku, a wartość zmiennej HKNGFGUE nie będzie nijak odzwierciedlać wartości funkcji QRGP. Niektóre kompilatory ostrzegają programistów o nieskuteczności porównywania z zerem, ale nie polegał- bym w takich przypadkach na kompilatorach. 1.2. i | to nie to samo co i || W przypadku operatorów porównania i przypisania pomyłki wynikają zazwyczaj z na- wyków wyniesionych z innych języków programowania, w których porównanie repre- zentuje znak . Podobne nawyki dotyczą również operatorów  i  oraz ^ i ^^, a to dla- tego, ponieważ znaczenie  i ^ jest w C odmienne niż w niektórych innych językach. Dokładne znaczenie wszystkich wymienionych operatorów omawiane jest w podroz- dziale 3.8. 18 W potrzasku języka C 1.3. Zachłanność analizatora leksykalnego Niektóre elementy leksykalne języka C, jak , czy  to ciągi jednoznakowe. Inne z kolei ( ,  czy wszelkiej maści identyfikatory) składają się z dwóch i większej liczby znaków. Kiedy kompilator napotyka w tekście programu znak , a za nim znak , musi zdecydować, czy oba znaki traktować jako niezależne elementy leksykalne czy też połączyć je w jeden element. W języku C wątpliwości rozstrzygane są prostą regułą: „zawsze dopasowuj najdłuższy możliwy ciąg”. Konwersja programu w języku C od- bywa się od lewej do prawej, a za każdym razem z tekstu programu izolowany jest element o największej możliwej liczbie znaków. Taka strategia dopasowania nosi nazwę zachłannej. Twórcy języka C, Kernighan i Ritchie, wyrazili tę zasadę następująco: „jeśli ciąg wejściowy został do danego znaku podzielony na elementy leksykalne, na- stępnym elementem leksykalnym jest najdłuższy ciąg znaków, który można uznać za element leksykalny”. Elementy leksykalne, z wyjątkiem literałów znakowych i łańcu- chowych (ciągów), nie mogą zawierać żadnych znaków odstępów. Jeśli uwzględnić zasadę zachłanności analizy leksykalnej, znaki  interpretuje się jako pojedynczy element leksykalny, znaki  tworzą dwa odrębne elementy, a zapis: CD znaczy dokładnie tyle, co: CD nigdy zaś: CD Podobnie, jeśli pierwszym znakiem elementu leksykalnego jest , a następnym , kompilator rozpoznaje dwuznakowy symbol rozpoczynający komentarz niezależnie od kontekstu. Poniższe wyrażenie z pozoru ustawia zmienną [ wartością Z podzieloną przez wartość wskazywaną przez R: [Z R RYUMCWLGPCFKGNPKM  W rzeczywistości znaki  rozpoczynają komentarz, więc kompilator zignoruje całość tekstu programu za tymi znakami, aż do znaków symbolu końca komentarza . In- nymi słowy, powyższa instrukcja po prostu przypisuje Z do [, ignorując zupełnie wartość R. Aby przed przypisaniem wykonać dzielenie (jak stoi w komentarzu), nale- żałoby zapisać instrukcję tak: [Z R RYUMCWLGPCFKGNPKM  albo choćby tak: [Z R  RYUMCWLGPCFKGNPKM  Tego rodzaju „niejednoznaczności” powodują problemy również w innych kontekstach. Rozdział 1. ♦ Pułapki leksykalne 19 Na przykład niegdyś w języku C operacja przypisania złożonego, reprezentowana dziś operatorem w rodzaju , reprezentowana była znakami  . Niektóre kompilatory do dziś akceptują tę archaiczną składnię, traktując zapis: C jako: C To z kolei znaczy tyle co: CC co z pewnością byłoby pewnym zaskoczeniem dla programisty, który najwyraźniej chciał wykonać operację: C „Archaiczne” kompilatory, o których mowa, potraktowałyby również zapis: C D jako: C D pomimo że znaki  z pozoru zapowiadają komentarz. Starsze kompilatory interpretują jako przypisania złożone również rozłączne elementy leksykalne, przyjmując bez protestów do kompilacji zapis: C  który to zapis zostałby przez kompilator ANSI C odrzucony. 1.4. Literały stałych całkowitych Jeśli pierwszy znak literału stałej całkowitej to cyfra 0, to stała jest interpretowana jako liczba ósemkowa. Stąd znaczenie literałów  i  jest odmienne. Co gorsza, wiele kompilatorów C przyjmuje bez protestów literały stałych ósemkowych zawierające cyfry „ósemkowe”  i . Wartość takich literałów obliczana jest zgodnie z definicją liczby ósemkowej, więc np. 0195 to tyle, co 182+981+580, czyli dziesiętnie 141, a ósemkowo: 0215. Oczywiście takie stosowanie literałów stałych ósemkowych gorą- co odradzam. W języku ANSI C jest ono niedozwolone. Niewłaściwe wartości stałych ósemkowych dają się we znaki w kontekstach takich jak ten: UVTWEV] KPVRCTVAPWODGT EJCT FGUETKRVKQP _RCTVVCD=?] YKEJCLUVGTNGY[ YKEJCLUVGTRTCY[ VGIGU _ 20 W potrzasku języka C 1.5. Ciągi a znaki W języku C znaki pojedynczych i podwójnych cudzysłowów mają zasadniczo różne znaczenie, a ich mylenie w niektórych kontekstach powoduje błędy objawiające się nie tyle komunikatami kompilatora, co właśnie niespodziewanym działaniem programu. Znak ujęty w znaki pojedynczego cudzysłowu to po prostu alternatywny zapis liczby całkowitej reprezentującej ów znak w zbiorze znaków przyjętym w danej implementa- cji. Jeśli jest to zbiór ASCII, to C oznacza tyle co  albo . Ciąg ujęty w znaki cudzysłowów podwójnych jest z kolei skrótowym zapisem wskaźnika pierwszego znaku nienazwanej tablicy inicjalizowanej znakami składającymi się na ciąg i uzupełnianej dodatkowym znakiem o wartości zerowej. Zapis: RTKPVH #JQLRT[IQFQ P  jest równoznaczny zapisowi: EJCTJGNNQ=?] #  J  Q  L       R  T   [  I  Q  F  Q  P _ RTKPVH JGNNQ  Ponieważ znak w pojedynczych cudzysłowach reprezentuje wartość całkowitą, a znak w cudzysłowach podwójnych reprezentuje wskaźnik, kompilator, kontrolując typy, wy- chwytuje zwykle miejsca, w których wartości te są stosowane odwrotnie. Stąd zapis: EJCT UNCUJ   spowoduje błąd kompilacji, ponieważ  nie jest wskaźnikiem znaku. W niektórych implementacjach języka kontrola typów nie jest jednak dość szczegółowa; dotyczy to zwłaszcza argumentów funkcji RTKPVH. Zapis: RTKPVH P  zamiast: RTKPVH  P  może wtedy powodować niespodzianki podczas wykonania programu mimo bezbłędnej kompilacji. Inne tego rodzaju przypadki zostały szczegółowo omówione w podroz- dziale 4.4. Ponieważ liczba całkowita jest w języku C z reguły na tyle pojemna, że mieści kilka wartości znakowych, niektóre kompilatory języka C dopuszczają stosowanie wielo- znakowych stałych znakowych; oznacza to, że można zapisać VCM w miejsce VCM i kompilator nie zgłosi błędu. Jednak należy pamiętać, że drugi z tych zapisów oznacza „adres pierwszego z czterech kolejnych komórek pamięci zawierających znaki V, C i M”, podczas gdy znaczenie zapisu VCM (choć niezdefiniowane dokładnie) w wielu implementacjach jest równoznaczne „liczbie całkowitej złożonej w jakiś spo- sób z wartości znaków V, C i M”. Rozdział 1. ♦ Pułapki leksykalne 21 Ćwiczenie 1.1 Niektóre z kompilatorów języka C pozwalają na stosowanie komentarzy zagnieżdżo- nych. Napisz program w języku C, który potrafi wykryć, za pomocą którego z kom- pilatorów został skompilowany, nie prowokując przy tym żadnych błędów kompilacji. Innymi słowy, kod programu powinien być poprawny dla obu reguł interpretacji ko- mentarzy, ale działać różnie dla różnych interpretacji. Wskazówka: symbol komentarza wewnątrz ciągu znaków jest interpretowany jako fragment ciągu znaków; znaki  wewnątrz komentarza to część komentarza. Ćwiczenie 1.2 Gdybyś pisał kompilator języka C, czy pozwoliłbyś jego użytkownikom na zagnieżdża- nie komentarzy? Gdybyś dysponował kompilatorem dającym możliwość zagnieżdżania komentarzy, czy korzystałbyś z tej możliwości? Czy odpowiedź na drugie z tych pytań wpływa na odpowiedź na pytanie pierwsze? Ćwiczenie 1.3 Dlaczego P  oznacza P , a nie P ? Ćwiczenie 1.4 Co oznacza zapis C D?
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

W potrzasku języka C
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: