3 points par GN⁺ 2024-10-16 | 1 commentaires | Partager sur WhatsApp
  • La version révisée de Modern C, alignée sur le nouveau standard C, est publiée en téléchargement gratuit, ce qui permet de revoir les ressources d’apprentissage et de référence du langage C selon C23
  • La révision est centrée sur l’intégration de C23 et vise avant tout à accompagner la transition vers le nouveau standard en phase avec le calendrier et le processus de publication de l’ISO
  • Les nouvelles versions des principaux compilateurs implémentent déjà la plupart des fonctionnalités de C23, si bien que les changements du livre vont au-delà d’une présentation expérimentale pour rejoindre un environnement d’usage réel
  • Des évolutions du langage et de la bibliothèque comme _BitInt(N), nullptr, auto, typeof, constexpr, etc. sont largement intégrées, avec un impact aussi sur les pratiques d’écriture du code C existant
  • Une annexe de transition et des en-têtes include temporaires ont été ajoutés pour permettre des essais immédiats sur les plateformes existantes, mais le MEAP de Manning est toujours en cours

Ressources gratuites et documentation du standard

  • Modern C, édition C23 peut être téléchargé gratuitement
  • Des ressources associées sont également disponibles sur la page dédiée au livre
  • Cette édition révisée reprend plusieurs explications, mais son objectif principal est de refléter le nouveau standard du C, C23
  • Parmi les documents en accès public, la ressource la plus proche du contenu du nouveau standard est le PDF N3220
  • Les nouvelles versions des principaux compilateurs implémentent déjà la plupart des nouvelles fonctionnalités introduites par C23

Évolutions du langage C23 et support de transition

  • Les changements liés aux entiers occupent une place importante
    • Ajout du nouveau type à précision de bits _BitInt(N)
    • Ajout de nouveaux en-têtes de la bibliothèque C pour l’arithmétique avec vérification de débordement et la manipulation de bits
    • Prise en compte de la possibilité des types 128 bits sur les architectures modernes
    • Y compris des améliorations concrètes des types enum
  • Les autres nouveaux concepts de C23 sont également couverts
    • La constante nullptr et le type sous-jacent associé
    • Les annotations syntaxiques via les attributes
    • Des outils de programmation générique de types, dont auto et typeof
    • L’initialisation par défaut avec {} qui s’applique aussi aux tableaux de longueur variable
    • constexpr pour des constantes nommées de tous les types
  • Le nouveau contenu aborde aussi les expressions composées, les lambdas, l’« internationalisation » et jusqu’à une approche globale des défaillances de programme
  • Une annexe et des en-têtes include temporaires ont été ajoutés pour permettre de démarrer immédiatement avec C23 sur les plateformes existantes
  • Le MEAP de la nouvelle édition chez Manning est toujours ouvert

1 commentaires

 
GN⁺ 2024-10-16
Commentaires sur Hacker News
  • Contrairement à l’endianness little-endian de stockage sur ma machine, la représentation où l’octet de poids fort vient d’abord s’appelle big-endian, mais dire que les deux sont encore couramment utilisés sur les processeurs modernes me semble un peu exagéré, car à part s390x, il n’en reste pratiquement plus
    On dirait qu’un commentaire sur les architectures big-endian de niche / disparues, le préféré de tout le monde, va bientôt apparaître

    • Arm est bi-endian et reste présent sur la plupart des téléphones
      Comme le disent d’autres commentaires bien classés, « moderne » ne veut pas forcément dire populaire ou largement utilisé
    • POWER est bi-endian. Linux sur POWER est aujourd’hui en little-endian, et autrefois Linux sur POWER en big-endian était courant, mais les distributions ont basculé il y a quelques années. En revanche, AIX et IBM i sont en big-endian
      AIX et IBM i ne sont peut-être pas aussi actifs que les mainframes IBM, mais on peut dire qu’AIX est plus vivant que Solaris ou HP/UX, et encore davantage si on le compare aux nombreux Unix commerciaux d’autrefois. IBM i tient aussi encore le coup, mais il est bien plus vivant que des plateformes legacy midrange concurrentes comme HP MPE, dont le support éditeur a officiellement pris fin
    • MIPS est encore assez présent dans le matériel réseau grand public
    • D’une certaine façon, on peut dire que tous les processeurs sont couramment utilisés. Parce que le network byte order est en big-endian
    • Sparc n’est-il pas big-endian ?
  • L’aspect le plus important du C, c’est sa portabilité. L’idée est d’aller des petits microcontrôleurs à presque toutes les plateformes de calcul, donc on peut se demander si les nouvelles versions du C seront adoptées à ce point
    Si je voulais utiliser l’état de l’art, je choisirais probablement C++2x ou Rust plutôt que C. Est-ce que je rate quelque chose ? Je me demande quels sont les avantages de ce soi-disant C moderne

    • L’un des avantages à écrire du code C, c’est qu’on n’a pas à se lancer dans ces débats pénibles sur ce à quoi devrait ressembler le code idiomatique, ou sur quel sous-ensemble du langage il faudrait utiliser
      Pour être à la pointe, je recommanderais Zig. La complexité du langage est bien plus faible que celle du C++ moderne et de Rust. Un effet secondaire positif, plus discret, de C23 est d’aligner davantage des syntaxes comme ... = {} et {0} sur le C++, ce qui rend la vie un peu moins pénible aux mainteneurs de bibliothèques C qui veulent prendre en charge ceux qui essaient de compiler leur code C avec un compilateur C++
    • Comme cela a été le cas pour C11, les extensions GNU et les fonctionnalités individuelles désormais entrées dans C23, cela finira par être adopté. Par exemple, la notation binaire 0b est largement utilisée dans le monde des microcontrôleurs
      Les toolchains pour microcontrôleurs sont en général construites sur GCC, donc elles récupèrent ces fonctionnalités gratuitement. Il reste toujours des compilateurs C propriétaires en retard, mais ils n’ont plus l’importance qu’ils avaient il y a 20 ans
    • Ces fonctionnalités finiront par entrer dans le courant dominant. Un peu comme c’est le cas pour C11 aujourd’hui
      À moins de viser l’embarqué ou un ensemble très large d’architectures, rien n’empêche d’utiliser C23 dès maintenant
    • Le spécificateur thread_local est déjà utilisé sur certaines plateformes de microcontrôleurs, alors qu’avant C11 il était complètement illégal. Pourtant, il simplifie énormément la gestion mémoire dans les environnements multithread
      Y a-t-il vraiment une raison d’entrer dans l’univers du C++ juste pour ça ?
    • Si la cible est prise en charge par LLVM/GCC, est-ce qu’on n’obtient pas automatiquement aussi la nouvelle version du C ? En revanche, on ne l’aura pas sur de vieux toolchains GCC modifiés par le fournisseur pour d’autres architectures
  • Personnellement, des choses comme guard, defer, auto, constexpr, nullptr rendent le C beaucoup plus complexe. Si on choisit le C, c’est parce qu’on a besoin de simplicité ; si on voulait de la complexité, on choisirait en général du C++, même sans vraiment en avoir envie, ou alors Go, voire Elixir côté serveur
    _BitInt(N) est aussi assez laid, et fait heureusement penser à _Bool, qui est maintenant bool. constexpr et nullptr sentent beaucoup trop le C++. Cela dit, Modern C est un excellent livre, et je m’en suis très bien servi pour du C99 que je compte continuer à utiliser

    • Si l’on demande ce qui ne va pas avec NULL, la réponse se trouve en lisant la documentation pertinente, grâce à l’un des rares avantages de la normalisation ISO : https://wg21.link/p2312
      En résumé, passer NULL comme argument à une macro générique en type peut produire des résultats surprenants, et le statut d’expressions conditionnelles comme (1 ? 0 : NULL) et (1 ? 1 : NULL) varie selon la manière dont NULL est défini. De plus, passer NULL à une fonction variadique qui attend un pointeur peut avoir de graves conséquences. Sur beaucoup d’architectures modernes, int et void* n’ont pas la même taille ; si NULL n’est qu’un simple 0, alors un argument de taille incorrecte est transmis à la fonction
    • Je ne pense pas que la complexité augmente de façon linéaire. Au contraire, il arrive souvent qu’un outil plus complexe simplifie à la fois le processus et le résultat final
      C’est comme construire une maison. Un marteau et un tournevis sont très simples, tandis qu’une grue est extrêmement complexe, mais ce qui simplifie la construction d’une maison, c’est la grue. Si l’on devait construire une maison uniquement avec un marteau et un tournevis, il faudrait inventer une procédure incroyablement complexe
      C’est pareil pour les langages de programmation. Créer des conteneurs génériques en C++ est trivial, alors qu’en C c’est très difficile. On peut l’imiter dans une certaine mesure avec void * et des conversions manuelles, mais c’est fastidieux, sujet aux erreurs, et le code devient plus complexe
      C’est pareil pour std::sort et qsort. Avec la puissance des templates et des foncteurs, l’implémentation est plus simple et plus rapide. Il n’est pas nécessaire de passer un void * ni de le déréférencer à l’exécution, et on peut intégrer la comparaison directement dans la définition de la fonction. Il n’y a ni appel indirect ni passage par la pile, et la fonction de comparaison peut même être inline. La complexité du langage ne signifie pas complexité d’implémentation
    • auto est surtout utile quand on manipule des macros génériques en type, mais il vaut mieux éviter de l’utiliser dans le code ordinaire. J’espère qu’on évitera la folie du almost always auto qui a été à la mode pendant un temps dans le monde du C++
      Malheureusement, il y a encore de légères différences selon les compilateurs. Si je me souviens bien, Clang implémentait auto à la manière du C++, tandis que GCC implémentait auto à la manière du C, ce qui créait de subtiles différences pour les pointeurs auto. Je ne sais pas si cela a été corrigé depuis
      _BitInt(N) ne s’utilise généralement pas directement ; on définit plutôt un typedef avec la largeur voulue. Par exemple : typedef _BitInt(2) u2;. Une syntaxe disgracieuse comme _B est nécessaire parce que les combinaisons underscore + majuscule sont réservées par la norme C, afin d’éviter les conflits avec du code existant lorsqu’on ajoute de petites fonctionnalités au langage. _Bool porte ce nom pour la même raison. À ma connaissance, defer n’est en fait pas entré dans C23
  • L’ancienne définition ne précisait même pas si NULL était un pointeur ou un entier. Sur les plateformes qui ne respectaient pas l’exigence Posix de ((void*)0), c’était donc un véritable piège, ni de type pointeur ni de taille pointeur
    Si constexpr et nullptr ont une odeur de C++, c’est probablement parce qu’ils ont été réimportés depuis le C++. On peut toutefois continuer à utiliser NULL, qui semble en apparence avoir été redéfini comme nullptr

    • Ce code a un bug et peut même provoquer un crash sur certaines architectures : execlp("echo", "echo", "Hello, world!", NULL);
      Ce code n’a pas ce bug : execlp("echo", "echo", "Hello, world!", nullptr);
      Celui-ci fonctionne aussi : execlp("echo", "echo", "Hello, world!", (char *)NULL);
  • J’allais demander s’il existait une bonne liste de livres sur le C, puis j’ai trouvé la réponse moi-même. Modern C y est classé comme intermédiaire
    https://stackoverflow.com/questions/562303/the-definitive-c-...

    • J’aime bien Modern C et je l’ai vu recommandé en bien à plusieurs endroits. Je suis d’accord avec son classement en intermédiaire
      Comme compagnon moderne de K&R pour le C, 21st Century C de Ben Klemens et C Programming: A Modern Approach de King me paraissent des alternatives plus accessibles
    • Fluent C: Principles, Practices and Patterns de Christopher Preschern mérite aussi le détour
    • Cette liste n’est pas complète. Par exemple, Effective C n’y figure pas : https://nostarch.com/effective-c-2nd-edition
      Personnellement, je préfère Effective C à Modern C. Modern C est extrêmement rigoureux ; pour un expert, cela peut être utile, mais pour quelqu’un comme moi qui utilise le C de façon assez légère, la lecture ressemble à celle d’une spécification annotée du langage et devient ennuyeuse
  • J’aime assez certaines des extensions High C de Metaware
    https://news.ycombinator.com/item?id=41647843
    https://news.ycombinator.com/item?id=38938402

  • Depuis plus d’un an, j’utilise le C++ moderne pour un projet personnel d’interpréteur de langage, mais la charge mentale du C++ et ses problèmes d’outillage me font sans cesse envisager un retour au C. L’IntelliSense de Visual Studio ne fonctionne toujours presque pas avec les modules C++20, et à cause des échecs du langage, bien trop de choses sont repoussées dans les interfaces, ce qui rend les temps de compilation hideux
    À l’inverse, je me suis tellement habitué aux classes, aux fonctions membres, à la programmation générique et aux espaces de noms que j’ai peut-être déjà mordu à l’hameçon

    • J’utilise le C++ depuis longtemps, et je n’ai absolument aucune envie d’abandonner les destructeurs pour passer au C
      Pour cet usage, as-tu envisagé le C# ? Visual Studio s’accorde bien mieux avec C#
  • Dans Preview sur macOS, cliquer sur les liens de table des matières dans la barre latérale ne fonctionne pas correctement

    • J’ai testé quelques liens de la table des matières dans le lecteur PDF zathura, et ils fonctionnent bien
    • La table des matières est clairement cassée en ce moment
  • Cela ne fait que quelques années que, pour une bibliothèque que je maintiens, je peux considérer comme acquis que les compilateurs C prennent tous en charge le C99 : https://github.com/eyalroz/printf
    Et pourtant, quelques années plus tard, un ticket a inévitablement été ouvert pour demander la compatibilité C89 à cause d’une antique toolchain embarquée. Donc oui, C23 c’est bien, mais j’ai un peu l’impression qu’on devrait en reparler dans 20 ans

  • Quelqu’un peut-il lier un article expliquant, d’un point de vue pratique, pourquoi le C est en réalité bloqué au C99 ? Il y a très peu de projets dignes d’être mentionnés qui utilisent des fonctionnalités postérieures au C11

    • Le C99 est encore récent. Microsoft a essayé de tuer le C en refusant d’implémenter quoi que ce soit qui n’existait pas aussi en C++, à part le strict minimum. MSVC a eu 16 ans de retard sur l’implémentation du C99, et n’en a implémenté que le minimum. L’implémentation du C11 a eu 11 ans de retard
      Comme le C est resté de fait gelé pendant des décennies, sa base d’utilisateurs semble s’être auto-sélectionnée en faveur de gens qui aiment le C tel qu’il est et n’hésitent pas à supporter des compilateurs antiques et hétéroclites. Ceux qui ont perdu patience ou voulaient un langage du XXIe siècle sont partis vers le C++/Rust/Zig, etc.
    • Microsoft a de fait freiné le C99 en n’implémentant quasiment aucune fonctionnalité C99 dans le compilateur C de Visual Studio jusqu’aux environs de 2015. Puis, en 2019, l’entreprise a fini par admettre l’échec et a recommencé à prendre en charge des versions plus récentes du C. Le front-end C de MSVC reste encore nettement à la traîne par rapport à Clang et GCC
      Cela peut sembler étrange vu d’une époque où la plupart des développeurs semblent avoir migré vers Linux, mais vers 2010, MSVC était encore très important. En revanche, il n’existe pas tant de projets que ça qui ont absolument besoin des fonctionnalités du C11. Le C11 a aussi supprimé les VLA du C99, et ce n’était pas une grande perte. Le C23 est peut-être la première version depuis le C99 qui mérite réellement une mise à niveau pour beaucoup de bases de code C