Tutoriumsinhalt

  • Plagiatscheck!
  • sizeof
  • Strukturen
  • File-IO
  • Command-line Arguments
  • Dynamischer Speicher
  • Übungsbeispiel

Plagiatscheck

Allgemein:

  • Aufgaben sind alleine zu lösen (Ausnahme: Gruppenbeispiel)
  • Abgaben werden automatisch geprüft
  • Abgaben werden automatisch auf Plagiate geprüft

Bei Verstoß:

  • Alle Verdächtigen werden von der LV disqualifiziert

Der sizeof Operator

  • Operator (keine Funktion), 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

Geht das auch einfacher?

  • Natürlich, wir lassen den Compiler für uns arbeiten
  • Direktes Einlesen in einen Integer mit fread:
    int length;
    fread(&length, sizeof(int), 1, file);
  • Über Pointer, indem wir den Speicherinhalt neu »interpretieren« lassen:
    // buffer is the char array containing the file
    int length = *((int*)buffer);
  • Mit memcpy in einen Integer kopieren:
    // buffer is the char array containing the file
    int length;
    memcpy(&length, buffer, sizeof(int));

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
  • Der Code dazu:
    long size;
    fseek(file, 0, SEEK_END);
    size = ftell(file);
    fseek(file, 0, SEEK_SET);
    

Command-line Arguments

int main (int argc, char *argv[])
{
  int count;
  printf ("This program was called with \"%s\".\n",argv[0]);

  if (argc > 1)
  {
    for (count = 1; count < argc; count++)
    {
      printf("argv[%d] = %s\n", count, argv[count]);
    }
  }
  else
  {
    printf("The command had no other arguments.\n");
  }
  return 0;
}
  

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

Ass3

Tipps

  • Gut und viel testen!
  • Codingstandard beachten!
  • 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 spaß mit QR-Codes!

Abgabeschluss:

  • SO, 23.12.2018 23:59:59