The C23 edition of Modern C

8 hours ago (gustedt.wordpress.com)

Important reminder just in the Preface :-)

Takeaway #1: "C and C++ are different: don’t mix them, and don’t mix them up"

  • >Takeaway #1: "C and C++ are different: don’t mix them, and don’t mix them up"

    Where "mixing C/C++" is helpful:

    - I "mix C in with my C++" projects because "sqlite3.c" and ffmpeg source code is written C. C++ was designed to interoperate with C code. C++ code can seamlessly add #include "sqlite3.h" unchanged.

    - For my own code, I take advantage of "C++ being _mostly_ a superset of C" such as using old-style C printf in C++ instead of newer C++ cout.

    Where the "C is a totally different language from C++" perspective is helpful:

    - knowing that compilers can compile code in "C" or "C++" mode which has ramifications for name mangling which leads to "LINK unresolved symbol" errors.

    - knowing that C99 C23 has many exceptions to "C++ is a superset of C" : https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B...

    • The entire I/O streams (where std::cout comes from) feature is garbage, if this was an independent development there is no way that WG21 would have taken it, the reason it's in C++ 98 and thus still here today is that it's Bjarne's baby. The reason not to take it is that it's contradictory to the "Don't use operator overloading for unrelated operations" core idea. Bjarne will insist that "actually" these operators somehow always meant streaming I/O but his evidence is basically the same library feature he's trying to justify. No other language does this, and it's not because they can't it's because it was a bad idea when it was created, it was still a bad idea in 1998, the only difference today is that C++ has a replacement.

      The modern fmt-inspired std::print and std::println etc. are much nicer, preserving all the type checking but losing terrible ideas like stored format state, and localisation by default. The biggest problem is that today C++ doesn't have a way to implement this for your own types easily, Barry illustrates a comfortable way this could work in C++ 26 via reflection which on that issue closes the gap with Rust's #[derive(Debug)].

      16 replies →

    • C++ can seamlessly include C89 headers.

      The C library headers for libraries I write often include C11/C99 stuff that is invalid in C++.

      Even when they are in C89, they are often incorrect to include without the include being in an `extern "C"`.

      3 replies →

  • My brief foray into microcontroller land has taught me that C and C++ are very much mixed.

    It's telling that every compiler toolchain that compiles C++ also compiles C (for some definition of "C"). With compiler flags, GCC extensions, and libraries that are kinda-sorta compatible with both languages, there's no being strict about it.

    _My_ code might be strict about it, but what about tinyusb? Eventually you'll have to work with a library that chokes on `--pedantic`, because much (most?) code is not written to a strict C or C++ standard, but is "C/C++" and various extensions.

  • Specially relevant to all those folks that insist on "Coding C with a C++ compiler", instead of safer language constructs, and standard library alternatives provided by C++ during the last decades.

    • Perfectly valid to do if you need to interface with a large C code base and you just want to do some simple OO here and there. Especially if you cannot have runtime exceptions and the like.

      This is how I managed to sneak C++ into an embedded C codebase. We even created some templates for data structures that supported static allocation at compile time.

      27 replies →

    • I mean as long as your goal is specifically to do that I think it's fine. Using a C++ compiler to compile a C program isn't that rare.

  • A couple of months ago, in the company I work, there was a talk from HR, where they explained how to make a good CV (the company is firing lots of people). She say: "if you have experience in programming C, you can writing just that, or, if you have lots of experience in C, is customary to write ``C++ Experience'' "

    Sooo... yeah... I should definitely change company!

I was going to ask if there is a good list of C books and then answered my own question. It categorizes _Modern C_ as Intermediate level.

https://stackoverflow.com/questions/562303/the-definitive-c-...

  • Note that this is not a complete list, fwiw. For example, I doesn't include "Effective C." [1].

    I like "Effective C" over "Modern C" because it's more engaging ... "Modern C" is super rigorous and feels a bit like reading an annotated spec of the language, which is what an expert may need, but makes for a dull read for a casual C user like me.

    --

    1: https://nostarch.com/effective-c-2nd-edition

Can someone link me to an article that explains why C is basically frozen at C99 for all practical purposes? Few projects worth talking about leverage features from C11 and newer

Table of contents in the sidebar doesn't work properly for me when I click on an entry (in macOS Preview).

Really looking forward to #embed, once the compilers catch up. Until then, Golang.

Wow, the use of attributes like [[__unsequenced__]], [[maybe_unused]] and [[noreturn]] throughout the book is really awful. It seems pretty pedantic of the author to litter all the code examples with something that is mostly optional. For a second I wondered if C23 required them.

  • Such is the issue with bad defaults. Opting into the sensible thing makes most of your code ugly, instead of just the exceptions.

It's only been a few years since I've come to feel I can rely on C compilers all supporting C99, for a library I'm maintaing [1]. And after a couple of years, sure enough - I get an issue opened asking for C89 compatibility because of some arcane embedded toolchain or what-not.

So, C23? ... that's nice and all, but, let's talk about it in 20 years or so T_T

[1]: https://github.com/eyalroz/printf

How does "Modern" C compare safety-wise to Rust or Zig?

  • Modern C still promptly decays an array to a pointer, so no array bounds checking is possible.

    D does not decay arrays, so D has array bounds checking.

    Note that array overflow bugs are consistently the #1 problem with shipped C code, by a wide margin.

    • > no array bounds checking is possible.

      This isn’t strictly true, a C implementation is allowed to associate memory-range (or more generally, pointer provenance) metadata with a pointer.

      The DeathStation 9000 features a conforming C implementation which is known to catch all array bounds violations. ;)

      8 replies →

  • You'd be surprised: Zig has one UB (Undefined Behaviour) that C doesn't have!

    In release fast mode, unsigned overflow/underflow is undefined in Zig whereas in C it wraps.

    :-)

    Of course C has many UBs that Zig doesn't have, so C is far less safe than Zig, especially since you can use ReleaseSafe in Zig..

  • Modern C is barely any different than older C. The language committee for C is extremely conservative, changes tend to happen only around the edges.

Do they still use 0-terminated strings/char* as the main string type?

Is the usage of single linked lists still prevalent as the main container type?

  • > Do they still use 0-terminated strings/char* as the main string type?

    Of course, it's still C.

    > Is the usage of single linked lists still prevalent as the main container type?

    As far as I can remember, the C standard library has never had any functions that used linked lists. Nor are there any container types, linked lists or otherwise, provided by C. So I'd say this is a question about how people teach and use C, not related to the language -- or language spec version -- itself.

  • I don’t think that will ever change. Will the possibly introduce a more modern string2 type? Maybe but it will probably be unlikely before 2050

  • The C standard library provides no recognizable container types, so there's no "main" anything.