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