C: Idiomatische Effizienz-Referenz
Inhaltsverzeichnis
- Speicherverwaltung
- Zeiger & Arrays
- Fehlerbehandlung
- Zeichenketten
- Strukturen & Aufzählungen
- Präprozessor & Header
- Anti-Patterns spezifisch für C
1. Speicherverwaltung {#speicher}
// ❌ malloc ohne Überprüfung des Rückgabewerts
char *buf = malloc(size);
strcpy(buf, src);
// ✅
char *buf = malloc(size);
if (!buf) return -ENOMEM;
memcpy(buf, src, size);
// ❌ Cast des malloc-Ergebnisses (in C unnötig, versteckt fehlendes #include)
int *p = (int *)malloc(n * sizeof(int));
// ✅
int *p = malloc(n * sizeof *p);
// ❌ free ohne Nullsetzen (Risiko von baumelnden Zeigern in langlebigen Bereichen)
free(ptr);
// ... späterer Code könnte ptr verwenden
// ✅
free(ptr);
ptr = NULL;
// ❌ Vergessenes Freigeben bei frühen Rückkehrpfaden
char *a = malloc(100);
char *b = malloc(200);
if (!b) return -1; // gibt a nicht frei
// ✅ — ein einzelnes Bereinigungslabel
char *a = NULL, *b = NULL;
a = malloc(100);
if (!a) goto cleanup;
b = malloc(200);
if (!b) goto cleanup;
// ... a, b verwenden ...
cleanup:
free(b);
free(a);
Verwenden Sie sizeof *ptr anstelle von sizeof(Typ) — es bleibt korrekt, wenn sich der Typ ändert.
2. Zeiger & Arrays {#zeiger}
// ❌ Manuelle Array-Größenverfolgung
void process(int *arr, int len) { ... }
process(data, 10);
// ✅ — Größe zusammen mit Zeiger übergeben oder ein Struct verwenden
typedef struct { int *data; size_t len; } IntSlice;
// ❌ Zeigerarithmetik, wo Array-Indizierung klarer ist
*(arr + i) = value;
// ✅
arr[i] = value;
// ❌ VLA im Produktionscode (Stapelüberlauf-Risiko, optional in C11+)
int arr[n];
// ✅
int *arr = malloc(n * sizeof *arr);
if (!arr) return -ENOMEM;
// ... arr verwenden ...
free(arr);
3. Fehlerbehandlung {#fehler}
// ❌ Verwenden magischer Zahlen für Fehlerrückgaben
if (do_thing() == -1) { ... }
// ✅ — benannte Fehlercodes definieren oder verwenden
#include <errno.h>
if (do_thing() < 0) {
perror("do_thing");
return errno;
}
// ❌ Tief verschachtelte Fehlerprüfungen
int r1 = step1();
if (r1 == 0) {
int r2 = step2();
if (r2 == 0) {
int r3 = step3();
// ...
}
}
// ✅ — frühe Rückkehr / goto cleanup
if (step1() < 0) goto fail;
if (step2() < 0) goto fail;
if (step3() < 0) goto fail;
return 0;
fail:
cleanup();
return -1;
goto cleanup ist idiomatisches C für Ressourcenbereinigung — vermeiden Sie es nicht grundsätzlich.
4. Zeichenketten {#zeichenketten}
// ❌ strcpy ohne Bereichsprüfung
strcpy(dest, src);
// ✅
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// oder besser: snprintf(dest, sizeof(dest), "%s", src);
// ❌ strcmp-Mißbrauch
if (str == "hello") { ... } // vergleicht Zeiger, nicht Inhalt
// ✅
if (strcmp(str, "hello") == 0) { ... }
// ❌ Zeichenketten mit wiederholtem strcat erstellen (O(n²))
char result[1024] = "";
for (int i = 0; i < n; i++) {
strcat(result, items[i]);
}
// ✅ — Schreibposition verfolgen
char result[1024];
int pos = 0;
for (int i = 0; i < n && pos < (int)sizeof(result); i++) {
pos += snprintf(result + pos, sizeof(result) - pos, "%s", items[i]);
}
Bevorzugen Sie snprintf gegenüber sprintf — immer.
5. Strukturen & Aufzählungen {#strukturen}
// ❌ Bloßes Struct, das überall das `struct`-Schlüsselwort erfordert
struct point { int x, y; };
struct point p = {1, 2};
// ✅
typedef struct { int x, y; } Point;
Point p = {1, 2};
// ❌ Uninitialisiertes Struct
Point p;
use(p.x); // UB
// ✅
Point p = {0};
// ❌ Magische Integer-Konstanten
if (state == 3) { ... }
// ✅
typedef enum { STATE_IDLE, STATE_RUNNING, STATE_DONE } State;
if (state == STATE_DONE) { ... }
6. Präprozessor & Header {#praeprozessor}
// ❌ Makro, wo eine Inline-Funktion funktioniert (keine Typsicherheit, doppelte Auswertung)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
MAX(x++, y) // x wird zweimal inkrementiert, wenn x > y
// ✅
static inline int max_int(int a, int b) { return a > b ? a : b; }
// ❌ Keine Include-Guard
// my_header.h
struct Foo { int x; };
// ✅
#ifndef MY_HEADER_H
#define MY_HEADER_H
struct Foo { int x; };
#endif
// oder: #pragma once (weit verbreitet, nicht standardisiert)
Halten Sie Makros für bedingte Kompilierung und Konstanten. Verwenden Sie static inline für Logik.
7. Anti-Patterns spezifisch für C {#antipatterns}
| Anti-Pattern | Bevorzugt |
|---|---|
sprintf | snprintf mit Puffergröße |
gets | fgets (gets wurde in C11 entfernt) |
Cast des malloc-Ergebnisses | implizite void*-Konvertierung nutzen |
sizeof(Typ) in malloc | sizeof *ptr |
| VLA für große/laufzeitbestimmte Arrays | Heap-Allokation |
void*-Callbacks ohne Kontextparameter | void *ctx zusammen mit Funktionszeiger übergeben |
| Globaler veränderlicher Zustand | Zustand über Struct-Zeiger übergeben |
assert für Laufzeitfehlerbehandlung | korrekte Fehlerrückgabecodes |
Fehlendes const bei schreibgeschützten Zeigerparametern | const char *str |
| Mischen von vorzeichenbehafteten/vorzeichenlosen Vergleichen | konsistente Typen verwenden, explizit casten |
Einschränkungen
- Dies sind sprachspezifische Richtlinien und decken keine übergreifenden Architekturentscheidungen ab.
- Übermäßige Verdichtung könnte die Lesbarkeit verringern; wenden Sie Ihr Urteilsvermögen an.