45
C Fortgeschritten Kammerer C Fortgeschritten Roland Kammerer Institut für Technische Informatik Technische Universität Wien 12. März 2012

C Fortgeschritten - ti.tuwien.ac.at fileC Fortgeschritten Kammerer Präprozessor Allgemeines Makros Allgemeines I Präprozessor wird vor dem kompilieren aufgerufen I Erledigt relativ

Embed Size (px)

Citation preview

CFortgeschritten

Kammerer

C Fortgeschritten

Roland Kammerer

Institut für Technische InformatikTechnische Universität Wien

12. März 2012

CFortgeschritten

Kammerer

Überblick

1. Präprozessor

2. Komplexe Datentypen

3. Funktionen

4. Modulare Programmierung

CFortgeschritten

Kammerer

PräprozessorAllgemeines

Makros

Teil I

Präprozessor

CFortgeschritten

Kammerer

PräprozessorAllgemeines

Makros

Allgemeines

I Präprozessor wird vor dem kompilieren aufgerufen

I Erledigt relativ einfache Ersetzungen im Source (casesensitive)

I Source nach dem Präprozessor kann mit gcc -Eeingesehen werden

I MotivationI Früher: Definition von Konstanten. Inline CodeI Heute: Portabilität. Nutzen von Compilerspezifika

Aufgaben des Präprozessors (in zeitlicher Abfolge, nichtvollständig):

I Trigraph → ASCII (z.B. ??) wird zu ])

I Zusammenfassen von durch ’\’ getrennten Zeilen

I Makros ersetzen und Dateien (#include) in den Sourcekopieren

CFortgeschritten

Kammerer

PräprozessorAllgemeines

Makros

Ersetzung von Konstanten

#define ANSWER (42) /* Konstante */

printf("ANSWER: %d\n", ANSWER);

wird zu:

printf("ANSWER: %d\n", (42));

Es findet keine Ersetzung in Stringliteralen statt.

CFortgeschritten

Kammerer

PräprozessorAllgemeines

Makros

Bedingte Ersetzung

#if, #ifdef, #ifndef, #elif, #else, #endif

#ifdef WIN32#include <windows.h>#else#include <unistd.h>#endif

#if DEBUG >= 2printf("debug, debug\n");

#endif

CFortgeschritten

Kammerer

PräprozessorAllgemeines

Makros

Makros

I Komplexe Makros mit Parametern sind möglich

#define NRELEMENTS(a) (sizeof(a) / sizeof(a[0]))

I Makros bergen leider auch viele Gefahren/Seiteneffekte

CFortgeschritten

Kammerer

PräprozessorAllgemeines

Makros

Gefahren

#define DOUBLE(a) a+a

int x = DOUBLE(5) * 3;/* x = 5 + 5 * 3 <=> 5 + (5 * 3) *//* => #define DOUBLE (a) ( (a) + (a) )

#define DOUBLE (a) ( (a) + (a) )

int x = 3;int y = DOUBLE(++x);/* y = ( (++x) + (++x) ) */

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Teil II

Komplexe Datentypen

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Typumwandlung

I Kann implizit passieren

I Kann explizit (cast) passieren

I Syntax: (Zieltyp)Ausdruck;

/* implizit */int i = 5;float f = i;/* erweiternde typumwandlung */

/* explizit */float pi = 3.1415;int i = (int)pi;/* einschraenkende typumwandlung */

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Typumwandlung

I Großer ganzzahliger Typ → kleiner ganzzahliger Typ:Obersten Bits werden abgeschnitten

I Fließkommatyp → kleinerer Typ:I Teil nach dem Komma wird abgeschnitten (falls der Teil vor

dem Komma im kleineren integer Type dargestellt werdenkann).

I Sonst: Verhalten unbestimmt

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

voidI void war in frühen C-Versionen nicht vorhanden, mit

ANSI-C eingeführtI Läßt sich am ehesten mit „leerer Datentyp“ beschreiben

Verwendung:I als Ergebnistyp von FunktionenI für leere Parameterlisten von FunktionenI zur Kennzeichnung von Zeiger, denen kein Datentyp

zugeordnet ist

/* void vobject; */ /* nicht zulaessig */void *pv; /* OK: Zeiger auf ein beliebiges Objekt */int *pint; int i;

int foo(void){ /* foo besitzt keine Aufrufparameter */

pv = &i; /* OK */pint = (int *)pv; /* (int *) in C wahlweise,

in C++ erforderlich */return 0;

}

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Structs

I Structs fassen Variablen zu einer logischen Einheitzusammen

I Die Gesamtgröße entspricht prinzipiell der Summe derElemente

I Plus dem Platz der durch das Alignment verbraucht wirdI Zur Bestimmung sizeof verwenden

I Zugriff auf die Elemente erfolgt mit ’.’

struct account {char username[8];char password[8];int uid;

};

struct account user1;struct account user2 = {"john", "doe", 2};

user1.uid = 1;

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Structs

I Tagged Struct:

struct foo {int a;int b;

};struct foo one, two;

I Untagged Struct:

struct {int a;int b;

} one, two;

I Mixed:

struct foo {int a;int b;

} one, two;

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Structs

I Ab C-99 können die Felder eine Struct bei derInitialisierung mit dem Namen angesprochen werden

struct foo {int a;int b;

};

struct foo one = {.b = 23, .a = 42};

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Structs

I Wie auf Variablen sind natürlich auch Pointer auf Structsmöglich

I Dereferenzierung mit ->

struct account {char username[8];char password[8];int uid;

};

struct account user = {"john", "doe", 2};struct account *p = &user;

(*p).uid = 1;p->uid = 1; /* lesbarer */

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Structs#include <stdio.h>#include <stdlib.h>

struct foo{int *a;

};

int main (void){

struct foo x;struct foo *y;

y = &x;

x.a = malloc(sizeof (int));

*y->a = 42;

printf("%d\n", *x.a); /* 42 */printf("%d\n", *y->a); /* 42 */

return 0;}

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Unions

I Elemente einer Union bezeichnen im Unterschied zuStructs den selben Speicherbereich

I Die Gesamtgröße entspricht dem größten Element

I Es kann immer nur ein Element „aktiv“ sein

union zahl {char c_zahl; /* 1 Byte */short s_zahl; /* 1 Byte + 1 Byte */

};

union zahl z;z.s_zahl = 0x23;z.c_zahl = 0x5;

Bei der letzten Zuweisung wird nur die eine Hälfte der Unionverändert. Greift man jetzt wie auf eine s_zahl zu, ist dasErgebnis nicht immer definiert!

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Verschachtelung

I Unions und Structs können nahezu beliebig verschachteltwerden

union vector3d {struct { float x, y, z; } vec1;struct { float alpha, beta, gamma; } vec2;float vec3[3];

};

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Tags

I Häufig wird eine Union von einer Struct umschlossen diezusätzlich eine Variable besitzt die das aktuelle Feld derUnion bezeichnet

struct checkedUnion {int type;union intFloat {

int i;float f;

} intFloat1;};

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

enumI Wird verwendet um sprechende Alias-Namen zu vergeben.

I Wenn nicht anders angegeben bekommt das ersteElement den Wert 0

I Wird für ein Element keine Zahl angegeben, bekommt esden Wert des Vorgängers um eins erhöht

I Vorteil gegenüber von #define: Scope

enum [Typname] {Bezeichner [= Wert] {, Bezeichner [= Wert]}};

enum boolean {FALSE = 0, TRUE};enum Farben {rot, gruen, blau};enum Primzahl {

Zwei = 2, Drei, Fuenf = 5, Sieben = 7};

enum boolean mybool;mybool = FALSE;

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

enum als Tagsenum types {a_int, a_float};

struct checkedUnion {enum types type;union intFloat{

int i;float f;

} IntFloat1;};

int main(){

struct checkedUnion my_checked_union;

my_checked_union.type = a_float;if (my_checked_union.type == a_int){

my_checked_union.IntFloat1.i = 23;printf("%d\n", my_checked_union.IntFloat1.i);

}return 0; }

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

typedef

I Weist einem vorhandenen Typ eine neue Bezeichnung zu

I Besonders geeignet für Structs/Unions

typedef int MyFaNcY_InT;typedef struct {

char username[8];char password[8];int uid;

} user;

MyFaNcY_InT i = 23;user user1 = {"john", "doe", 2};

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Alignment

I Data Alignment: Wie werden Daten im Speicher abgelegt?

I Data Structure Padding: Wie werden die Zwischenräumegefüllt?

Typ Alignment1 (32-bit x86)

char 1-byteshort 2-byteint 4-bytefloat 4-bytedouble 8-byte (win), 4-byte (Linux)long double 4-byte (Linux)pointer 4-byte (Linux)

1für Compiler von Borland, Microsoft und GNU

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Alignment

struct mixed {char data1;short data2;int data3;char data4;

}; /* 8 bytes */

struct mixed { /* nach dem kompilieren */char data1;char padding1[1];short data2;int data3;char data4;char padding2[3]; /* auf groesztes Element */

}; /* 12 bytes */

CFortgeschritten

Kammerer

DatentypenCasts

void

Structs

Unions

enum

typedef

Alignment

Alignment

struct mixed { /* umordnen */int data3;short data2;char data1;char data4;

}; /* 8 bytes */

I Am besten Elemente einer Struct der Größe nachabsteigend anordnen.

I Warum macht das nicht der der Compiler? - Ein Compileroptimiert (ordnet die Elemente um), ein anderer nicht,beide greifen auf eine struct in einem shared memory zu⇒ Problem.

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Teil III

Funktionen

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Rückblick

Rückgabetyp Funktionsname(Parameterliste){

Anweisungen}

I Achtung: int foo(); != int foo(void);

I int foo(): foo nimmt eine unbestimmte Anzahl anParametern

I int foo(void): foo darf kein Parameter übergebenwerden

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Werteparameter

I Werteparameter sind für die Funktion lokal, sie werdennicht verändert

void foo(int a){

a = 23;}

int main(){

int a = 42;foo(a);/* a ist immer noch 42 */return 0;

}

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Variablenparameter

I Der Funktion wird ein Pointer übergeben (ebenfalls alsWert). Der Inhalt auf den der Pointer zeigt kann geändertwerden.

void foo(int *a){

*a = 23;}

int main(){

int b = 42;foo(&b);/* b hat jetzt den Wert 23 */return 0;

}

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

VariablenparameterI Arrays werden immer als Variablenparameter übergeben.

Dirty Trick: Array in Struct packen um es alsWerteparameter zu übergeben

void foo(int *a){

*a = 23;}

int main(){

int a[] = {1, 2, 3, 4, 5, 6};

foo(a);printf("%d\n", a[0]); /* 23 */foo(&a[3]);printf("%d\n", a[3]); /* 23 */

return 0;}

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Pointer und const

I const wird verwendet um etwas als nicht veränderbar(konstant/read-only) zu deklarieren

I Sinnvoll bei Funktionsparametern (z.B. strcpy das den srcnicht verändern soll)

char c;char *const cp = &c;/*der Inhalt auf den cp zeigt kann veraendert werdender Pointer kann nicht mehr umgebogen werden */const char *cp;/* der Pointer kann auf eine andere Adresse

gesetzt werden. Der Inhalt auf den derPointer zeigt kann nicht geaendert werden */

I Beide Varianten können gemischt werden (read-only auffixen Speicherbereich). const char * const ptr;

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Rückgabewerte

I Es können sowohl Werte als auch Pointer zurückgegebenwerden

int mydouble(int a){

return 2*a;}

int main(){

int a;

a = mydouble(5);printf("a ist %d\n", a) /* 10 */

return 0;}

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Rückgabewerte

char *first_b(char *a) {int i;

for (i = 0; i < strlen(a); ++i) {if (a[i] == ’b’)

return &a[i];}

return NULL;}

int main() {char *string1 = "foobar";char string2[] = "foofoo";char *p;

p = first_b(string1);if (p != NULL)

printf("rest ab b: %s\n", p); /* bar */

return 0;}

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

inline

inline Rückgabetyp Funktionsname(Parameterliste){

Anweisungen}

I Soll laut Standard (ab C-99) so schnell wie möglichausgeführt werden (Hint für den Compiler)

I Implementierung nicht vorgeschrieben. Oft wird derentsprechende Code der Funktion statt dem eigentlichenAufruf in den Code kopiert

I Kann aber auch ignoriert werden

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

FunktionspointerI Man kann nicht nur auf Variablen Pointer zuweisen, sonder

auch auf Funktionen

int (*f) (int, int);

int sub(int a, int b) {return a - b;

}int add(int a, int b) {

return a + b;}

int main() {int ret;

f = sub; /* besser f = &sub */ret = f(42, 23); /* oder (*f)(42, 23); */printf("%d\n", ret); /* 19 */

f = &add;ret = f(42, 23);printf("%d\n", ret); /* 65 */

return 0; }

CFortgeschritten

Kammerer

FunktionenWerte alsParameter

Pointer alsParameter

Pointer undconst

Rückgabewerte

inline

Pointer aufFunktionen

Funktionspointer

#include <stdio.h>

char* (*f) (char *);

char *plusone(char *a){

*a += 1; /* might be dangerous */return &a[0];

}

int main (int argc, char **argv){

char str[] = "hallo";char *p;f = plusone;p = f(str);printf("%s\n", p); /* iallo */return 0;

}

CFortgeschritten

Kammerer

ModulareProgram-mierung*.h Dateien

*.c Dateien

Kompilierung

Teil IV

Modulare Programmierung

CFortgeschritten

Kammerer

ModulareProgram-mierung*.h Dateien

*.c Dateien

KompilierungI Dient der Lesbarkeit, Wiederverwendbarkeit und

Wartbarkeit

I Modul wird in Header (*.h) und Implementierung (*.c)aufgeteilt

CFortgeschritten

Kammerer

ModulareProgram-mierung*.h Dateien

*.c Dateien

Kompilierung

Header Dateien

I Enthalten Prototypen und Konstanten

I Enthalten keine Definitionen von Funktionen(Implementierung geschieht in c-Dateien)

I Werden mit #include <mylib.h> bzw.#include "mylib.h" eingebunden. Ersteres sucht imLibrary-Pfad, zweiteres im lokalen Verzeichnis

/* mylib.h */#ifndef MYLIB_H /* include guard */#define MYLIB_H

#define MYCONST (23)extern int myvar;

int add(int, int);int sub(int, int);#endif /* MYLIB_H */

CFortgeschritten

Kammerer

ModulareProgram-mierung*.h Dateien

*.c Dateien

Kompilierung

Implementierung

I Normale C-Dateien in denen die Funktionenimplementiert werden

I Funktionen die mit static definiert werden, sind nurinnerhalb ihrer C-Datei sichtbar

/* mylib.c */#include "mylib.h"

int add(int a, int b) {return a + b;

}

int sub(int a, int b) {return a - b;

}

CFortgeschritten

Kammerer

ModulareProgram-mierung*.h Dateien

*.c Dateien

Kompilierung

Modul verwenden

I Module/Libraries werden mit #include eingebunden

/* prog.c */#include "mylib.h"

int myvar = 0;

int main(){

int f;

f = add(23, 42);

return 0;}

CFortgeschritten

Kammerer

ModulareProgram-mierung*.h Dateien

*.c Dateien

Kompilierung

Kompilierung

I Projekte die aus mehreren Modulen bestehen werden wiefolgt übersetzt:

$ gcc -c mylib.c # -> mylib.o$ gcc -c prog.c #datei mit main funktion$ gcc -o prog prog.o mylib.o

CFortgeschritten

Kammerer

Material

Teil V

Material

CFortgeschritten

Kammerer

Material

Material

I C Programming Language - Kernighan & Ritchie

I http://de.wikibooks.org/wiki/C-Programmierung

I AusblickI Traps & Pitfalls

I Lexikalische FallstrickeI Syntaktische FallstrickeI Semantische FallstrickeI Dynamische SpeicherallozierungI Strings und PointerI Makros