C Language Tips for Embedded Development

The C language often makes people feel that its capabilities are very limited. It does not have advanced features like first-class functions and pattern matching. However, C is very simple, and there are still some very useful syntax tricks and features that not many people know about.

1 Designated Initialization

Many people know how to statically initialize an array like this:

int fibs[] = {1, 1, 2, 3, 5};

The C99 standard actually supports a more intuitive and simple way to initialize various collection types (such as structures, unions, and arrays).2 Arrays
We can specify the elements of an array for initialization. This is very useful, especially when we need to keep a certain mapping relationship synchronized based on a set of #define definitions. Let’s look at a set of error code definitions, such as:

/* Entries may not correspond to actual numbers. Some entries omitted. */
#define EINVAL 1
#define ENOMEM 2
#define EFAULT 3
/* ... */
#define E2BIG 7
#define EBUSY 8
/* ... */
#define ECHILD 12
/* ... */

Now, suppose we want to provide a string description for each error code. To ensure that the array stays updated with the latest definitions, regardless of any modifications or additions made to the header file, we can use this designated syntax for the array.

char *err_strings[] = {[0] = "Success",[EINVAL] = "Invalid argument",[ENOMEM] = "Not enough memory",[EFAULT] = "Bad address",/* ... */[E2BIG ] = "Argument list too long",[EBUSY ] = "Device or resource busy",/* ... */[ECHILD] = "No child processes"/* ... */};

This allows for static allocation of sufficient space, ensuring that the maximum index is valid while initializing special indices to specified values and the remaining indices to 0.3 Structures and Unions
Initializing data using the field names of structures and unions is very useful. Suppose we define:

struct point {  int x;  int y;  int z;}

Then, we initialize the struct point like this:

struct point p = {.x = 3, .y = 4, .z = 5};

This approach allows for easy generation of the structure at compile time without needing to call a specific initialization function when we do not want to initialize all fields to 0. For unions, we can use the same method, but we only initialize one field.4 Macro Lists
A common idiom in C is to have a named list of entities for which functions need to be created, initializing each of them and expanding their names in different code modules. This is often used in Mozilla’s source code, and I learned this trick while working there. For example, in a project I worked on last summer, we had a macro list for flags for each command. It works as follows:

#define FLAG_LIST(_) \ 
_(InWorklist) \ 
_(EmittedAtUses) \ 
_(LoopInvariant) \ 
_(Commutative) \ 
_(Movable) \ 
_(Lowered) \ 
_(Guard)

It defines a FLAG_LIST macro that takes a parameter called _, which itself is a macro that can call each parameter in the list. A practical example might illustrate this better. Suppose we define a macro DEFINE_FLAG, like:

#define DEFINE_FLAG(flag) flag,
enum Flag {  None = 0,  FLAG_LIST(DEFINE_FLAG)  Total};#undef DEFINE_FLAG

Expanding FLAG_LIST(DEFINE_FLAG) would yield the following code:

enum Flag {  None = 0,  DEFINE_FLAG(InWorklist)  DEFINE_FLAG(EmittedAtUses)  DEFINE_FLAG(LoopInvariant)  DEFINE_FLAG(Commutative)  DEFINE_FLAG(Movable)  DEFINE_FLAG(Lowered)  DEFINE_FLAG(Guard)  Total};

Then, expanding each parameter with the DEFINE_FLAG macro gives us the following enum:

enum Flag {    None = 0,    InWorklist,    EmittedAtUses,    LoopInvariant,    Commutative,    Movable,    Lowered,    Guard,    Total};

Next, we might want to define some accessor functions to better utilize the flag list:

#define FLAG_ACCESSOR(flag) \ 
bool is##flag() const {\ 
    return hasFlags(1 << flag);\ 
}\ 
void set##flag() {\ 
    JS_ASSERT(!hasFlags(1 << flag));\ 
    setFlags(1 << flag);\ 
}\ 
void setNot##flag() {\ 
    JS_ASSERT(hasFlags(1 << flag));\ 
    removeFlags(1 << flag);\ 
}FLAG_LIST(FLAG_ACCESSOR)#undef FLAG_ACCESSOR

Step-by-step demonstration of the process is very enlightening, and if there are any uncertainties about its usage, one can spend some time on gcc -E.5 Compile-Time Assertions
This is actually a very “creative” feature implemented using macros in the C language. Sometimes, especially in kernel programming, it is very useful to perform assertions at compile time rather than at runtime. Unfortunately, the C99 standard does not support any compile-time assertions. However, we can use preprocessing to generate code that will only compile if certain conditions are met (preferably commands that do not perform actual functions). There are various ways to achieve this, typically by creating an array or structure of negative size. The most common way is as follows:

/* Force a compilation error if condition is false, but also produce a result* (of value 0 and type size_t), so it can be used e.g. in a structure* initializer (or wherever else comma expressions aren't permitted). *//* Linux calls these BUILD_BUG_ON_ZERO/_NULL, which is rather misleading. */#define STATIC_ZERO_ASSERT(condition) (sizeof(struct { int:-!(condition); }) )#define STATIC_NULL_ASSERT(condition) ((void *)STATIC_ZERO_ASSERT(condition) )/* Force a compilation error if condition is false */#define STATIC_ASSERT(condition) ((void)STATIC_ZERO_ASSERT(condition))

If the result of (condition) evaluates to a non-zero value (i.e., true in C), then ! (condition) is zero, and the code will compile successfully, generating a structure of zero size. If (condition) results in 0 (false in C), then a compilation error will occur when trying to generate a structure of negative size. Its usage is very simple; if any hypothetical condition can be checked statically, then it can assert at compile time. For example, in the previously mentioned flag list, the type of the flag set is uint32_t, so we can make the following assertion:

STATIC_ASSERT(Total <= 32)

It expands to:

(void)sizeof(struct { int:-!(Total <= 32) })

Now, suppose Total <= 32. Then -!(Total <= 32) equals 0, so this line of code is equivalent to:

(void)sizeof(struct { int: 0 })

This is valid C code. Now suppose there are more than 32 flags, then -!(Total <= 32) equals -1, so this code would be equivalent to:

(void)sizeof(struct { int: -1 } )

Since the bit width is negative, it can be determined that if the number of flags exceeds the space we have allocated, then the compilation will fail.

Leave a Comment