C Traps &Pitfalls
Höftberger
C Traps & Pitfalls
Oliver Höftberger
basierend auf Slides von Roland Kammerer
Institut für Technische InformatikTechnische Universität Wien
-182.709 Betriebssysteme UE
WS14
28. Oktober 2014
1/41
C Traps &Pitfalls
Höftberger
zu C. . .
Andrew Koenig - AT&T Bell Labs:
The C language is like a carving knife: simple, sharp,and extremely useful in skilled hands. Like any sharptool, C can injure people who don’t know how tohandle it.
2/41
C Traps &Pitfalls
Höftberger
Überblick
1. Lexikalische Fallstricke
2. Syntaktische Fallstricke
3. Semantische Fallstricke
4. Allgemeine FallstrickeI Dynamische SpeicherallokierungI Strings, Arrays und PointerI Makros
5. Coding/Style Guidelines
3/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
Teil I
Lexikalische Fallstricke
4/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
Allgemeines
I Bei der Übersetzung wird der Source Code zwei mal inToken geteilt
I Präprozessor (z.B. zur Makroersetzung)
I Der Compiler selbst um ein ausführbares Programm zuerstellen
5/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
= vs. ==
I = wird für Zuweisungen verwendet
I == für Vergleiche auf Gleichheit
if (x = y) /* set x to y and check if not zero */{
foo();}
while (c == ’ ’ || c = ’\t’ || c == ’\n’){
c = getc(f);}
6/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
= vs. ==
Will man wirklich ein Zuweisung machen und diese auswerten,sollte die Überprüfung explizit passieren.
if ( (x = y) != 0 ){
foo();}
x = y;if ( x != 0 ){
foo();}
7/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
& and | vs. && and ||
I & und | sind bitweise Operatoren
I && und || sind logische Operatoren
int a = 0x4, b = 0x2;
if (a && b) /* true */{
foo();}
if (a & b) /* false */{
foo();}
8/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
Multi Character Token
I Compiler muss entscheiden ob aufeinanderfolgendeZeichen zu einem Token gehören, oder ob sie zweigetrennte Token sind
y = x/*p /* p points at divisor */;/* Compiler interpretiert erstes "/*" als
Anfang eines Kommentars (-Wall hilft) */
/* besser */y = x / *p /* p points at divisor */;y = x /(*p) /* p points at divisor */;y = x / *p; /* comment after statement */
a=-1;/* alte Compiler interpretieren es als: */a =- 1; /* a.k.a a -= 1 a.k.a a = a - 1 */
9/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
Multi vs. Singletoken
Token: An atomic parse element.
a + /* blabla */ = 1; /* Multitoken, gültig *//* wird zu a += 1; */p - > a; /* -> ist single Token, nicht gültig *//* >> ist ein Singletoken >>= sind also zwei Token */
10/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
Strings and Characters
I "" wird für Strings verwendet
I ’’ wird für einzelne Zeichen verwendet und ist nur einanderer Weg Integer zu schreiben (z.B. ’a’ gleich 97)
char c = ’a’;char *string = "hallo";/* gleiches verhalten, unterschied später: */char string[] = {’h’, ’a’, ’l’, ’l’, ’o’, ’\0’};
11/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
Strings and Characters
Strings können nicht direkt zugewiesen werden, man mussString-Funktionen verwenden (#include <string.h>)
char buffer[256];
buffer = "hallo"; /* Fehler */strcpy(buffer, "hallo"); /* OK */
char *buf = "abc";
buf = "def"; /* OK */printf("%s\n", buf);
12/41
C Traps &Pitfalls
Höftberger
LexikalischeFallstrickeAllgemeines
= vs. ==
& and | vs. &&and ||
Multi CharacterToken
Strings andCharacters
Wichtige String-Funktionen
Funktion Bedeutung
strlen(3) Länge des Strings (exkl. ’\0’)strstr(3) Sucht einen Teilstringstrcpy(3), strncpy(3) Kopiert einen Stringstrdup(3), strndup(3) Dupliziert einen Stringstrcmp(3), strncmp(3) Vergleicht einen Stringstrtol(3), strtoll(3) String → ganze Zahlstrtof(3), strtod(3), String → Gleitkommazahlstrtold(3)
I Die Zahl in Klammern gibt das entsprechende Kapitel derman-page an (man 3 strlen)
13/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Teil II
Syntaktische Fallstricke
14/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Deklarationen
I int f, ((g)); f und g liefern int
I int ff(); Funktion, retourniert int
I int *g(), (*h)(); g retourniert Pointer auf int, h istein Pointer auf eine Funktion die int retourniert
15/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Cast
I Cast ist wie eine Deklaration aber ohne Variablenname,ohne Strichpunkt und in runden Klammern
I Deklaration: int (*h)();
I Cast: (int (*)())
16/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Aufgabe
Aufruf einer Funktion, deren Adresse an der Speicheradresse 0liegt
I Versuch: (*0)(); Fehler: 0 hat den falschen Typ
I Es wird ein Objekt der Art void (*fp)(); benötigt
I Lösung mit Cast: (*(void(*)())0)();
17/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Operator Precedence
I Immer auf die Reihenfolge der Auswertung achten
I Make it explicit! Besser „zu viele“ Klammern als zu wenig
if (flags & FLAG) .../* besser etwas expliziter ? */if (flag & FLAG != 0) .../* PROBLEM: != bindet stärker als & *//* Compiler interpretiert es als: */if (flag & (FLAG != 0))
r = h<<4 + 1;/* + bindet stärker, Compiler interpretiert: */r = h << (4 + 1);
c = a---b; /* c = a-- - b; */
18/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Operator Precedence Table1
Operator Bedeutung
() Funktionsaufruf[] Arrayindizierung. -> Struktur-Elementauswahl++ -- Postfix increment/decrement++ -- Prefix increment/decrement+ - ! ˜ unäre Operatoren: plus/minus/logische
Negation/bitweise Negation(type) Typecasts* & Dereferenzierung/Adressoperatorsizeof Ermittlung des Speicherbedarfs in byte* / % Multiplikation/Division/Restbildung+ - Addition/Subtraktion<< >> bitweise Shift links/rechts
1http://www.difranco.net/compsci/C_Operator_Precedence_Table.htm19/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Operator Precedence TableOperator Bedeutung
< <= > >= relationale Operatoren: kleiner als/kleinergleich/größer als/größer gleich
== != relationale Operatoren: gleich/ungleich& bitweise UND^ bitweise exklusive ODER| bitweise inklusive ODER&& logisches UND|| logisches ODER? : Ternärer Operator= Zuweisung+= -= *= Zuw. mit Addition/Subtraktion/Multiplikation/= %= &= Zuw. mit Division/Restbildung/bitweisem UND^= |= Zuw. mit exklusivem ODER/inklusivem ODER<<= >>= Zuw. mit bitweisem Shift links/rechts, Trennungsoperator
20/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Semicolons
if (x[i] > big);big = x[i];
Die Zuweisung wird immer ausgeführt!
21/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
switch
I Bei switch nicht auf break vergessen, sonst „fällt“ manin den nächsten case
I Immer einen default case machen
int color = 2;
/* schlecht */switch (color) {case 1: printf("red");case 2: printf("green");case 3: printf("blue");}/* prints "greenblue" */
22/41
C Traps &Pitfalls
Höftberger
SyntaktischeFallstrickeDeklarationen
Cast
OperatorPrecedence
Semicolons
switch
Dangling else
Dangling else
I else wird immer mit dem nächstgelegenen, nichtgematchten if assoziiert
if (x == 0)if (y == 0) error();
else {z = x + y;f(&z);
}/* eigentlich: */if (x == 0) {
if (y == 0)error();
else {z = x + y;f(&z);
}}
23/41
C Traps &Pitfalls
Höftberger
SemantischeFallstrickePointer undArrays
Reihenfolge derAuswertung
Teil III
Semantische Fallstricke
24/41
C Traps &Pitfalls
Höftberger
SemantischeFallstrickePointer undArrays
Reihenfolge derAuswertung
Pointer und Arrays
I C organisiert Arrays als linear fortlaufendenSpeicherbereich
I C89: Arraygröße wird zur Compilezeit fixiert, C99: VariableGröße möglich (z.B.: char buf[argc])
I Zwei Arrayoperationen:1. Ermitteln der Größe des Arrays2. Retournieren der Adresse von Element 0
I Alle anderen Operationen werden durchPointeroperationen realisiert (z.B. Indizierung)
int a[] = {1, 2, 3, 4, 5};printf("%d\n", a[3]); /* OK */printf("%d\n", 3[a]); /* OK! aber unschoen*/
25/41
C Traps &Pitfalls
Höftberger
SemantischeFallstrickePointer undArrays
Reihenfolge derAuswertung
Arrays als Parameter
I Bei der Übergabe eines Arrays als Parameter wird derParameter in einen Pointer auf das erste Elementumgewandelt
I Compiler konvertiert Array-Parameterdeklarationen inensprechende Pointerdeklarationen
int main(int argc, char **argv) { ... }int main(int argc, char *argv[]) { ... }/* beide sind vollkommen gleichbedeutend */
I Diese Konversion (Äquivalenz) gilt nur bei Parametern
26/41
C Traps &Pitfalls
Höftberger
SemantischeFallstrickePointer undArrays
Reihenfolge derAuswertung
Unterschiede: Pointer und Arrays2
I Ich habe gehört Pointer und Arrays sind in C äquivalent.Nein! Sie verhalten sich nur ähnlich
char a[] = "hello";char *p = "world";
I a[3]: Beginne bei a, gehe drei weiter, hole diesesElement
I p[3]: Beginne bei p, hole den Inhalt (die Adresse), zähledrei zu dieser Adresse hinzu, hole das Element auf das dieAdresse zeigt
2http://www.c-faq.com27/41
C Traps &Pitfalls
Höftberger
SemantischeFallstrickePointer undArrays
Reihenfolge derAuswertung
Unterschiede: Pointer und Arrays
int a[] = {1,2,3};int *p = &a[0];
printf("%d %d\n", a[2], p[2]); /* 3 3 */printf("%ld %ld %ld\n",
sizeof(a), sizeof(p), sizeof((*p));/* auf meinem pc: 12 8 4 */
#define NRELEMENTS(a) (sizeof(a) / sizeof(a[0]))Funktioniert für Arrays aber nicht für Pointer
28/41
C Traps &Pitfalls
Höftberger
SemantischeFallstrickePointer undArrays
Reihenfolge derAuswertung
Auswertungsreihenfolge
I Reihenfolge nur für vier Operatoren definiert: &&, ||, ?:und ,
a < b && c < d
I a < b wird sicher vor c < d ausgewertet, aber ob a vorb oder b vor a ausgewertet wird ist compilerspezifisch
while (i < n)y[i] = x[i++];
I Die Auswertungsreihenfolge ist abermals nicht festgelegt
I Gedankenmodell: Mit Hilfe von Precedence undAssociativity wird ein Baum aufgebaut. Die Reihenfolge inder die Blätter ausgewertet werden ist nicht festgelegt
29/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
Teil IV
Allgemeines
30/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
malloc
I Wie bei allen Funktionen ist der Rückgabewert zuüberprüfen. Im Fall von malloc ist dies besonders wichtig
char *p;
p = malloc(sizeof (*p) * 6);/* strlen + 1 */if (p == NULL){
bailout();}else{
strcpy(p, "hallo");}
free(p); /* nicht auf free vergessen! */
31/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
freeI Dynamisch allokierter Speicher muss wieder frei gegeben
werdenI Achtung bei Pointerarithmetik
char *p;
p = malloc(sizeof (*p) * 6);/* strlen + 1 */if (p == NULL) {
bailout();}else {
strcpy(p, "hallo");}
p += 3;free(p); /* segfault */
p -= 3;free(p); /* wieder okay */
32/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
So nicht
char str1[] = "text"; /* ok */char *str2 = str1; /* ok */char *str3; /* ok */strcpy(str2, "out of bounds"); /* Fehler */strcpy(str3, "hallo"); /* kein Speicher vorhanden */
char *str = "";strcpy(str, "hallo");/* Konstantenbereich wird überschrieben *//* kein Warning, aber Segfault */
char str[MAX_LENGTH];strcpy(str, str_from_user);/* bösartiger User kann Überlauf erzeugen */
33/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
So schon
char str[MAX_LENGTH];
strncpy(str, str_from_user, MAX_LENGTH - 1);str[MAX_LENGTH - 1] = ’\0’;
$ man 3 strncpy #man strncpyThe strncpy() function is similar, except that at most nbytes of src are copied. Warning: If there is no nullbyte among the first n bytes of src, the string placed indest will not be null terminated.
Randnotiz: OpenBSD Entwickler haben das Problem seit über10 Jahren gelöst (strlcpy). strlcpy ist aber nicht in der CStandard Library ⇒ Problem bei Portabilität
34/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
Makros
#define SQR(x) x*x
int a = 2;int b = 2;SQR(a) /* 4 */SQR(a+b) /* a + b * a + b == 8 */SQR(a++) /* a++ * a++ == 4, a == 4, undef */a = 2;SQR(++a) /* ++a * ++a == 16, a == 4, undef */
I Abermals ist Vorsicht bei der eventuell nicht definiertenAuswertungsreihenfolge geboten!
I Lösung: Bessere Klammerung (siehe letzte VO) undVermeidung von nicht eindeutigen Ausdrücken
I http://c-faq.com, Frage 3.8
35/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
Mehrere Statements
#define CMDS \a = b; \c = d
if (var == 23)CMDS;
elsereturn;
Wird zu:
if (var == 23)a = b;
c = d; /* syntax error */else
return;
Ohne else würde c = d immer ausgeführt!
36/41
C Traps &Pitfalls
Höftberger
AllgemeinesDynamischeSpeicherver-waltung
Strings undPointer
Makros
Lösung
#define CMDS \do { \
a = b; \c = d; \
} while (0)
Wird zu:
if (var == 23)do {
a = b;c = d;
} while (0);else
return;
37/41
C Traps &Pitfalls
Höftberger
CodingGuidelines
Teil V
Coding Guidelines
38/41
C Traps &Pitfalls
Höftberger
CodingGuidelines
Guidelines/Conventions/StandardsOhne bestimmte Ordnung der Wichtigkeit:
I K&R (siehe Material)
I GNU Coding Standards:http://www.gnu.org/prep/standards/
I Linux Coding Style:/usr/src/linux/Documentation/CodingStyle
I OpenBSD Kernel Style: http://www.openbsd.org/cgi-bin/man.cgi?query=style
I CERT Secure Coding: https://www.securecoding.cert.org/confluence/display/seccode/CERT+Secure+Coding+Standards
I The Power of Ten:http://spinroot.com/gerard/pdf/P10.pdf
I OSUE Guidelines
Nachteil: Untereinander widersprüchlich
39/41
C Traps &Pitfalls
Höftberger
Material
Teil VI
Material
40/41
C Traps &Pitfalls
Höftberger
Material
Material
I http://www.c-faq.com
I C Traps and Pitfalls - Andrew Koenig
I http://de.wikibooks.org/wiki/C-Programmierung
I C Programming Language - Kernighan & Ritchie
41/41