Lesser known C features

There are many C features which are lesser known even to experienced programmers.

Array of a variable size

Do not be confused with dynamic arrays. C99 supports arrays of variable length where the size is calculated at run-time while processing the array definition. This is a back-port from C++ and far better than the alloca function. Have a look at the example below.

int fo( int i)
{
	return i+1;
}

void f( int len, char s[ len][ fo( len+1)*2])
{
	for (int i=1; i<10; i++)
	{
		char s1[ i+len];
		...
	}
}

It is not possible to skip the definition using break in switch statement.

Flexible array as a member of a structure

Arrays with unspecified size are used in structures to help addressing data past the structure end.

struct s {
	int i;
	unsigned u;
	char end[];
};

There are several restrictions. The flexible array member must be the last element of the structure and the structure itself must be neither used in another structure definition nor as a member of an array.

Structure field addressing in definitions

In old C the fields of a structure have to be in fixed order during initialization. It is a well-known GNU extension that specifies structure fields with the label-like syntax. C99 has a new approach using the . operator. Although it looks strange, it has a hidden meaning (see below).

struct fops {
	int open, read, write, close;
};
{
	// old span
	struct fops f1 = { open: 0, close: 1, read: 2};
	// new span
	struct fops f2 = { .open=0, .close=1, .read=2}
	};
}

Array initialization

It is not very well known that an array initiator may jump in index like in the enumeration definition.

// initializing an array of int
int a[ 7] = { [5]=1, [2]=3, 2};
// resulting in
int a[ 7] = { 0, 0, 3, 2, 0, 1, 0};

// initializing an array of struct
struct { int x,y; } ar[ 4] = { [1].x=23, [3].y=34, [1].y=-1, [1].x=12};
// resulting in
struct { int x,y; } ar[ 4] = { { 0, 0}, { 12, -1}, { 0, 0}, { 0, 34}};

// interesting usage
char forbidden[ 256] = { ['a']=1, ['e']=1, ['i']=1, ['o']=1, ['u']=1};

Note that already initialized fields may be overwritten.

Compound literals

Compounds literals bring a new method of assigning to structures and passing structures as parameters.

struct point {
	int x, y;
};
void foo( struct point p1, struct point p2);

{
	struct point p1 = { 2, 4}; // this is standard
	p1 = (struct point){ 1, 3}; // this is new

	// passing to the function
	foo( (struct point){ 10, 11}, (struct point){ 1, 2});

	// constructing an array
	char **sx = (char *[]){ "Adam", "Eva", "Simon"};
}

Inline functions

Yes, C has inline functions. Like in C++ prepend the function header with the inline keyword (__inline__ in headers). Note that certain constructions may disallow the compiler to inline the function. Especially constructions implemented on stack like variadic parameters, alloca or variable sized arrays.

Volatile type qualifier

Have a look at the example below - there are two functions each executed in a separate thread with the same pointer as a parameter. Do you think that the check function must terminate?

void add( int *i)
{
	while (1)
	{
		*i = 0;
		for (int a=0; a<10; a++)
			(*i)++;
	}
}

void check( int *i)
{
	while (*i == 5) ;
}

No, it doesn't have to. The incrementation may by optimized. Below is a disassembled code produced by GCC -O1:

<add+00>	push	ebp
<add+01>	mov	ebp,esp
<add+03>	mov	ecx,DWORD PTR [ebp+8]
<add+06>	mov	DWORD PTR [ecx],0x0
<add+12>	mov	eax,0x0
<add+17>	mov	edx,0x0
<add+22>	inc	edx
<add+23>	inc	eax
<add+24>	cmp	eax,0x9
<add+27>	jle	<add+22>
<add+29>	mov	DWORD PTR [ecx],edx
<add+31>	jmp	<add+6>

As we can see, *i has been replaced by the edx register, which is written only at the beginning and at the end of the cycle. The value of *i switches between 0 and 10, and never becomes 5.

Marking the pointer as volatile will solve the problem. The semantics is that a volatile object may change its value outside the scope of local execution and thus every read and write access has to be processed immediately without any optimizations. Focus on this problem when you observe different behavior across different optimization levels. It is not guaranteed that a read or write access even on volatile objects are atomic.

Restricted pointers

The freedom of pointers in C leads sometimes to slower code. The programmer can enable several optimizations by guarantying that objects referred by pointers do not overlap.

void copy1( char *s1, char *s2, int n)
{
	while (n--)
		*s1++ = *s2++;
}

void copy2( char restrict *s1, char restrict *s2, int n)
{
	while (n--)
		*s1++ = *s2++;
}

Both functions copy one block of chars into another. In copy2 the compiler may use word addressing instructions to speedup the execution. But when blocks overlap the behavior is undefined.

Macros with variable number of parameters

The syntax is similar to functions. Parameters in ... are then addressed as __VA_ARGS__ (variable argument).

#define myfunc( A, B, ...)	do_something( 0, B, A, __VA_ARGS__);

Some predefined identifiers

Trigraphs

This may looks like a joke, but it is not. All occurrences of sequences ??<, ??>, ??(, ??), ??=, ??/, ??!, ??', ??- in a source file are converted to one of characters { } [ ] # / | ^ ~. So don't be surprised...

As an aside, tokens <: :> <% %> %: behave as [ ] { } # and ## (but the conversion is not performed in strings).

Types with defined size

Have a look at the header file stdint.h. There are typedefined types like (where N is in { 8, 16, 32, 64})

Unsigned variants have prefix "u".

Be careful when using printf functions. Since you do not know which C type are behind these typedefs, you have to use predefined constants from inttypes.h. Constants starts with PRI followed by type character (one of diouxX), modificator LEAST or FAST or nothing, and number of bits. For example, 32 bit fast integer would take the form PRIdFAST32.

Boolean type

C still does not have a boolean type, but reserves an integer type _Bool big enough to store 0 and 1. The header file stdbool.h only typedefines _Bool as a bool and defines constants false as 0 and true as 1.

This is not C++ bool!

Complex numbers

C has three complex types: {float, double, long double} _Complex. In the header file complex.h _Complex is typedefined to complex. Complex types may be not implemented in freestanding (without OS) implementations.