C11 and C23 feature highlights
This is a short overview of things added along with c11 and c23 which I find useful or interesting.
auto
keyword… yet again
auto has been repurposed in C23. Originally, it defined a storage duration for local variables (similarly as static) now, comparably as in C++, it can be used for type inference purposes.
|
|
In its original purpose, variables marked with auto have their storage automatically allocated and deallocated on scope entry and exit. It was implied for all local, stack variables. No one really used auto explicitly because it was just unnecessary now, it has an extra meaning.
It’s useful in most expected contexts:
|
|
Unfortunately, since C has no way to support function overloads and generic functions, it can’t be applied to function arguments or return values i.e.:
|
|
More details in the proposal: N3007.
Attribute specifiers
Another addition similar to what we’ve got in C++ are attribute specifiers. C23 introduces the following:
[[deprecated]]
[[fallthrough]]
[[nodiscard]]
[[maybe_unused]]
[[noreturn]]
[[unsequenced]]
[[reproducible]]
Here’s some usage examples:
|
|
_Generic
Contrary to what one might believe on first glance, _Generic
does not provide
similar functionality as generics in C++ or any other language. This is not a
mechanism to declare generic types, variables, functions or object templates. In
its essence, it’s just an expression selector. It’s composed of a
controlling expression and a list of generic associations to implement a so
called Generic selection. It allows to choose an expression, in compile time, from the list of
expressions in generic associations list based on the type of the controlling expression.
Here’s a simple example:
|
|
The 1u + 2u
is a controlling expression. Its resulting type will be
unsigned
. So, for this case, the _Generic
will choose, at compile time,
the expression 100
(associated with unsigned
type) as an expression used in
an assignment to variable i
.
Of course, this example is not very useful since it’s using static data.
_Generic
seems to work best to implement function overloading, just like in C++.
Consider the following:
|
|
SIN
might be thought of as a function with three overloads, selected at
compile time, for types double
, long double
and float
. These overloads
are implemented with sin
, sinl
and sinf
. _Generic
allows them to share a common interface.
_Generic
was introduced in C11, more can be found on cppreference.
nullptr
and nullptr_t
Along with C23 nullptr_t
type and its predefined constant nullptr
has been
introduced to avoid implicit conversion of integer types from/to NULL
.
It’s a small change that was introduced in C++ along with c++11.
The problem is well understood by now. It’s a nice to have, making C at least a little bit safer. More details available on cppreference.
#embed
- binary inclusion
This is a great feature allowing for inclusion of data as binary blobs into the source code. #embed has been accepted in C23. There’s a similar proposal for C++ which is still a work in progress. At the time of writing, only clang19 actually implements it. Still though, it’s something I’m really happy with.
Usually, as a workaround you’d generated a header file with a tool like xxd
:
|
|
With #embed
this is now supported directly by the compiler:
|
|
I’ve tested that myself with clang19 and it worked like a charm. Now, writing quines has become trivial. Looking forward for the same feature in C++.
Static asserts
_Static_assert
has become a keyword on its own: static_assert
. It’s
similar in nature to its C++ protoplast but quite limited by C itself. Again,
since there’s no function templates, object templates, constexpr
functions and all the cool stuff we have in C++, all it can do is assert on
integer constant expressions which isn’t really that useful.
It can be combined with _Generic
to perform some compile time checks on its
result but that’s the best I can think of i.e.:
|
|
Conclusion
All these changes are definitely a step in the right direction. It’s definitely better late then never regardless of C’s diminishing influence and popularity.
One concerning factor is that the divergence between C and C++ seems to be widening in some regards. This is not necessarily a good thing and might introduce unneeded confusion considering the fact that majority of the features adapted to C are already well established in C++. For such things, I’d consider a direct, compatible port (as much as technically possible) to be a much better approach.