C - Der Systemnahe Standard
-----------------< Systemnähe? C! > ----------------- \ \ .~-~. /^ ^\ (__ O O __) \ \< >/ /C ist seit Jahrzehnten die Sprache für systemnahe Programmierung. Sie bietet direkten Zugriff auf Hardware und Speicher hat dabei minimalen Overhead.
Viele Betriebssysteme, Kernel und grundlegende Systemkomponenten sind in C geschrieben, was zu einer großen Verbreitung und einem reichen Ökosystem von Bibliotheken und Tools geführt hat.
- C und C++ Programme haben oft Fehler in der Speicherverwaltung
- C und C++ Programme sind nicht typsicher (schwache Typisierung)
- “Null References: The Billion Dollar Mistake” (Tony Hoare)
- Gleichzeitigkeit ist schwer zu programmieren
Undefined Behaviour
Abschnitt betitelt „Undefined Behaviour“Beispiele für Undefined Behaviour
Abschnitt betitelt „Beispiele für Undefined Behaviour“Compiler Unterschiede
int main() { int i = 2; int result = --i + i++; return 0;}- gcc https://godbolt.org/z/oYE6zvP9h (result=3)
- clang https://godbolt.org/z/bxx7YPPvb (result=2)
Buffer Overflow
Ein Buffer Overflow tritt auf, wenn mehr Daten in einen fest dimensionierten Speicherbereich (Buffer) geschrieben werden, als dieser fassen kann. Dies führt dazu, dass benachbarte Speicherbereiche überschrieben werden, was zu unvorhersehbarem Verhalten oder Sicherheitslücken führen kann.
#include <stdio.h>#include <string.h>int main() { char name[5]; printf("Enter your name: "); gets(name); printf("Hello, %s!\n", name); return 0;}Use after free
- 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;}Double Free
- 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;}Invalid Index
- 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;}Divide by Zero
- 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;}Dangling Pointer
- 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;}C: Effizienz zum Preis der Sicherheit
Abschnitt betitelt „C: Effizienz zum Preis der Sicherheit“Aus den vorhergegangenen Beispielen wird ersichtlich, dass gültige C Programme zu einigen Probelmen führen können.
Warum moderne Alternativen entstehen
Abschnitt betitelt „Warum moderne Alternativen entstehen“Rust
- Memory Safety