ANSI C99 Extensions in LabWindows/CVI

Contents

Introduction   

LabWindows/CVI 9.0 introduces support for a number of useful additions to the C language as specified in ISO/IEC 9899:1999 standard. LabWindows/CVI is not a fully conforming C99 compiler, but rather supports some C99 features as extensions to the ANSI C language.

LabWindows/CVI supports the following C99 extensions:

  • Mixed declarations and statements
  • for loop variable declarations
  • Variable-length arrays
  • Designated initializers
  • Variable argument macros
  • Non-constant initializers for aggregate types
  • __func__ identifier
  • inline and restrict keyword recognition
  • atoll, snprintf, strtoll, strtoull, and vsnprintf functions
  • long long integers

 

Portability

Because older versions of LabWindows/CVI and most other commercially available C compilers do not support C99, code that uses these features may have portability issues. LabWindows/CVI allows you to disable C99 extension support on a per-project or per-file basis for when portability is a concern. When you create a new project in LabWindows/CVI 9.0 or later, C99 extensions are enabled by default. Existing projects must have this option explicitly enabled. Enable and disable C99 extensions using the Build with C99 extensions option in the Build Options dialog box.

Image: Build with C99 extensions option on the Build Options dialog.

You can override this project-wide setting on a per-file basis by using the following pragmas in t source code:

  • #pragma C99_extensions_off
  • #pragma C99_extensions_on

The pragmas affect the compilation of all code that follows in that module. Use the C99_extensions_off pragma to disable C99 extensions and enforce the portability of a particular file.

 

Mixed Declarations and Statements

Whereas ANSI C89 requires all variables be declared at the top of a function or local block, C99 allows a declaration to appear anywhere within the block. The scope of local variables extend from their declaration to the end of the block in which they are defined.

foo();
int bar;
foo();

For Loop Variable Declarations

You also can declare variables in the initialization section of a for loop. The scope of local variables defined in the for loop extend from their declaration to the end of the for loop in which they are defined.

for (int i = 0; i < 10; ++i)
      printf("%d", i);

 

Variable-Length Arrays

Whereas ANSI C requires local array declarations to have constant size expressions, C99 allows arbitrary size expressions:

void foo (int n)
{
      int bar[n+1];
      ...

}

For an array with a non-constant size expression, the number of elements is evaluated and stack storage is allocated at run time, when the declaration comes into scope. Like any other local variable, the storage is automatically deallocated when the object goes out of scope. For small to moderately large, temporary allocations, you can use variable-length arrays as an alternative to malloc. This helps avoid memory leaks by moving the deallocation burden from the programmer to the compiler. By default, LabWindows/CVI programs reserve a stack of 250,000 bytes (roughly 30,000 doubles), so an allocation on that scale is inappropriate as a variable-length array declaration since it may cause a stack overflow. The heap can tolerate much larger allocations.

There are some behavioral considerations and restrictions for variable-length arrays that are a direct result of their run-time allocation. Because the size can only be known at run time, you cannot declare global or static variable-length arrays. Declarations of variable-length arrays cannot have initializers. If you use the sizeof operator with a variable-length array, the operator evaluates only at run time when the length of the array is known. If the size expression of a variable-length array evaluates to a non-positive number at run time, the program generates a fatal run-time error. You can use a goto statement to jump within the scope of a variable-length array, but you cannot jump past a declaration of an object of variably modified type.

 

Designated Initializers

ANSI C requires that an initializer list for an array, struct, or union, begin with the first member of the aggregate, and proceed on in sequential order, without skipping any members. C99 provides a special “designator” syntax that allows the initialization of specific array, struct, or union members.

For structs or unions, to initialize a member x, use the designator .x= followed by the initial value:

struct S1
{
      int a;
      float b;
      char c[3];
} foo = { .b=0.5, .a=1, .c="hi" };

To initialize an array member at index i, use the designator [i]= followed by the initial value:

float bar[100][100] = { [99][99] = 1.0, [1][1] = 1.0 };

Initialize members in any order you wish, and avoid explicitly initializing members you do not care about. Any members not explicitly initialized are implicitly initialized with 0. To this end, designated initializers are handy for static initialization of sparse arrays or matrices.

Designators may be composed to initialize members of more complex data structures:

struct S2
{
      double d[2];
} baz[2] = { [1].d[0]=0.5, [0].d[1]=0.6 };

results in: { { { 0.0, 0.6 } }, { { 0.5, 0.0 } } }

Designated initializers may be interspersed within standard initialization lists. Any non-designated initializer initializes the next sequential member in the aggregate:

struct S1
{
      int a;
      float b;
      char c[3];
} foo = { .c=’h’, ‘i' /* foo.c[1] */, .a=1, 2.1 /* foo.b */ };

 

Variable Argument Macros

In C99, macro declarations can accept a variable number of arguments when the last member of the argument declaration list is an ellipsis (...). Use a variable argument list if the number and type of arguments in the function might vary:

#define debug_printf (format, ...) fprintf (stderr, format, __VA_ARGS__);

To access the arguments in the list, you must specify at least one fixed argument. Use the __VA_ARGS__ macro to access the variable argument list. You also can use the va_list, va_start, va_arg, and va_end macros in the stdarg.h file located in the cvi\include\ansi directory to work with variable argument lists.

 

Non-Constant Initializers for Aggregate Types

You can initialize the elements of an aggregate (array, struct, or union) using an expression instead of a constant only:

foo (float a, float b)
{
      float freqs[2] = { a-b, a+b };
}

 

__func__ Identifier

You can use the __func__ predefined identifier to access the name of the function in which it is used. This identifier behaves as a static string variable.

void foo (void)
{
      printf ("In function %s\n", __func__); /* prints "In function foo." */
}

 

restrict and inline Keyword Recognition

LabWindows/CVI recognizes the restrict and inline keywords from the C99 specification.

The restrict pointer type qualifier has no effect on the code generated by the compiler, but syntactically correct usage of the keyword is accepted and produces no compile errors.

LabWindows/CVI does not support code inlining. However, syntactically correct usage of the inline function specifier is accepted and results in semantically correct behavior. If all declarations of a function within a module specify the inline keyword and do not specify the extern keyword, LabWindows/CVI handles the definition of the function as a static definition.

 

Additional Resources

Download LabWindows/CVI Evaluation Software

Subscribe to the LabWindows/CVI Newsletter