Sunday, December 18, 2022

C11 (C standard revision) features

C11 revision

1 Overview

C11 is the informal name for ISO/IEC 9899:2011, the current standard for the C language that was ratified by ISO in December 2011.

It standardizes many features for safer programming, such as removal of gets() function.

Additionally, C11 adds concurrency support, type-generic expressions, alignment-related facilities, static assertion, unicode support, floating-point characteristic macros, no-return functions, anonymous structures and unions, and various bound-checking and reentrancy functions.

The following examples demonstrating C11 features can be compiled with options such as

clang -std=c11 ex.c

2 Multiple threading

C11 standardized multi-threading support.

It introduces new headers threads.h and stdatomic.h, supporting multiple threads of execution:

  • threads creation and management
  • mutexes
  • conditional variables
  • atomic objects
  • thread specific storage


The following example

#include <stdio.h>
#include <threads.h>

#define NUM_THRD 5
int th_data[NUM_THRD];

int th_func(void *data)
{
        printf("hi from thread %d\n", *(int *)data);
        thrd_sleep(&(struct timespec){ .tv_sec = 2 }, NULL);
        printf("bye thread %d\n", *(int *)data);
        return 0;
}

int main(void)
{
        thrd_t t[NUM_THRD];

        for (int i = 0; i < NUM_THRD; ++i) {
                th_data[i] = i;
                thrd_create(t + i, th_func, th_data + i);
                printf("%d-th thread created\n", i);
        }

        for (int i = 0; i < NUM_THRD; ++i) {
                thrd_join(t[i], NULL);
        }
        return 0;
}

generates output similar to the following:

0-th thread created
hi from thread 0
1-th thread created
2-th thread created
hi from thread 2
hi from thread 1
3-th thread created
4-th thread created
hi from thread 3
hi from thread 4
bye thread 0
bye thread 3
bye thread 1
bye thread 2
bye thread 4

3 Generic selection

Generic selection is a type-generic expressions using the _Generic keyword.

It allows to define type-generic macros or functions similar to the idea of overloading in C++.

The following macro typename(x) gives a string depending on the type name of x:

#include <stdio.h>

#define typename(x) _Generic((x),                       \
                                int: "int",             \
                                char: "char",           \
                                double: "double",       \
                                default: "other")

int main(void)
{
        printf("9 %s\n", typename(9));
        printf("3.6 is %s\n", typename(3.6));
        return 0;
}

4 Alignment facilities

C11 introduces facilities to manually control memory alignment.

_Alignas keyword specifies a custom alignment for a variable or user-defined type and _Alignof reports the alignment of its operand.

The macros alignas and alignof are defined directly mapped to _Alignas and _Alignof, which match keywords used in C++.

For the following example

#include <stdio.h>
#include <stdalign.h>

typedef struct {
        char c;
        char data[16];
} s0;

typedef struct {
        char c;
        alignas(16) char data[16];
} s1;

int main(void)
{
        printf("sizeof(s0): %zu, alignof(s0): %zu\n", sizeof(s0), alignof(s0));
        printf("sizeof(s1): %zu, alignof(s1): %zu\n", sizeof(s1), alignof(s1));
        return 0;
}

The output will be

sizeof(s0): 17, alignof(s0): 1 sizeof(s1): 32, alignof(s1): 16

Every object in s1 is aligned to 16. So, sizeof(s0) = 32 bytes = 1 byte char + 15 bytes padding + 16 bytes data.

5 Static assertions

With macro static_assert, an expression can be evaluated at translation phase, when its type is known. So, compared to #if and #error preprocessor directives, it can detect errors that are not possible to detect during preprocessing phase.

In effect if the expression is evaluated equal to zero, a compile-time error occurs. Otherwise, if is not equal to zero, no code is emitted.

#include <assert.h>

int main(void)
{
        static_assert(2 + 2 == 4,
                "require to have 2 + 2 = 4");

        //this produces a compile time error
        static_assert(sizeof(int) < sizeof(char),
                "require to have int size < char size");
        return 0;
}

6 Anonymous structs and unions

An anonymous struct or union has no name - neither tag name nor typedef name. It is useful for nesting aggregates.

A typical usage is to provide an alternative view to data.

#include <stdio.h>

typedef union {
        struct {
                int width;
                int height;
        };
        struct {
                int x;
                int y;
        };
} vec_t;

int main(void)
{
        vec_t point;
        point.x = 6;
        point.y = 9;
        printf("point.x = %d, point.y = %d\n",
                point.x, point.y);

        vec_t *rectangle = &point;
        printf("rectangle->width = %d, rectangle->height = %d\n",
                rectangle->width, rectangle->height);
        return 0;
}

In the example, variable point represents a point on a coordinate system. Variable rectangle represents the rectangular area within point and origin, being the right-top and the left-bottom corner separately.

With C99, we have to name the structures, and the access to values of x and y will be instead something like point.s_data.x, that is verbose and less readable.

7 Other features

7.1 quick_exit() function

exit() ensures that stream buffers are flushed, closed, etc. However, quick_exit() only does minimal cleanup and exits a program quickly.

#include <stdlib.h>
#include <stdio.h>

void fquick(void)
{
        puts("quick exit");
}

void fnormal(void)
{
        puts("normal termination");
}

int main(void)
{
        at_quick_exit(fquick);
        atexit(fnormal);
        quick_exit(EXIT_SUCCESS);
        puts("end of main");
        return 0;
}

The example outputs "quick exit" only.

7.2 _Noreturn function specifier

It declares a function does not return. _Noreturn specifier is to suppress compiler warnings on a non returning function, and enable certain optimizations that are only allowed on non returning functions.

7.3 Bounds-checking functions

It defines bound checking version of functions having _s suffix appended to the original function names, strcat_s() etc.

7.4 Unicode support

Prefixes u and U introduce UTF-16 and UTF-32 characters and strings.

UTF-8 encoding strings can be enforced with prefix u8.

#include <uchar.h>

char u8str[] = u8"UTF-8 string π";
char u8chr = u8'π';

char16_t u16str[] = u"UTF-16 string π";
char16_t u16char = u'π';

char32_t u32str[] = U"UTF-32 string π";
char32_t u32char = U'π';

7.5 complex types

#include <stdio.h>
#include <complex.h>

int main(void)
{
        double complex z = CMPLX(6.0, 9.0);
        printf("Value: %f-%fi\n", creal(z), cimag(z));
        return 0;
}

8 C17 / C18

Since it was prepared in 2017 and published in 2018, C17 is also referred to as C18.

C17 addressed defects in C11 without introducing new language features.

9 C2x

C2x is an informal name. It is expected to be voted on in 2023, and will be C23.

Refer to wikipedia/C2x for more information.

Author: TPECWL2089XD

Created: 2022-12-19 Mon 13:46

Validate

No comments: