2.10. Keywords

The c29clang compiler supports C and C++ language keywords defined in the relevant language standards. You can find information about these keywords in an up-to-date version of the C and C++ language standards. The C++ reference web site is also a very useful resource for information about elements of the C and C++ programming languages.

2.10.1. const Keyword

The const keyword is part of the C standard. The c29clang compiler supports this keyword in all language modes (see C/C++ Language Options),

This keyword gives you greater optimization and control over allocation for certain data objects. You can apply the const qualifier to the definition of any variable or array to ensure that its value is not altered.

Global objects qualified as const are placed in the .rodata section. The linker allocates the .rodata section from ROM or FLASH, which are typically more plentiful than RAM. The const data storage allocation rule has the following exceptions:

  • If the object has automatic storage (function scope).

  • If the object is a C++ object with a “mutable” member.

  • If the object is initialized with a value that is not known at compile time (such as the value of another variable).

In these cases, the storage for the object is the same as if the const keyword were not used.

The placement of the const keyword is important. For example, the first statement below defines a constant pointer p to a modifiable int. The second statement defines a modifiable pointer q to a constant int:

int * const p = &x;
const int * q = &x;

Using the const keyword, you can define large constant tables and allocate them into system ROM. For example, to allocate a ROM table, you could use the following definition:

const int digits[] = {0,1,2,3,4,5,6,7,8,9};

2.10.2. inline Keyword

The inline keyword is part of the C standard beginning with C99. The c29clang compiler supports inlining on a per-function basis in all language modes (see C/C++ Language Options). However, if you are using -std=c89, which requires the compiler to follow the C89 standard strictly, use the __inline keyword instead.

The compiler inlines a function only if it is legal to do so. Functions are never inlined if the compiler is invoked with the -O0 option or the -fno-inline-functions option.

A function may be inlined even if the function is not declared with the inline keyword. The c29clang compiler inlines functions with the inline keyword and some library functions when the -O1 optimization option is used. It performs additional inlining when the -O2 option is used and aggressive inlining when the -O3 option is used. A function mayn be inlined even if the compiler is not invoked with any -O command-line option. The -Og and -Os options reduce inlining.

2.10.3. restrict Keyword

The restrict keyword is part of the C standard beginning with C99. The c29clang compiler supports specifying restricted access to pointers, references, and arrays in all language modes (see C/C++ Language Options). However, if you are using -std=c89, which requires the compiler to follow the C89 standard strictly, use the __restrict keyword instead.

To help the compiler determine memory dependencies, you can qualify a pointer, reference, or array with the restrict keyword. The restrict keyword is a type qualifier that can be applied to pointers, references, and arrays. Its use represents a guarantee by you, the programmer, that within the scope of the pointer declaration the object pointed to can be accessed only by that pointer. Any violation of this guarantee renders the program undefined. This practice helps the compiler optimize certain sections of code because aliasing information can be more easily determined.

The following example uses the restrict keyword to tell the compiler that the function func1 is never called with the pointers a and b pointing to objects that overlap in memory. You are promising that accesses through a and b will never conflict; therefore, a write through one pointer cannot affect a read from any other pointers. The precise semantics of the restrict keyword are described in the 1999 version of the ANSI/ISO C Standard.

void func1(int * restrict a, int * restrict b)
{
    /* func1's code here */
}

The following example uses the restrict keyword when passing arrays to a function. Here, the arrays c and d must not overlap, nor may c and d point to the same array.

void func2(int c[restrict], int d[restrict])
{
    int i;
    for(i = 0; i < 64; i++)
    {
        c[i] += d[i];
        d[i] += 1;
    }
}

2.10.4. volatile Keyword

The volatile keyword is part of the C standard. The c29clang compiler supports this keyword in all language modes (see C/C++ Language Options).

The volatile keyword indicates to the compiler that there is something about how the variable is accessed that requires that the compiler not use overly-clever optimization on expressions involving that variable. For example, the variable may also be accessed by an external program, an interrupt, another thread, or a peripheral device.

The compiler eliminates redundant memory accesses whenever possible, using data flow analysis to figure out when it is legal. However, some memory accesses may be special in some way that the compiler cannot see, and in such cases you should use the volatile keyword to prevent the compiler from optimizing away something important. The compiler does not optimize out any accesses to variables declared volatile. The number of volatile reads and writes will be exactly as they appear in the C/C++ code, no more and no less and in the same order.

Any variable that might be modified by something external to the obvious control flow of the program (such as an interrupt service routine) must be declared volatile. This tells the compiler that an interrupt function might modify the value at any time, so the compiler should not perform optimizations which will change the number or order of accesses of that variable. This is the primary purpose of the volatile keyword. In the following example, the loop intends to wait for a location to be read as 0xFF:

unsigned int *ctrl;
while (*ctrl !=0xFF);

However, in this example, *ctrl is a loop-invariant expression, so the loop is optimized down to a single-memory read. To get the desired result, define ctrl as:

volatile unsigned int *ctrl;

Here the *ctrl pointer is intended to reference a hardware location, such as an interrupt flag.

The volatile keyword must also be used when accessing memory locations that represent memory-mapped peripheral devices. Such memory locations might change value in ways that the compiler cannot predict. These locations might change if accessed, or when some other memory location is accessed, or when some signal occurs.

Volatile must also be used for local variables in a function that calls setjmp, if the value of the local variables needs to remain valid if a longjmp occurs.

#include <stdlib.h>
jmp_buf context;

void function()
{
    volatile int x = 3;
    switch(setjmp(context))
    {
        case 0: setup(); break;
        default:
        {
            /* We only reach here if longjmp occurs. Because x's lifetime begins before setjmp
            and lasts through longjmp, the C standard requires x be declared "volatile". */
            printf("x == %d\n", x);
            break;
        }
    }
}