Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing an array size in a function parameter in C when passing an array

Context

In C, I have a function which take an array as a parameter. This parameter is used as an output in this function. The output is always the same size. I would:

  • make the required size clear for anyone reading the code (it will already be in the function comments, though),
  • ideally the compilation to output a warning or an error so I can prevent problems at compile-time instead of run-time.

A potential solution

I found here: https://hamberg.no/erlend/posts/2013-02-18-static-array-indices.html something which look like a solution but I am not able to get a warning or an error during the compilation if I try to pass a smaller array than the required size.

Here is my complete program main.c:

void test_array(int arr[static 5]);

int main(void)
{
    int array[3] = {'\0'};

    test_array(array); // A warning/error should occur here at compilation-time
                       // telling me my array does not meet the required size.

    return 0;
}

void test_array(int arr[static 5])
{
    arr[2] = 0x7; // do anything...
}

Contrary to this blog, I use gcc (version 7.4.0) instead of clang with the following command:

gcc -std=c99 -Wall -o main.out main.c

In my code, we can see that the test_array() function needs a 5 elements array. I am passing a 3 elements one. I would expect a message from the compiler about this.

Question

In C, how to force a function parameter being an array to be of a given size? In case it is not, it should be noticeable at compilation-time.

like image 381
Mat.R Avatar asked May 16 '19 20:05

Mat.R


Video Answer


2 Answers

If you pass a pointer to the array instead of a pointer to its first element, you will get an incompatible pointer warning:

void foo(int (*bar)[42])
{}

int main(void)
{
    int a[40];
    foo(&a);  // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
    // note: expected 'int (*)[42]' but argument is of type 'int (*)[40]'

    int b[45];
    foo(&b);  // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
    // note: expected 'int (*)[42]' but argument is of type 'int (*)[45]'
}

Compile with -Werror to make it an error.

godbolt

like image 109
Swordfish Avatar answered Oct 19 '22 18:10

Swordfish


To test that the size of the array (not pointer) being passed is at least 5 elements, a Static_assert can be used, and the necessary _Static_assert can be inserted via a preprocessor macro.

After the declaration of the function, insert:

#define test_array(arr) \
    do \
    { \
        _Static_assert(sizeof (arr) / sizeof *(arr) >= 5, "Array is too small."); \
       test_array(arr); \
    } while (0)

(The do … while (0) is a classic way of defining a macro to act syntactically like a statement, so that it can be followed by a ; and flow as expected with if statements and such.)

Before the definition of the function, insert:

#undef test_array

(If any more uses of the function follow, another copy of the #define must be inserted. Alternatively, the function could be defined early in the source file and followed by a #define, eliminating any need for further #undef or #define directives.)

Generally, such code is unlikely to be useful, as programs often pass pointers to the first elements of the arrays (or to elements in the middle of arrays), and it is not possible to test how many elements are at the space a pointer is pointing to. So this is useful only in code where we require an array to be given as the argument. And that requirement is not enforced by this code.

like image 30
Eric Postpischil Avatar answered Oct 19 '22 16:10

Eric Postpischil