3 Punkte von GN⁺ 2024-10-16 | 1 Kommentare | Auf WhatsApp teilen
  • Eine an den neuen C-Standard angepasste überarbeitete Ausgabe von Modern C steht als kostenloser Download bereit, sodass Lern- und Referenzmaterial zur Programmiersprache C nun auf Basis von C23 neu betrachtet werden kann
  • Im Mittelpunkt der Überarbeitung steht die Berücksichtigung von C23; der Fokus liegt darauf, den Übergang zum neuen Standard im Einklang mit dem ISO-Veröffentlichungsprozess und dessen Zeitplan zu erleichtern
  • Neue Releases der wichtigsten Compiler haben bereits den Großteil der C23-Funktionen implementiert, sodass die Änderungen im Buch über eine experimentelle Vorstellung hinausgehen und an reale Einsatzumgebungen anschließen
  • Sprach- und Bibliotheksänderungen wie _BitInt(N), nullptr, auto, typeof und constexpr werden breit berücksichtigt und wirken sich auch auf die bisherige Art aus, C-Code zu schreiben
  • Damit man es auf bestehenden Plattformen direkt ausprobieren kann, wurden ein Anhang für die Migration und temporäre Include-Header ergänzt; Mannings MEAP ist jedoch noch in Arbeit

Kostenlos verfügbare Materialien und Standarddokumente

  • Die Modern C C23 Edition kann kostenlos heruntergeladen werden
  • Auf der dedizierten Buchseite finden sich auch zugehörige Materialien
  • Die überarbeitete Ausgabe hat viele Erklärungen angepasst, ihr Hauptzweck ist jedoch, den neuen C-Standard C23 abzubilden
  • Unter den öffentlich zugänglichen Dokumenten kommt N3220 PDF den Inhalten des neuen Standards am nächsten
  • Neue Releases der wichtigsten Compiler implementieren bereits den Großteil der neuen Funktionen, die C23 mitbringt

Sprachänderungen in C23 und Unterstützung beim Umstieg

  • Änderungen rund um Integer machen einen großen Teil aus
    • Neuer bitgenauer Typ _BitInt(N) hinzugefügt
    • Neue C-Bibliotheks-Header für arithmetische Operationen mit Overflow-Prüfung und Bitmanipulation hinzugefügt
    • Berücksichtigung der Möglichkeit von 128-Bit-Typen auf modernen Architekturen
    • Einschließlich wesentlicher Verbesserungen bei enum-Typen
  • Auch weitere neue Konzepte aus C23 sind enthalten
    • Die Konstante nullptr und ihr zugrunde liegender Typ
    • Syntax-Annotationen über attributes
    • Werkzeuge für typgenerische Programmierung, darunter auto und typeof
    • {}-Default-Initialisierung, die auch für Arrays variabler Länge gilt
    • constexpr für benannte Konstanten aller Typen
  • Das neue Material behandelt außerdem zusammengesetzte Ausdrücke, Lambdas, „Internationalisierung“ und einen umfassenden Ansatz für Programmfehler
  • Damit man mit C23 auf bestehenden Plattformen direkt starten kann, wurden ein Anhang und temporäre Include-Header ergänzt
  • Mannings MEAP zur neuen Edition ist noch offen

1 Kommentare

 
GN⁺ 2024-10-16
Meinungen auf Hacker News
  • Anders als die Speicherreihenfolge auf meinem Rechner, Little Endian, heißt die Variante, bei der die höherwertige Darstellung zuerst kommt, Big Endian. Zu sagen, beides sei auf modernen Prozessoren verbreitet, wirkt etwas übertrieben, weil außer s390x kaum noch etwas übrig ist.
    Jetzt kommen bestimmt gleich die Kommentare zu den allseits beliebten Nischen-/aussterbenden Big-Endian-Architekturen.

    • Arm ist bi-endian und lebt in den meisten Smartphones weiter.
      Wie in einem anderen Top-Kommentar gesagt: „modern“ heißt nicht zwingend beliebt oder weit verbreitet.
    • POWER ist bi-endian. Aktuelles Linux on POWER ist Little Endian; früher war Big-Endian-Linux auf POWER üblich, aber die Distributionen haben vor einigen Jahren umgestellt. AIX und IBM i dagegen sind Big Endian.
      AIX und IBM i sind vielleicht nicht so aktiv wie IBM-Mainframes, aber AIX kann man als lebendiger ansehen als Solaris oder HP/UX, erst recht im Vergleich zu den vielen kommerziellen Unix-Systemen von früher. Auch IBM i hält sich gerade so, ist aber deutlich lebendiger als konkurrierende Legacy-Midrange-Plattformen wie HP MPE, deren Vendor-Support offiziell beendet wurde.
    • MIPS ist in Consumer-Netzwerkhardware noch ziemlich lebendig.
    • In gewisser Weise könnte man sagen, dass alle Prozessoren es häufig verwenden, weil die Network Byte Order Big Endian ist.
    • Ist Sparc nicht Big Endian?
  • Der wichtigste Aspekt von C ist Portabilität. Der Kern ist, von kleinen Mikrocontrollern bis zu nahezu jeder Computing-Plattform zu reichen; ich frage mich, ob eine neue C-Version in diesem Maß übernommen wird.
    Wenn ich Cutting Edge nutzen wollte, würde ich eher C++2x oder Rust statt C wählen. Übersehe ich etwas? Mich interessiert, welchen Vorteil dieses sogenannte moderne C bringt.

    • Einer der Vorteile beim Schreiben von C-Code ist, dass man sich die lästigen Debatten darüber spart, wie idiomatischer Code aussehen sollte und welche Sprach-Teilmenge die richtige ist.
      Für Cutting Edge würde ich Zig empfehlen. Die Sprachkomplexität ist viel geringer als bei modernem C++ und Rust. Ein weniger offensichtlicher positiver Nebeneffekt von C23 ist, dass Syntax wie ... = {} und {0} stärker an C++ angeglichen wird, sodass es für Maintainer von C-Bibliotheken weniger mühsam wird, Leute zu unterstützen, die C-Code mit einem C++-Compiler bauen wollen.
    • Wie früher schon C11, GNU-Erweiterungen und einzelne Features, die jetzt in C23 gelandet sind, wird es am Ende übernommen werden. Die Binärnotation 0b zum Beispiel ist in der Mikrocontroller-Welt weit verbreitet.
      Mikrocontroller-Toolchains bauen meist auf GCC auf und bekommen solche Features kostenlos dazu. Es gibt immer noch proprietäre C-Compiler, die hinterherhinken, aber sie sind nicht mehr so wichtig wie vor 20 Jahren.
    • Solche Features werden letztlich in den Mainstream durchsickern, ähnlich wie es jetzt mit C11 passiert.
      Wenn man nicht Embedded oder eine sehr breite Menge von Architekturen als Ziel hat, gibt es auch keinen Grund, ab heute kein C23 zu verwenden.
    • Der thread_local-Specifier wird bereits auf einigen Mikrocontroller-Plattformen genutzt, war vor C11 aber völlig illegal. Trotzdem vereinfacht er Speicherverwaltung in Thread-Umgebungen erheblich.
      Gibt es dafür einen Grund, extra in die C++-Welt zu wechseln?
    • Wenn LLVM/GCC ein Ziel unterstützt, bekommt man dann nicht automatisch auch die neue C-Version? Nur bei alten, vom Vendor für andere Architekturen angepassten GCC-Toolchains wird man sie wohl nicht bekommen.
  • Persönlich machen Dinge wie guard, defer, auto, constexpr und nullptr C für mich deutlich komplexer. Ich wähle C, weil ich Einfachheit brauche; wenn ich Komplexität will, würde ich normalerweise C++ wählen, auch wenn ich das eigentlich nicht will, oder lieber Go — und auf dem Server vielleicht Elixir.
    _BitInt(N) ist auch hässlich und erinnert mich an _Bool, das jetzt zum Glück bool geworden ist. constexpr und nullptr riechen sehr stark nach C++. Trotzdem ist Modern C ein hervorragendes Buch, und für C99, an dem ich weiter festhalten will, hat es mir gute Dienste geleistet.

    • Die Frage, was an NULL falsch ist, beantwortet sich dank eines der wenigen Vorteile der ISO-Standardisierung, wenn man die zugehörigen Dokumente liest: https://wg21.link/p2312
      Kurz gesagt: Wenn man ein NULL-Argument an ein type-generic Macro übergibt, können überraschende Ergebnisse entstehen, und der Status von bedingten Ausdrücken wie (1 ? 0 : NULL) und (1 ? 1 : NULL) hängt davon ab, wie NULL definiert ist. Außerdem kann es schwerwiegende Folgen haben, NULL an eine variadische Funktion zu übergeben, die einen Pointer erwartet. Auf vielen heutigen Architekturen haben int und void* unterschiedliche Größen; wenn NULL einfach 0 ist, wird also ein Argument mit falscher Größe an die Funktion übergeben.
    • Ich glaube nicht, dass Komplexität linear zunimmt. Im Gegenteil: Oft macht ein komplexeres Werkzeug den Prozess und das Endergebnis einfacher.
      Es ist ähnlich wie beim Hausbau. Hammer und Schraubendreher sind sehr einfach, ein Kran ist extrem komplex, aber der Kran macht das Hausbauen einfacher. Wenn man ein Haus nur mit Hammer und Schraubendreher bauen will, muss man einen enorm komplexen Ablauf entwerfen.
      Bei Programmiersprachen ist es genauso. Einen generischen Container in C++ zu bauen ist trivial, in C dagegen sehr schwierig. Man kann es mit void * und manuellen Casts bis zu einem gewissen Grad nachahmen, aber das ist umständlich, fehleranfällig und macht den Code komplexer.
      Bei std::sort und qsort ist es dasselbe. Durch die Stärke von Templates und Funktionsobjekten ist die Implementierung einfacher und schneller. Man muss kein void * übergeben und zur Laufzeit dereferenzieren, und der Vergleich kann direkt in die Funktionsdefinition wandern. Es gibt keinen indirekten Aufruf, keine Übergabe über den Stack, und die Vergleichsfunktion kann sogar inline werden. Sprachkomplexität bedeutet nicht Implementierungskomplexität.
    • auto ist vor allem nützlich, wenn man mit type-generic Macros arbeitet, aber in normalem Code sollte man es besser nicht verwenden. Ich hoffe, man vermeidet den Wahnsinn à la almost always auto, der eine Zeit lang in der C++-Welt in Mode war.
      Leider gibt es zwischen Compilern kleine Unterschiede. Soweit ich mich erinnere, implementiert Clang das C++-artige auto, während GCC das C-artige auto implementiert, sodass es bei auto-Pointern subtile Unterschiede gab. Ich weiß nicht, ob das inzwischen behoben wurde.
      _BitInt(N) verwendet man normalerweise nicht direkt, sondern legt per typedef die benötigte Breite fest, zum Beispiel typedef _BitInt(2) u2;. Hässliche Syntax wie _B ist nötig, weil Kombinationen aus Unterstrich und anschließendem Großbuchstaben im C-Standard reserviert sind, damit kleine Ergänzungen der Sprache nicht mit bestehendem Code kollidieren. Der Name _Bool hat denselben Grund. Soweit ich weiß, ist defer tatsächlich nicht in C23 gelandet.
  • In der alten Definition war nicht einmal festgelegt, ob NULL ein Zeiger oder eine Ganzzahl ist. Auf Plattformen, die der Posix-Anforderung ((void*)0) nicht folgen, war das deshalb eine Falle, die weder Zeigertyp noch -größe hatte.
    Dass constexpr und nullptr nach C++ riechen, liegt vermutlich daran, dass sie aus C++ zurückimportiert wurden. NULL kann man trotzdem weiterhin verwenden; nach außen hin scheint es aber als nullptr neu definiert worden zu sein.

    • Dieser Code hat einen Bug und kann auf manchen Architekturen abstürzen: execlp("echo", "echo", "Hello, world!", NULL);
      Dieser Code hat diesen Bug nicht: execlp("echo", "echo", "Hello, world!", nullptr);
      Das hier ist ebenfalls in Ordnung: execlp("echo", "echo", "Hello, world!", (char *)NULL);
  • Ich wollte fragen, ob es eine gute Liste von C-Büchern gibt, habe die Antwort dann aber selbst gefunden. Hier wird Modern C als Mittelstufe eingeordnet.
    https://stackoverflow.com/questions/562303/the-definitive-c-...

    • Ich mag Modern C und habe es an mehreren Stellen positiv bewertet gesehen. Der Einordnung als Mittelstufe stimme ich zu.
      Als moderne C-Begleiter zu K&R halte ich Ben Klemens’ 21st Century C und Kings C Programming: A Modern Approach für zugänglichere Alternativen.
    • Christopher Prescherns Fluent C: Principles, Practices and Patterns ist ebenfalls einen Blick wert.
    • Diese Liste ist nicht vollständig. Zum Beispiel fehlt Effective C: https://nostarch.com/effective-c-2nd-edition
      Persönlich finde ich Effective C besser als Modern C. Modern C ist sehr streng; es fühlt sich an, als lese man eine kommentierte Sprachspezifikation, was für Experten nötig sein mag, für jemanden wie mich, der C eher locker nutzt, aber langweilig zu lesen ist.
  • Einige von Metawares High-C-Erweiterungen gefallen mir ziemlich gut.
    https://news.ycombinator.com/item?id=41647843
    https://news.ycombinator.com/item?id=38938402

  • Ich verwende seit über einem Jahr modernes C++ für ein persönliches Projekt, einen Sprachinterpreter, denke wegen der mentalen Belastung durch C++ und der Tooling-Probleme aber immer wieder darüber nach, auf C umzusteigen. Visual Studios IntelliSense funktioniert mit C++20-Modulen immer noch fast gar nicht, und wegen der Fehlentwicklungen der Sprache wird zu viel in Interfaces gedrückt, wodurch auch die Kompilierzeiten hässlich werden.
    Andererseits habe ich mich so sehr an Klassen, Member-Funktionen, generische Programmierung und Namespaces gewöhnt, dass ich wohl schon in der Falle sitze.

    • Ich nutze C++ seit langer Zeit und habe absolut nicht vor, Destruktoren aufzugeben, nur um zu C zu wechseln.
      Hast du für diesen Zweck C# in Betracht gezogen? Visual Studio passt deutlich besser zu C#.
  • Wenn man in macOS Preview in der Seitenleiste auf Links im Inhaltsverzeichnis klickt, funktionieren sie nicht richtig.

    • Im PDF-Reader zathura habe ich einige Links im Inhaltsverzeichnis getestet, und sie funktionieren gut.
    • Das aktuelle Inhaltsverzeichnis ist eindeutig kaputt.
  • Es ist erst ein paar Jahre her, dass ich mich bei einer von mir gepflegten Library darauf verlassen konnte, dass alle C-Compiler C99 unterstützen: https://github.com/eyalroz/printf
    Doch kaum waren ein paar Jahre vergangen, wurde unweigerlich ein Issue eröffnet, das wegen irgendeiner uralten Embedded-Toolchain C89-Kompatibilität verlangte. C23 ist also schön und gut, aber irgendwie fühlt es sich an wie: Reden wir in 20 Jahren noch einmal darüber.

  • Kann jemand einen Artikel verlinken, der praxisnah erklärt, warum C faktisch bei C99 stehen geblieben ist? Unter den Projekten, über die man sprechen kann, nutzen kaum welche Features nach C11.

    • C99 ist immer noch neu. Microsoft hat versucht, C zu töten, indem es sich weigerte, etwas zu implementieren, das nicht auch in C++ vorhanden war. MSVC war mit der C99-Implementierung 16 Jahre zu spät und hat nur das Minimum implementiert. Auch die C11-Implementierung kam 11 Jahre zu spät.
      Weil C über Jahrzehnte praktisch eingefroren war, scheint sich die Nutzerschaft selbst in Richtung jener Leute selektiert zu haben, die C so mögen, wie es ist, und denen die Unterstützung uralter Sammelsurium-Compiler nichts ausmacht. Wer die Geduld verloren hatte oder eine Sprache des 21. Jahrhunderts wollte, ist zu C++/Rust/Zig usw. abgewandert.
    • Microsoft hat C99 faktisch sabotiert, indem es bis etwa 2015 kaum C99-Features im Visual-Studio-C-Compiler implementierte. Erst 2019 gestand man das Scheitern ein und begann wieder, neuere C-Versionen zu unterstützen. Das C-Frontend von MSVC liegt immer noch zuverlässig hinter Clang und GCC zurück.
      Um 2010 herum war MSVC noch sehr wichtig, was aus einer heutigen Perspektive seltsam klingt, in der die meisten Entwickler offenbar zu Linux gewechselt sind. Andererseits gibt es auch nicht viele Projekte, die C11-Features unbedingt brauchen. C11 hat C99 VLAs auch wieder weggenommen, was kein besonders bedauerlicher Verlust war. C23 könnte die erste Version seit C99 sein, für die sich ein Upgrade vieler C-Codebasen tatsächlich lohnt.