Programmiersprachen Einführung in C 14 Vertiefung einiger Themen Prof. Dr. Gliederung Programmiersprachen 1. Von der Maschinensprache zu C 2. Die Struktur von C-Programmen 3. Variable und Datentypen in C 4. Bildschirm Ein-/Ausgabe 5. Kontrollstrukturen 6. Funktionen 7. Programmierstil, Programmierrichtlinien 8. Felder u. Zeichenketten 9. 10. Arbeiten mit Dateien 11. Strukturen, Aufzählungstypen 12. Zeiger 13. Speicherklassen 14. Vertiefung einiger Themen 15. Sicherheitsprobleme mit C 2 1
14 Vertiefung einzelner Themen dynamische Objekte/Speicheranforderung Bit-Manipulation Präprozessor Statische vs. dynamische Objekte Statische Objektdefinitionen char ch; unsigned char xx = 130; char *text = "Dies ist ein Text"; short sbuf[] = 1, 5, 10, 15; int j = 1; double *dp = &d; unsigned ui = 2000000; long list[10] = 1, -2, -5, 10, 15 ; int feld[10][20];... sind über Namen aufrufbar... kommen im Programmablauf nur einmal vor... Jede Instanz bzw. die Anzahl der Instanzen (bei Feldern) muß zur Compile-Zeit bekannt sein 4 2
malloc malloc - C-Form der Speicheranforderung void *malloc(size_t size); char *buf = malloc(1000); // 1000 Bytes werden angefordert typedef struct adresse char strasse[30]; int hausnr; tadresse; tadresse *myadr = malloc(sizeof(tadresse)); // besser: tadresse *myadr; myadr = (tadresse *) malloc(sizeof(tadresse)); // Es wird Speicher für ein Adreßobjekt angefordert Freigabe mit void free( void *memblock ); free(myadr); Benötigte Header-Dateien: #include <stdlib.h> #include <malloc.h> 5 Verwaltung dynamischer Objekte durch eine lineare Liste #define new(t) (t *) malloc(sizeof(t)) typedef struct Adr // Definition der Adreßstruktur char Name[30], Tel[20]; struct Adr *next; tadr; tadr *Anf; // Aufbauen einer linearen Liste: tadr *Laufz, *aktuell; Anf = new(tadr); // Erzeugen eines ersten Objekts strcpy(anf->name, "Schwenk" ); aktuell = new(tadr);// Erzeugen des zweiten Objekts strcpy(aktuell->name, "Meier"); aktuell->next = NULL; Anf->next = aktuell;// Verbinden von 1. und 2. Element aktuell = new(tadr);// Erzeugen des 3. usw. Objekts aktuell->next = NULL; Laufz = Anf; // Suche nach dem letzten Element while (Laufz->next)// Solange es weitere Elemente gibt: Laufz = Laufz->next; // Geh' zum nächsten Element // Sonst: Letztes Element gefunden: Laufz->next = aktuell;// dieses mit dem aktuellen // Element verbinden 47 44 1 7489 7489 7489123245678 6 3
Löschen eines Objekts der linearen Liste Aufgabe: Löschen eines Elements (Name ist angegeben) aus der linearen Liste. Der Parameter 'Anf' zeigt auf den Anfang der Liste tadr *lösche(char *ElemName, tadr *Anf) tadr *Laufz = Anf; tadr *tmp; if (strcmp(anf->name, ElemName) == 0) Anf = Anf->next; // Ist das erste Element das gesuchte? free(laufz); return Anf; while (Laufz->next && // Nach benanntem Element suchen! strcmp(laufz->next->name, ElemName)!= 0) Laufz = Laufz->next;// Wichtig! Laufz muß eins vor dem 7 47 888 7489 7489 if (Laufz->next) // gesuchten Element stehen bleiben! tmp = Laufz->next; Laufz->next = Laufz->next->next; // Element ausklinken free(tmp); return Anf; 1 748974 7 Bit-Operationen Boolesche Operationen auf der internen Bit-Repräsentation von Operanden Operationen auf den Bits eines Operanden Operationen zwischen Bits zweier Operanden Ausdruck Wert / Wirkung 1 x << y schiebe Inhalt von x um y Stellen links 1 x >> y schiebe Inhalt von x um y Stellen rechts 1 x & y bit-weise UND-Verknüpfung der Bits aus x und y 1 x y bit-weise ODER-Verknüpfung der Bits aus x und y 1 x ^ y bit-weises EXKLUSIV-ODER zwischen x u. y 1 x bit-weise Komplementbildung, Einerkompl. von x Vergleichs-Operatoren ==!= vor Bit-Operatoren Klammerung 8 4
Anwendung von Bit-Operationen Kodierung von mehreren Zuständen (Flags), die unabhängig vonein-ander auftreten können, in einer Variablen (Beispiel: Maustasten) const int LMButtonBitMap = 1; // Wichtig: Kodierung durch 2er Potenzen const int RMButtonBitMap = 2; const int... = 4; int MButton = 0; void OnLButtonDown() // Routine wird bei Drücken der linken Maustaste // aufgerufen, rechte Taste entsprechend... MButton = LMButtonBitMap; void OnLButtonUp() // Routine wird bei Loslassen der linken Maustaste // aufgerufen MButton &= ~LMButtonBitMap; int LButtonDown() // Test, ob die linke Maustaste gedrückt wurde return MButton & LMButtonBitMap; 9 Bit- Beispiel: Client-/Server Schnittstelle 1 #define POSITION (1<<0) 1 #define ENDPOSITION (1<<1) 1 #define ROTATE_FAIL (1<<2) 1 #define MOVE_FAIL (1<<3) 1 #define ALIGN_FAIL (1<<4) 1 #define EMERGENCY_STOP (1<<5) 1 #define OBSTACLE_DISTANCE (1<<6) 1 #define OBSTACLE_POS (1<<7) 1 #define SONAR_READINGS (1<<8) 1 #define JOY_STATUS (1<<9) 1 #define EOC (1<<10) 1 #define Z_POSITION (1<<11) 1 #define Z_END_POSITION (1<<12) 1 #define Z_FORCE (1<<13) 1 #define Z_EOC (1<<14) 1 #define Z_COLLISION (1<<15) 1 CSetEvents(GlPlanClient, RobServer, POSITION ENDPOSITION SONAR_READINGS EOC); 10 5
Beispiel: Auswahl beim Verteilen von Informationen an mehrere Empfänger /* CSendEvent dient dazu, von einem Server aus an alle interessierten Clients Nachrichten (Events) zu versenden. Hierzu wird abgefragt, ob in der jeweiligen EventRequests-Bitmaske das für diesen Event relevante Bit gesetzt ist */ void CSendEvent(EventType *Event) int Client; for (Client=FIRST_CLIENT; Client<LAST_CLIENT; Client++) if (EventRequests[Client] & Event->Type) // Event über Sockets versenden 11 Präprozessor: #include und #define Prä - vor ; Prozessor - Verarbeitungsphase 1 Bearbeitung des Quelltextes vor der eigentlichen Übersetzung und Codeerzeugung 1 Präprozessoranweisungen beginnen mit '#' und gelten ab dieser Zeile #include <datei.h>einbinden von datei.h aus dem include - Verzeichnis #include datei.h Einbinden von datei.h aus dem aktuellen Verzeichnis #define konstnam wert 1 Konstantendefinition: #define MAXWERT 100 1 Achtung! Kein abschließendes Semikolon! 'Wert' von MAXWERT ist der Rest der aktuellen Zeile; ein Semikolon würde daher mit zum Wert dazugehören 1 Achtung! Nur textuelle Definition der Konstante; Hier erfolgt weder Syntax- noch Typprüfung. Hier gemachte Fehler werden erst später an den verwendeten Stellen erkannt Verwendung: if (ergebnis < MAXWERT) wird ersetzt zu... if (ergebnis < 100) (nur textuelle Ersetzung) Durch Typisierung u. Syntaxprüfung in unbedingt const bevorzugen! 12 6
Parametrisierte #define-makros #define-makros können auch parametrisiert werden 1 #define max(a,b) (((a)<(b))? (b) : (a)) // a und b sind textuell verwendete Parameter Starke Klammerung sehr empfehlenswert, z.t. unbedingt notwendig! 1 #define sq(a) a*a // sq soll das Quadrat einer übergebenen Zahl bestimmen 1 #define incr(x) x+1 // incr soll die um 1 erhöhte Zahl liefern 1 : 1 x = sq(d); // noch problemlos 1 x = sq(d+1); 1 // textuelle Ersetzung: x = d+1 * d+1; 1 x = incr(d); // wieder problemlos 1 x = incr(d) * 2; 1 // textuelle Ersetzung: x = d+1 * 2; Richtig wäre: 1 #define sq(a) ((a)*(a)) 1 #define incr(a) ((a) + 1) Makros werden in C gern eingesetzt, um damit kleine Funktionen wie max zu erzeugen, ohne jedoch den Overhead zu haben, der mit jedem Funktionsaufruf einhergeht; in C++ besser: inline-funktionen 13 Bedingte Compilierung: #if, #elif, #else, #endif Anwendung: Bedingtes Setzen von Konstanten #define TEST 12 #if TEST == 10 #define MAXWERT 99 #elif TEST == 12 #define MAXWERT 101 #else #define MAXWERT 50 #endif Anwendung: Bedingtes Compilieren #if TEST == 10 printf("wert von 'ergebnis': %e\n", ergebnis); #else printf("wert von 'ergebnis': %e\n", ergebnis); printf("wert von 'zaehler': %d\n", zaehler); #endif Auswirkung 1 if (ergebnis < MAXWERT) --> (textuelle Ersetzung!) --> if (ergebnis < 101) 14 7
Bedingte Compilierung: Definition überprüfen mit #ifdef, #ifndef Anwendungsfall: Bedingte Compilierung mit nur zwei Alternativen #define DEMO... : void main() #ifdef DEMO #define VERSION 0.9 printf("progr. superduper, #else Vers.%d\n", #define VERSION 1.0 VERSION); #endif Anwendung: Einfügen von Codestücken für 'besondere' Fälle #if DEBUG printf("wert von 'ergebnis': %e\n", ergebnis); #endif // Print-Anweisung wird (nur) im 'DEBUG'-Modus eingefügt! #pragma - Compilerdirektiven, -Einstellungen 1 nicht festgelegt, compilerabhängig >>> Compiler - Handbücher #error Fehlertext 1 Ausgabe des Fehlertexts (während des Präprozessorlaufs!) und Abbruch des Compiliervorgangs (z.b. als Hinweis auf nicht mehr unterstützte Programmversionen) 15 8