Warum ich neuen Code in Zig statt in C schreibe
Why Zig over C for Systems Programming
Zig und C wollen dasselbe: kleine Sprachen ohne Garbage Collector, nah an der Maschine. Der Autor zeigt an konkreten Beispielen, wo C ihn Zeit kostet – goto-Cleanup, Zeiger ohne Länge, ignorierte Fehlercodes, der Präprozessor, undefiniertes Verhalten bei Überläufen – und wie Zig das löst: errdefer, Slices mit Länge, Fehler im Rückgabetyp, comptime. Ehrlich benennt er auch die Kosten: Zig ist noch instabil, C bleibt unerreicht verbreitet.
Ich schreibe diesen Beitrag nicht, weil ich C für schlecht halte; C ist meine Lieblingsprogrammiersprache! Ich schreibe ihn, weil ich keinen neuen Code mehr in C schreiben möchte.
- uecker
Zig ist eine schöne Sprache, aber diese Artikel vergleichen neue Sprachen immer mit C im alten Stil ohne moderne Werkzeuge. Die meisten der aufgeführten Vorteile kann man auch in C haben, wenn man bereit ist, moderne Tooling und Stil zu übernehmen: Prüfung auf vorzeichenbehafteten Überlauf: Sanitizer, Fehler, die man nicht ignorieren kann: [[nodiscard]], Slice-Typen, die ihre Größe kennen: variabel modifizierte Typen oder Span-Typen.
Defer wird auch bald in C-Compilern erscheinen, aber persönlich finde ich die goto-Fehlerbehandlung immer noch klarer.
(Rust hat einen Vorteil für zeitliche Speichersicherheit, aber das gilt nicht für Zig.)
- variadix
Ich habe mich nach langer Zeit mit C für einen C++-Dialekt für die Systemprogrammierung entschieden. Er ist größtenteils C-ähnlich und ich deaktiviere Exceptions, RTTI usw., aber ich verwende Templates, Overloads, Methoden, Lambdas usw.
Ich habe mein eigenes Objektsystem mit Konstruktormethoden, die alle einen Statuscode zurückgeben, sodass man keine Exceptions braucht, meine eigene Standardbibliothek für Container, gepufferte E/A, Netzwerk, Hashing, Mathematik, Threading, Async usw.
C++ gibt dir alle Features, um effektiv deine eigene Sprache zu bauen, und du kannst erstklassiges Tooling nutzen, da C++ weit verbreitet ist. LLMs machen es auch viel einfacher, eine ganze Standardbibliothek selbst zu bauen und zu testen.
- asveikau
Nur aus dem ersten Beispiel:
struct user *user_new(const char *name, size_t n_groups)
{
struct user *u = malloc(sizeof *u);
if (u == NULL)
goto fail;
u->name = strdup(name);
if (u->name == NULL)
goto fail_user;
u->groups = calloc(n_groups, sizeof *u->groups);
if (u->groups == NULL)
goto fail_name;
return u;
fail_name:
free(u->name);
fail_user:
free(u);
fail:
return NULL;
}
Ich denke, man kann C besser darstellen als das. Ich konzentriere mich gerne auf einen einzigen Aufräumblock und nutze die Tatsache aus, dass free(NULL) ein No-Op ist.
struct user *user_new(const char *name, size_t n_groups)
{
struct user *u = calloc(sizeof *u, 1);
if (u == NULL)
goto fail;
u->name = strdup(name);
if (u->name == NULL)
goto fail;
u->groups = calloc(n_groups, sizeof *u->groups);
if (u->groups == NULL)
goto fail;
return u;
fail:
if (u)
{
free(u->name);
free(u->groups);
}
free(u);
return NULL;
}
Obwohl zu beachten ist, dass dies annimmt, dass calloc(sizeof *u, 1) Zeiger auf NULL initialisiert; IIRC ist die Annahme, dass das Bitmuster von NULL alles Null ist, technisch gegen den Standard, aber jedes System, das du wahrscheinlich anvisierst, wird trotzdem funktionieren.
Wenn du dem Muster folgst "es ist OK, NULL-Zeiger zu freeen", kannst du auch eine free-Funktion wiederverwenden: […]
- pron
Außerdem ist Zig mindestens so ausdrucksstark wie C++ (aber ohne die Implizitheit und allgemeine Sprachkomplexität, die das Review von manch sensiblem Code, wo man genau sehen muss, welche Operationen ausgeführt werden, schwer macht), und es unterstützt Arenas besser als C/C++/Rust.
- drnick1
All diese neuen Sprachen sind im Vergleich zu C unlesbar. Wenn man, wie ich, explizite Dinge mag wie for (size_t i = 0; i <= n; i++) total += a[i];, dann kommen all diese neuen Sprachen nicht in Frage.
- kombine
D und Nim haben übrigens seit Jahren dieselben Features.
- qweqwe14
Rust wird sowieso all diese Nischensprachen übernehmen. Ja, Rust ist langfristig ein brauchbarer Ersatz für C und C++. Zig versucht nur, ein Mittelweg zu sein, von dem wir festgestellt haben, dass er nicht funktioniert. Zig ist im LLM-Zeitalter besonders irrelevant im Vergleich zu Rust.
- program
> Fehler, die man nicht ignorieren kann
Es ist für mich in Ordnung, ein Makro mit `__attribute__((warn_unused_result))` für GCC und Clang zu deklarieren und `[[nodiscard]]`, wenn der C-Code später mit C++ (oder gar nicht) kompiliert wird.