Zum Inhalt springen

1_C

-----------------
< Systemnaehe? C! >
-----------------
\
\ .~-~.
/^ ^\
(__ O O __)
\ \< >/ /

The good

  • Standard seit 70er
  • Portabel
  • Low-Level
  • Manual Memory Management
  • Legacy Code

the bad

  • Undefined Behaviour
  • Memory Safety
  • Parallelverarbeitung
  • Error handling
  • Linking

and the ugly

  • Paketverwaltung
  • Build System

In C ist Undefined Behaviour (UB) nicht immer offensichtlich. Rust loest dies mit dem unsafe keyword, anhand dessen ein Entwickler direkt kritische Stellen im Code erkennen kann.

int main() { // 1
int i = 2; // 2 Initialize variable
int result = --i + i++; // 3
return 0; // 4
}

gcc: result=3 clang: result=2


  • Buffer mit bestimmter Groesse erstellen
  • Daten in Buffer schreiben
  • Daten sind groesser als Buffer
  • Naheligender Speicher wird ueberschrieben -> Overflow
#include <stdio.h>
#include <string.h>
int main() {
char buffer[10];
char *src = "This is a long string.";
memcpy(buffer, src, strlen(src));
printf("%s\n", buffer);
return 0;
}

  • Allokieren von Speicher
  • Schreiben in den Speicher
  • Freigeben des Speichers
  • Lesen aus dem Speicher -> Fehlerhaft
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int)); // TODO check allocation success
*ptr = 42;
printf("ptr: %d\n", *ptr);
free(ptr);
// Pointer is dangling
printf("ptr after free: %d\n", *ptr);
return 0;
}

  • Speicher wird allokiert
  • Pointer wird an free uebergeben
  • Speicher is freigegeben, Pointer existiert weiter
  • Pointer wird erneut an free uebergeben -> Welcher Speicher wird bereinigt?
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
free(ptr);
free(ptr);
return 0;
}

  • Index ist groesser als array
  • Value enthaelt falsche Daten
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int value = arr[6];
printf("Value: %d\n", value);
return 0;
}

  • Undefinded
  • Manche Programme brechen ab, manche arbeiten mit falschem Ergebnis weiter
#include <stdio.h>
int main() {
int numerator = 10;
int denominator = 0;
int result = numerator / denominator;
printf("Result: %d\n", result);
return 0;
}

  • Value in einem lokalen Scope
  • Dereferenzieren des Values im lokalen Scope
  • Referenz (Pointer) wird returned
  • Value existiert nicht mehr, Pointer schon
  • Pointer zeig auf fehlerhaften Speicher
int *getDanglingPointer() {
int value = 42;
return &value;
}
int main() {
int *ptr = getDanglingPointer();
*ptr = 10; // Unsicher welcher Speicher beschrieben wird
printf("%d", *ptr);
return 0;
}