Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Traditionally, C arrays are declared with an index that is either a fixed constant , or empty. An array with a fixed constant index indicates to the compiler how much space to reserve for the array. An array declaration with an empty index is considered an incomplete type , and indicates that the variable indicates references a pointer to an array of indeterminite indeterminate size.

The C standard, since 1999 has permitted array term conformant array parameter comes from Pascal; it refers to a function argument that is an array whose size is specified in the function declaration. Since C99, C has supported conformant array parameters by permitting array parameter declarations to use extended syntax. The most well-known extension is for variable-length arrays (VLAs). In this case, the array index is a variable, and the size of the array is determined at run-time, rather than compile-time.Section Subclause 6.7.6.12, paragraph 1, of C11 [ISO/IEC 9899:2011] summarizes the array index syntax extensions:

In addition to optional type qualifiers and the keyword static, the The [ and ] may delimit
an delimit an expression or *. If they delimit an expression (which specifies the size of an array), the
the  expression shall have an integer type. If the expression is a constant expression, it shall
have  have a value greater than zero. The element type shall not be an incomplete or function
type. The optional type qualifiers and the keyword static shall appear only in a
declaration of a function parameter with an array type, and then only in the outermost
array type derivation.

Consequently, an array declaration that serves as a function argument may have an index that is a variable or an expression. The array argument is demoted to a pointer and is consequently not a variable length array (VLA). Conformant array parameters can be used by developers to indicate the expected bounds of the array. This information may be used by compilers, or it may be ignored. However, such declarations are useful to developers because they serve to document relationships between array sizes and pointers. This information can also be used by static analysis tools to diagnose potential defects.By the way, one thing that came up for me last week was that we probably ought to mention conformant array parameters. For example,

Code Block
int f(size_t n, int a[n])

...

;  /* Documents a relationship between n and a */

Standard Examples

Section Subclause 6.7.6.3 of C11 the C Standard [ISO/IEC 9899:2011] has several examples of conformant array parameters. Example 4 (paragraph 20) illustrates a variably modified parameter:

Code Block
void addscalar(int n, int m,
               double a[n][n*m+300], double x);

int main(void)
{
  double b[4][308];
  addscalar(4, 2, b, 2.17);
  return 0;
}

void addscalar(int n, int m,
               double a[n][n*m+300], double x)
{
  for (int i = 0; i < n; i++)
  for (int j = 0, k = n*m+300; j < k; j++)
    //* a is a pointer to a VLA with n*m+300 elements */
    a[i][j] += x;
}

Example 5 4 illustrates a set of compatible function prototype declarators

Code Block
double maximum(int n, int m, double a[n][m]);
double maximum(int n, int m, double a[*][*]);
double maximum(int n, int m, double a[ ][*]);
double maximum(int n, int m, double a[ ][m]);

These prototype declarators are also compatible:

Code Block
void f(double (* restrict) a)[5]);
void f(double a[restrict][5]);
void f(double a[restrict 3][5]);
void f(double a[restrict static 3][5]);

C11 concludes with the following note regarding example 5:

Note that the last declaration also specifies that the argument corresponding to a in any call to f must be a
non-null pointer to the first of at least three arrays of 5 doubles, which the others do not.

...

Noncompliant Code Example

This code example provides a function that wraps a call to the standard memset() function and has a similar set of arguments. However, although this function clearly intends that p point to an array of at least n chars, this invariant is not explicitly documented.

Code Block
bgColor#ffcccc
langc
void my_memset(char* p, size_t n, char v)
{
  memset( p, v, n);
}

Noncompliant Code Example

This noncompliant code example attempts to document the relationship between the pointer and the size using conformant array parameters. However, the variable n is used as the index of the array declaration before n is itself declared. Consequently, this code example is not standards-compliant and will usually fail to compile.

Code Block
bgColor#ffcccc
langc
void my_memset(char* p[n], size_t n, char v) 
{
  memset( p, v, n);
}

Noncompliant Code Example

Compliant Solution (GCC)

This compliant solution uses a GNU extension to forward declare the size_t variable n before using it in the subsequent array declaration. Consequently, this code allows the existing parameter order to be retained and successfully documents the relationship between the array parameter and the size parameterThis doesn't compile because n is used before being declared.

Code Block
bgColor#ffcccc#ccccff
langc
void my_memset(size_t n; char p[n], size_t n, char v) 
{
  memset( p, v, n);
}

Compliant Solution (API change)

This compliant solution changes the function's API by moving the size_t variable n to before the subsequent array declaration. Consequently, this code complies with the C99 standard and successfully documents the relationship between the array parameter and the size parameter, but requires all callers to be updated.

Code Block
bgColor#ccccff
langc
void my_memset(size_t n, char p[n], char v) 
{
  memset( p, v, n);
}

Exceptions

API05-C-EX0: The extended array syntax is not supported by C++, or platforms that do not support C99, such as MSVC. Consequently, C programs that must support such platforms, including Windows, need not use conformant array parameters. One option for portable code that must support such platforms is to use macros:

Code Block
bgColor#ccccff
langc
#include <stddef.h>
  
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L || defined(__STDC_NO_VLA__)
#define N(x)
#else
#define N(x)  (x)
#endif
  
int f(size_t n, int a[N(n)]);

Risk Assessment

Failing to specify conformant array dimensions increases the likelihood that another developer will invoke the function with out-of-range integers, which could cause an out-of-bounds memory read or write.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

API05-C

High

Probable

Medium

P12

L1

Bibliography

...

[ISO/IEC 9899:2011]Subclause 6.7.6.2, "Array Declarators"
Subclause 6.7.6.3, "Function Declarators (Including Prototypes)"


...

Image Added Image Added Image Added (see example 4)