Tutoriumsinhalt

Übungsbeispiel

  • sizeof
  • Strukturen
  • File-IO
  • Dynamischer Speicher
  • Testen
  • Demo und Design

Gruppensuche




Der sizeof Operator

sizeof ist keine Funktion!!!
Funktioniert nur für Größen, die zur Kompilierzeit bekannt sind

Funktioniert

  • int charsize = sizeof(char);
  • int ptrsize = sizeof(int*);
  • int size = sizeof("Hallo");
  • int x[20];
    int len =
     sizeof(x) / sizeof(x[0]);
    

Funktioniert nicht

  • int x[20];
    int* y = x;
    int len =
     sizeof(y) / sizeof(y[0]);
    
  • char* hallo = "Hallo";
    int size = sizeof(hallo);
    

Strukturen

Zusammenfassung mehrer Variablen zu neuem Datentyp.
Zugriff mit ».« bzw. »->« bei Pointern.

#include <stdio.h>
struct _Person_ {
 char* name;
 int age;
};

int main()
{
 struct _Person_ petra;
 petra.name = "Petra";
 petra.age = 17;
 return 0;
}

Strukturen

Kürzere Schreibweise mit typedef:
[...]

struct _Person_ {
 char* name;
 int age;
};
typedef struct _Person_ Person;

[...]

Person petra;
petra.name = "Petra";
petra.age = 17;

[...]

Selbstreferenzierende Strukturen

Struktur kann Pointer auf Objekte vom eigenem Typ enthalten:
struct _Person_ {
 char* name;
 int age;
 struct _Person_* father;
 struct _Person_* mother;
};
Funktioniert, da Pointer immer gleich groß sind (4 bzw. 8 Byte).
Für viele Datenstrukturen benötigt (z.B. Baum, Linked List).

File I/O

  • Textdateien und Binärdateien
  • Textdateien:
    • enthalten nur Text
    • können mit dem normalen Editor gelesen werden
    • z.B. .txt Dateien
  • Binärdateien:
    • enthalten beliebige binäre Daten
    • sind nicht mit dem normalen Editor lesbar
    • z.B. Bilder, Musik

struct in Datei speichern

structs können in Binärdateien gespeichert werden
[...]
struct _Phone_ {
  int country_code;
  int number;
}__attribute__((packed));

[...]
struct _Phone_ number;
number.number = 1234567;
number.country_code = 43;

FILE* file = fopen("phone_numbers", "wb"); // wb = write binary
fwrite(&number, sizeof(struct _Phone_), 1, file);
fclose(file);

__attribute__((packed)) ?

Wichtig wenn struct in Datei gespeichert werden soll, entfernt unnötige Padding-Bytes.
struct _Code_ {
0:char symbol;
1:uint8_t bits;
2:0 (Padding)
3:0 (Padding)
4:char* code;
}
struct _Code_ {
0:char symbol;
1:uint8_t bits;
2:char* code;
}__attribute__((packed))

struct aus Datei lesen

structs können aus Binärdateien geladen werden:
[...]
struct _Phone_ {
  int country_code;
  int number;
}__attribute__((packed));

[...]
struct _Phone_ number;

FILE* file = fopen("phone_numbers", "rb"); // rb = read binary
fread(&number, sizeof(struct _Phone_), 1, file);
fclose(file);

Wie schaut ein int in einer Datei aus?

  • Integer hat 4 Byte
  • Jedes der Bytes kann wie eine »Ziffer« gesehen werden
  • Die Umrechung zurück in einen Integer erfolgt dann wie in jedem anderen Zahlensystem
  • Achtung Endianness: Intel ist Little-Endian, d.h. die Byte-Reihenfolge ist »verkehrt«
  • Beispiel: Integer 0x12345678 (= 305.419.896) in einer Datei: 78 56 34 12

Dateigröße ermitteln

Keine C Funktion um die Dateigröße zu ermitteln!

Folgende Vorgehensweise:
1. Dateizeiger an das Ende der Datei setzen
2. Abfragen, wo der Dateizeiger steht
3. Dateizeiger wieder auf den Anfang der Datei setzen
long size;
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);

Verwendung von Pointern: Dynamischer Speicher

  • Wird zur Laufzeit angefordert
  • Hat keinen »Namen«, nur eine Adresse → Zugriff über Pointer
  • Angeforderter Speicher muss auch wieder freigegeben werden
  • void* malloc(size_t size) fordert Speicher der Größe size an (in Bytes angegeben)
  • void free(void* ptr) gibt angeforderten Speicher wieder frei
  • void* realloc(void* ptr, size_t new_size) vergrößert einen angeforderten Speicherbereich

Beispiel

1int main() { 2 int* mem = (int*)malloc(sizeof(int) * 2); 3 if(mem == NULL) return 0; 4 *mem = 1; 5 *(mem + 1) = 2; 6 mem[0] = 3; 7 mem[1] = 4; 8 free(mem); return 0; }
Heap:
0xA000
0xA004
0xA008
Stack:
0xF000
0xF004
0xF008
Heap:
0xA000
0xA004
0xA008
Stack:
0xF000
0xF004
mem   0xF0080xA000
Heap:
0xA000
0xA004
0xA008
Stack:
0xF000
0xF004
mem   0xF0080xA000
Heap:
0xA0001
0xA004
0xA008
Stack:
0xF000
0xF004
mem   0xF0080xA000
Heap:
0xA0001
0xA0042
0xA008
Stack:
0xF000
0xF004
mem   0xF0080xA000
Heap:
0xA0003
0xA0042
0xA008
Stack:
0xF000
0xF004
mem   0xF0080xA000
Heap:
0xA0003
0xA0044
0xA008
Stack:
0xF000
0xF004
mem   0xF0080xA000
Heap:
0xA0003
0xA0044
0xA008
Stack:
0xF000
0xF004
mem   0xF0080xA000

Häufige Fehler / Best Practice

  • Pointer auf NULL initialisieren
  • Pointer nach free auf NULL setzen
  • malloc/realloc auf NULL überprüfen
  • Bei realloc Hilfspointer verwenden:
    char* text = (char*)malloc(8 * sizeof(char));
    char* new_text = (char*)realloc(text, 16 * sizeof(char));
    if(new_text != NULL)
    {
      text = new_text;
    }
    else
    {
      free(text);
      printf("Out of memory!\n");
    }
    

Valgrind

  • Tool zum Finden von
    • Memory Leaks
    • unitialiserten Variablen
    • Off-by-one Fehlern
    • Segmentation Faults
  • Testsystem verwendet Valgrind → Valgrind Fehler geben Punkteabzug!
  • Nur für Linux, aber auf Pluto schon installiert

Livedemo

Tipps

  • Gut und viel testen!
  • Codingstandard beachten!
  • Rückgabewerte der File I/O Funktionen prüfen!
  • Bei malloc und realloc auf NULL prüfen!
  • Valgrind verwenden!
  • Programm sinnvoll in ("kurze") Funktionen einteilen!
  • Komplizierte Stellen kommentieren!
  • Aussagekräftige Variablennamen verwenden!
  • Variablen initialisieren!

Viel Erfolg bei den Hausübungen!

Abgabeschluss:

  • Do, 07.12.2017 14:00