2024-03-29
.
A brief reference for the differences between C standards. Source
Anything before standardisation was called K&R C as per the first edition of the book that was the de facto implementation at the time. The book came out at 1978, so sometimes this implementation is also referred to as C78.
The following language features were introduced by the book:
long int
data type.unsigned int
data type.a =+ 1;
has become a += 1;
. This happened to remove ambiguity. Before, a=-10;
meant a =- 10;
instead of a = -10;
C functions that returned an int
could be declared without the int
keyword
upfront as it was implicit that they returned int.
/* K&R */
long function_that_returns_long();
function_that_returns_int();
/* newer standards */
long function_that_returns_long();
int function_that_returns_int();
Function declarations also had an implicit int
return when the return type
was not specified:
/* K&R */
function_that_returns_int() {
return 10;
}
/* newer standards */
int function_that_returns_int() {
return 10;
}
Function declarations also didn't include information about function arguments. This means that function parameter type checks were not performed. Some compilers would still raise a warning if the function was called with the wrong number of arguments or if different calls to the same function had different numbers of arguments.
After the book publication and before C89, several compilers added other features to the language:
void
functions.struct
and union
types. Prior to that only a pointer
(int or float) could be returned.struct
data types.enum
types. Before that, preprocessors were used like #define BLUE 0
.The goal of this standards was to create a superset of K&R out of the many unofficial features, such as:
/* before (no argument info in declarations) */
long function_that_returns_long();
/* C89 */
long function_that_returns_long(int a, int b);
void
pointers.const
qualifier.#error
and #pragma
directives.The following functionalities were introduced in this new version of the standard:
inline
functions. Those are used to suggest (but not mandate) the compiler
to inline the body of the function where it is called by performing inline
expansion.inline void swap(int *m, int* n) {
int tmp = *m;
*m = *n;
*n = tmp;
}
struct point p = { .x = 1, .y = 2 };
my_sweet_function_call((struct x) {1, 2});
long long int
and complex
.float read_and_process(int n)
{
float vals[n];
for (int i = 0; i < n; ++i)
vals[i] = read_val();
return process(n, vals);
}
static
in array indices in parameter
declarations was added. The below indicates that the array size is determined
by the params rows
and cols
:void function(int rows, int cols, int array[static rows][cols]) {
// Function implementation
}
struct vectord {
short len; // there must be at least one other data member
double arr[]; // the flexible array member must be last.
// The compiler may reserve extra padding space here, like it can between struct members
};
//
.\u0040
.restrict
qualification allows more aggressive code optimization, removing
compile-time array access advantages previously held by FORTRAN over ANSI C.
This is used to tell the compiler two pointers don't point to the same
address in memory. With that, the compiler can aggressively optimise the code
as it knows pointers won't be aliased (overlapped in memory).void update(int *restrict a, int *restrict b, int n) {
// Using 'restrict' keyword to indicate no aliasing
for (int i = 0; i < n; i++) {
a[i] += b[i];
}
}
_Noreturn
this specifier informs the compiler that once the function is
called, it will never return control to the calling function. The
<stdnoreturn.h> header provides the _Noreturn
keyword for compilers that
don't support the C11 standard but still want to use this feature._Alignas
specifier, _Alignof
operator, aligned_alloc
function, <stdalign.h>). These can be helpful when optimising performance in
SIMD (Single Instruction, Multiple Data) programming or dealing with
hardware-specific requirements.// Align the array to a 16-byte boundary.
_Alignas(16) char aligned_array[32];
// Get the alignment requirement of a data type.
printf("Alignment of int: %zu\n", _Alignof(int));
// Allocate 32 bytes of memory aligned to a 16-byte boundary
void *ptr = aligned_alloc(16, 32);
// <stdalign.h> header provides similar functionality:
alignas(16) char aligned_array[32];
printf("Alignment of aligned_array: %zu\n", alignof(aligned_array));
_Generic
keyword.#define print_value(x) _Generic((x), \
int: printf("%d\n", x), \
float: printf("%f\n", x), \
double: printf("%lf\n", x), \
default: printf("Unsupported type\n") \
)
_Thread_local
storage-class specifier and
<threads.h> header.memcpy_s
, memmove_s
, strcpy_s
, strncpy_s
, and others with the _s
suffix, which perform bounds-checking for array accesses. These functions
typically take additional parameters specifying the size of the destination
buffer to ensure that the copy operation does not exceed the buffer size.
These changes remain controversial and they haven't been widely implemented._Static_assert
keyword,
allowing programmers to embed compile-time assertions directly into their
code. There is also the library <assert.h> to that end, with the function
static_assert
.struct T { int tag; union { float x; int n; }; };
quick_exit
function as a third way to terminate a program, intended to
do at least minimal deinitialisation.real + imaginary*I
might not yield the expected value if imaginary is infinite or
NaN).C17 fixes numerous minor defects in C11 without introducing new language features.
Embedded C is a set of language extensions for the C programming language by the C Standards Committee to address commonality issues that exist between C extensions for different embedded systems.
These include:
Many embedded processors lack an FPU, because integer arithmetic units require substantially fewer logic gates and consume much smaller chip area than an FPU