# modern-c-features
NOTE: this is a web “mirror” of Anthony Calandra’s modern-c-features shared under MIT License (see at bottom). The only reason I do a copy is I hate reading markdowns from github. I want something simple and plain for my own reference.
# Overview
A collection of descriptions along with examples for C language and library features.
C23 includes the following language features:
- auto
- constexpr
- decimal floating-point types
- bit-width integers
- binary literals
- char8_t
- utf-8 character literals
- unicode string literals
- empty initializer
- attributes
- new keywords (C23)
- nullptr
#embed
- enums with underlying type
- typeof
- improved compatibility for tagged types
C23 includes the following library features:
- floating-point formatting functions
- memset_explicit
unreachable
macromemccpy
strdup
andstrndup
gmtime_r
andlocaltime_r
timespec_getres
C17 contains defect reports and deprecations.
C11 includes the following language features:
- generic selection
- alignof
- alignas
- static_assert
- noreturn
- wide string literals
- anonymous structs and unions
C11 includes the following library features:
- bounds checking
- timespec_get
- aligned_malloc
- char32_t
- char16_t
- <stdatomic.h>
- <threads.h>
- quick exiting
- exclusive mode file opening
# C23 Language Features
# auto
auto
-typed variables are deduced by the compiler according to the type of their initializer.
|
|
See: generic selection.
# constexpr
Scalars specified with constexpr
are constants: they cannot be modified after being initialized, and must be fully initialized to a value that can be stored in the given type.
The C constexpr
specifier does not support functions, structures, unions, or arrays. Additionally, constexpr
cannot be specified for pointer, atomic, or volatile-qualified types.
|
|
# Decimal floating-point types
Supports IEEE-754 Decimal floating-point types, _Decimal32
, _Decimal64
, _Decimal128
. These floating point types are designed for base-10 floating point semantics.
As of September 2023, compiler support is lacking for most features.
|
|
# Bit-width integers
The _BitInt(N)
type allows specifying an N
bit integer signed or unsigned type.
|
|
# Binary literals
Binary literals provide a convenient way to represent a base-2 number. It is possible to separate digits with '
.
|
|
# char8_t
An unsigned char type for holding 8-bit wide characters.
See: char32_t, char16_t, unicode string literals.
# UTF-8 character literals
A character literal that begins with u8
is a character literal of type char8_t
. The value of a UTF-8 character literal is equal to its ISO 10646 code point value.
|
|
See: char8_t, unicode string literals.
# Unicode string literals
String literals prefixed with u8
, u
, and U
represent UTF-8, UTF-16, and UTF-32 strings respectively.
See: char32_t, char16_t, char8_t, wide string literals.
# Empty initializer
An object is empty-initialized if it is explicitly initialized using only a pair of braces. Arrays of unknown size cannot be initialized by an empty initializer. If an object is initialized using an empty initializer, default initialization occurs:
- Pointers are initialized to
NULL
; - Decimal floating point types are initialized to positive zero;
- Arithmetic types are initialized to zero;
- Array types are initialized such that each slot is initialized according to these rules;
- Aggregate types are initialized such that each member is initialized according to these rules;
- Unions are initialized such that the first named member is initialized according to these rules.
|
|
# Attributes
Attributes provide a universal syntax over __attribute__(...)
, __declspec
, etc. C++-styled attributes.
C23 provides the following attributes:
[[deprecated("reason")]]
- the entity declared with the attribute (such as a function), is deprecated. Specifying a reason is optional and can be ommitted.[[fallthrough]]
- indicates falling-through in a switch statement case is intentional.[[nodiscard("reason")]]
- encourages a compiler warning if the return value for the function declared with this attribute is discarded. Specifying a reason is optional and can be omitted.[[maybe_unused]]
- suppresses a compiler warning on the entity where this attribute is declared if it’s unused (such as an unused function parameter), if applicable.[[noreturn]]
- indicates that the function this attribute is declared on does not return.- Compiler-specific directives - such as
[[clang::no_sanitize]]
.
|
|
See: noreturn.
# New keywords (C23)
New keywords replacing traditional macros definitions:
true
andfalse
thread_local
static_assert
See: static_assert.
# nullptr
Introduces a new null pointer type designed to replace NULL
. nullptr
itself is of type nullptr_t
which can be implicitly converted into pointer types and bool
, and – unlike NULL
– is not convertible to integral types.
|
|
#
#embed
#embed
is a preprocessor directive to include text and binary resources directly into source code. This pattern is common in applications such as games that create custom fonts, load images into memory, etc. This allows the C preprocessor to replace external tools that convert resources to byte representations as C arrays.
The #embed
preprocessor directive also includes optional parameters including suffix
, prefix
, and if_empty
.
|
|
# Enums with underlying type
C23 enums provide the ability to optionally specify the underlying type.
|
|
# typeof
Gets the type of an expression, similar to decltype
in C++. Also includes typeof_unqual
which removes cv-qualifiers from the type.
|
|
# Improved compatibility for tagged types
Tagged types that have the same tag name and content become compatible not only across translation units but also inside the same translation unit. Also, redeclaration of the same tagged types is now allowed.
|
|
# C23 Library Features
# Floating-point formatting functions
Converts floating-point values to byte strings. Convert floats
, doubles
, and long double
s using strfromf
, strfromd
, and strfroml
respectively.
|
|
# memset_explicit
Copies the given value into each of the first count
characters of the object pointed to by dest
. Unlike memset
, this function cannot be optimized away by the compiler; therefore, this function is guaranteed to perform the memory write.
|
|
#
unreachable
macro
Provides a standard macro for (historically) compiler-specific macros for denoting an unreachable area of the code.
|
|
#
memccpy
Copies bytes from source to destination stopping after either the terminating byte is found (and also copied) or the given number of bytes is copied.
|
|
#
strdup
and strndup
These functions duplicate a given string by allocating a buffer and copying. The returned string is always null-terminated. Be sure to free the memory returned by thhese functions after use.
|
|
#
gmtime_r
and localtime_r
Works as gmtime
and localtime
does but uses a given storage buffer for the result. Returns a NULL
pointer on error.
|
|
#
timespec_getres
Stores the resolution of time provided by the given base. Returns zero on failure.
|
|
# C11 Language Features
# Generic selection
Using the _Generic
keyword, select an expression based on the type of a given controlling expression. The format of _Generic
is as follows:
_Generic( controlling-expression, T1: E1, ... )
where:
controlling-expresion
is an expression that yields a type present in the type list (T1: E1, ...
).T1
is a type.E1
is an expression.
Optionally, specifying default
in a type list, will match any controlling expression type.
|
|
# alignof
Queries the alignment requirement of a given type using the _Alignof
keyword or alignof
convenience macro defined in <stdalign.h>
.
|
|
max_align_t
is a type whos alignment is as large as that of every scalar type.
# alignas
Sets the alignment of the given object using the _Alignas
keyword, or alignof
macro in <stdalign.h>
.
|
|
max_align_t
is a type whos alignment is as large as that of every scalar type.
# static_assert
Compile-time assertion either using the _Static_assert
keyword or the static_assert
keyword macro defined in assert.h
.
|
|
See: new keywords (C23).
# noreturn
Specifies the function does not return. If the function returns, either by returning via return
or reaching the end of the function body, its behaviour is undefined. noreturn
is a keyword macro defined in <stdnoreturn.h>
.
|
|
See: attributes.
# Wide string literals
Create 16-bit or 32-bit wide string literals and character constants.
|
|
See: char32_t, char16_t, unicode string literals.
# Anonymous structs and unions
Allows unnamed (i.e. anonymous) structs or unions. Every member of an anonymous union is considered to be a member of the enclosing struct or union, keeping the layout intact. This applies recursively if the enclosing struct or union is also anonymous.
|
|
|
|
# C11 Library Features
# Bounds checking
Many standard library functions now have bounds-checking versions of existing functions. You can specify a callback function using set_constraint_handler_s
as a constraint handler when a function fails its boundary check. The standard library includes two constraint handlers: abort_handler_s
which writes to stderr
and terminates the program; and ignore_handler_s
ignores the violation and continue the program.
Standard library functions with bounds-checked equivalents will be appended with _s
. For example, some bounds-checked versions include fopen_s
, get_s
, asctime_s
, etc.
Example of a custom constraint handler with get_s
:
|
|
If a user enters a string greater than or equal to BUFFER_SIZE
the custom constraint handler will be called.
# timespec_get
Populates a struct timespec
object with the current time given a time base.
|
|
# aligned_malloc
Allocates bytes of storage whose alignment is specified.
|
|
# char32_t
An unsigned integer type for holding 32-bit wide characters.
See: wide string literals, char16_t, unicode string literals.
# char16_t
An unsigned integer type for holding 16-bit wide characters.
See: wide string literals, char32_t, unicode string literals.
# <stdatomic.h>
# Atomic types
The _Atomic
type specifier/qualifier is used to denote that a variable is an atomic type. An atomic type is treated differently than non-atomic types because access to atomic types provide freedom from data races. For example, the following code was compiled on x86 clang 4.0.0:
|
|
Notice that the atomic type used a mov
then an xchg
instruction to assign the value 1
to a
, while b
uses a single mov
. Other operations may include +=
, ++
, and so on… Ordinary read and write access to atomic types are sequentially-consistent.
See this StackOverflow post for more information.
A variety of typedefs exist which expand to _Atomic T
. For example, atomic_int
is typedef’d to _Atomic int
. See this list for a more complete list.
Atomic types can also be tested for lock-freedom by using the atomic_is_lock_free
function or the various ATOMIC_*_LOCK_FREE
macro constants. Since atomic_flag
s are guaranteed to be lock-free, the following example will always return 1
:
|
|
# Atomic flags
An atomic_flag
is a lock-free (guaranteed), atomic boolean type representing a flag. An example of an atomic operation which uses a flag is test-and-set. Initialize atomic_flag
objects using the ATOMIC_FLAG_INIT
macro.
A simple spinlock implementation using atomic_flag
as the “spin value”:
|
|
|
|
# Atomic variables
An atomic variable is a variable which is declared as an atomic type. Atomic variables are meant to be used with the atomic operations which operate on the values held by these variables (except for the flag operations, ie. atomic_flag_test_and_set
). Unlike atomic_flag
, these are not guaranteed to be lock-free. Initialize atomic variables using the ATOMIC_VAR_INIT
macro or atomic_init
if it has been default-constructed already.
The C11 Atomics library provides many additional atomic operations, memory fences, and allows specifying memory orderings for atomic operations.
|
|
|
|
# <threads.h>
C11 provides an OS-agnostic thread library supporting thread creation, mutexes, and condition variables. Threading library is located in <threads.h>
. As of September 2023, it is poorly supported in most major compilers.
# Threads
Creates a thread and executes print_n
in the new thread.
|
|
# Mutexes
C11 provides mutexes that support:
- Timed locking - Given a
timespec
object, blocks the current thread until the mutex is locked or until it times out. - Recursive locking - Supports recursive locking (e.g. re-locking in recursive functions). Recursive timed locks can also be created, capturing both these operations.
Specify which type of mutex to be created by passing any of mtx_plain
, mtx_timed
, mtx_recursive
, or any combination of these to mtx_init
’s type
parameter.
|
|
Mutexes must be cleaned up by calling mtx_destroy
.
# Condition variables
C11 provides condition variables as part of its concurrency library. Condition variables will block when waiting, support timed-waiting, and can be signalled and broadcasted. Note that spurious wakeups may occur.
Condition variables must be cleaned up by calling cnd_destroy
.
|
|
# Quick exiting
Causes program termination to occur where clean termination is not possible or impractical; for example if cooperative cancellation between different threads is not possible or cancellation order is unachievable. This would have resulted in some threads trying to access static-duration objects while they are/have been destructed.
Quick exiting allows applications to register handlers to be called on exit while being able to access static duration objects (unlike atexit
). Quick exiting does not execute static-duration object destructors in order to achieve this.
Therefore, quick exiting can be thought of as being “in between” choosing to normally terminate a program, and abnormally terminating using abort
.
|
|
# Exclusive mode file opening
File access mode flag x
can optionally be appended to w
or w+
specifiers. This flag forces the function to fail if the file exists, instead of overwriting it.
|
|
# COPYING & ATTRIBUTIONS:
Author Anthony Calandra
License: The MIT License (MIT) Copyright (c) 2017 Anthony Calandra
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.