8

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.

12
  • You can get an answer (well and you have one yourself) if you remove the "at least" part. With this part the answer is "no way". Commented May 16, 2019 at 21:01
  • 3
    As you noticed, C has a feature for this, static in the array size in the parameter declaration, but compiler support for it is mostly non-existent. (Some compilers will warn if they see you pass a null pointer for such a parameter, but they otherwise neglect it.) You could pass a pointer to an array, such as int (*)[5], and this will require the caller to pass a pointer to an array of 5 int, but it must be exactly 5; they could not pass an array of 6 int. Commented May 16, 2019 at 21:03
  • @Eugene-sh I wrote at least because of this sentence for the blog I mentioned: "The compiler can warn callers when it sees them calling the function with anything but an array of 10 or more ints.". But I can remove this part to make the question clearer. Commented May 16, 2019 at 21:04
  • It's not clearer, it's the difference between "possible" and "not possible". You have the answer down there for that. Commented May 16, 2019 at 21:07
  • 1
    C11: 6.7.6.3.7: "A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression." Commented May 16, 2019 at 22:13

2 Answers 2

11

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

Sign up to request clarification or add additional context in comments.

7 Comments

Sadly, this approach also causes problems with const correctness, especially with typedefs because the intuitive way of making the pointer const causes incompatible pointer warnings/errors
@bruno Yes, I spoke about the case the array passed is too small and, as it is meant to be an output array, the function will potentially write outside the boundary of the array. It is why I would like to be sure at compile-time the passed array is of the required size.
What happens when you input a pointer to a VLA? Its size is unknown at compile time, so I guess it won't complain. You should still assert its size inside the function, maybe.
@Swordfish Except the remark of Vlad Tepesch, are there any cons using the method you suggest regarding reliability and security of the code?
@Mat.R If we're talking statically typed arrays (size known at compile time) it shouldn't be too hard to write code that doesn't write beyond the bounds of those arrays. However, my suggestion does what you asked for, it gives a warning (or error) at compile time. If you want a method that allows arrays of a minimum size then Eric Postpischils answer is for you.
|
4

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.

2 Comments

Can I ask you what do you mean by: "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."? Passing an array to a function is in fact passing a pointer so how can we enforce to pass an array? Or maybe I just did not understand your sentence.
@Mat.R; The argument is an array. During evaluation of the function call, the arguments are evaluated. In that evaluation, the array is converted to a pointer to its first element. But it is an array originally and when the macro uses it with sizeof. The sizeof operates on the array, not the pointer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.