Low-Level C Programming – CSE 325 Lecture Videos
youtube.com
youtube.com
The real reason to use C in embedded programming is that "vendor_forked_gcc_from_10_years_ago" is your only compiler on most platforms, and almost certainly does not support C++ or has some bugs in its C++ compiler implementation.
There’s only a handful of things that matter when it comes to generating embedded code for a specific microcontroller and most of it comes down to the format required for the final linked executable, which usually boils down to an ld script.
In general any kind of ARM chip is going to work perfectly fine with the latest C++.
Or to rephrase your comment, the real reason to use C is because all the support tooling, header files, peripheral drivers, RTOS, etc. are written in C, and there’s not a huge benefit to moving user code to C++.
I remember using a vendor fork of GCC 2 (released in 1999) to compile for a specific microcontroller in 2015, but since that vendor now releases Cortex-M0's, the vendor-blessed compiler uses a near-mainline GCC.
For rare targets, there's just no money in making a compiler work for more than this small subset of C. My favorite example is the compiler for a really strange architecture where everything is 24 bits. Char is short is long is a pointer.
Bigger controllers tend to be ARM-based, so it's getting better.
Also Tricore has a big share, but their compiler support is getting better. There was even a rust compiler being announced recently, though I doubt it's using LLVM backend, so it's likely to be behind in terms of features.
I thought char was required by the C standard to be 8 bits. If everything is 24 bits, how do you cover 24 bit address space with 8 bit pointers?
It's not. (EDIT: it is required to be at least 8 bits though)
The C standard specifies so much less than everyone thinks.
Common misconception.
to be at least 8 bits though
At least 8 bits for the latter-day compilers for which the 'standard' was the guide. I have seen C compilers where char was 6-bit or 7-bits matching the underlying hardware. However, those probably never implemented more than K&R or some pre-standardization perversion of it.
Also...as noted a char can be more than 8 bits, pre- and post-standardization. I've heard tell of (at least) 9-, 16-, 24- and 32-bit chars on more or less weird platforms.
At some point I'm sure the standards bodies will do some variation of depreciating 8-ish-bit char's for 32-bit Unicode as the standard representation for text.
DSP? That's the one example I can think of where 24-bit architectures are in active use (and many of them are not Von Neumann, but Harvard with separate data/program memory.)
is there no appetite to self-write a compiler for the architecture being used?
Once that happens, you might as well just use C.
* RAII makes modern C++ a lot cleaner for things like holding locks, disabling interrupts, and other stuff that needs to be unwound at function exit, and doesn't need heap allocation. Until C gets 'defer', the cleanest alternative is gotos.
* Operator overloading is pretty nice for code where you have custom data types (like 2-element vectors), which can be very common with DSP systems.
* References give you a non-nullable pointer type.
* Template-based data structures are much nicer than the C alternative: macros.
* std::array is kind of nice, and doesn't have dynamic memory. Also, while std::string is pretty unsafe, std::string_view is very nice!
* For the few dynamic data structures you may want to use, you can write a quick allocator and hook that into every std data structure you want rather than writing your own data structures. The "null allocator" strategy is an option: just return the same pointer to one fixed chunk of memory, and fail if the data structure grows too big. You can also do the "init allocator" strategy: Only allow dynamic allocation during initialization.
> Until C gets 'defer', the cleanest alternative is gotos.
In an embedded setting, a given project is probably only ever targeting a single compiler, and given that in non-exotic situations it’s a vendor fork of GCC, you should be fine using GCC-specific features. And __attribute__((cleanup)) (essentially a block-scoped defer with cleanup code exiled to a separate function) has been in GCC since (checks docs) version 3.3, from 2003. It would have to be a truly spectacular vendor GCC fork to be missing that feature. (For reference, the year 2003 also saw the release of Linux kernel 2.6, as in two-six-zero.)