4

Is the following function strictly compliant to C99?

void foo(int a[static 0]) {
  (void)a;
}

Both GCC and Clang emits warning about using zero-sized array but I don't think this warning is justified. AFAIK, the 6.7.6.3p7 tells the static keyword indicates that the pointer a should point to at least zero elements, a requirement that is trivially satisfied by any pointer with determinate value.

Moreover, can such a function be called in the following cases:

int i;
foo(NULL);
foo(&i);
foo(&i + 1);
tstanisl
  • 13,520
  • 2
  • 25
  • 40
  • You’re not allowed to declare zero-sized arrays. You could use `1` just as meaningfully. – Jonathan Leffler Aug 11 '23 at 12:50
  • @JonathanLeffler: `foo(&i + 1)` is valid for `static 0`, but does not match the declaration of `foo` for `static 1`. – chqrlie Aug 11 '23 at 13:00
  • I think for similar language-lawyering in the past, we have deduced that an array passed to a function is first examined by the compiler to be a valid array _before_ it gets adjusted to a pointer to the first element. Same story as why `int arr[][]` is not a valid function parameter. I don't recall exactly which chapter that "overrules" the array decay though. – Lundin Aug 11 '23 at 13:01

2 Answers2

5

AFAIK, the 6.7.6.3p7 tells the static keyword indicates that the pointer a should point to at least zero elements,…

6.7.6.3 7 is not the only rule that applies.

As you know, a parameter declared as an array is automatically adjusted to be a pointer. But before it is adjusted, it is a parameter declared as an array. One of the rules for array declarators is a constraint in 6.7.6.2 1:

In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or * . If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero…

Violating 6.7.6.2 1 is a constraint violation even if 6.7.6.3 7 is satisfied.

… point to at least zero elements, a requirement that is trivially satisfied by any pointer with determinate value.

No, it is not. First, a null pointer does not point to at least zero elements because it does not point to anything.

Second, the pointer is not merely required to point to at least zero elements but specifically to the first element of an array with at least zero elements. Any array that has at least zero elements has at least one element, because 6.2.5 20 defines an array type as “contiguously allocated nonempty set of objects with a particular member object type”. So the mere fact it is an array means it has at least one element.

Therefore, even if declared int a[static 0], a must point to at least one int.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • How do you specify that pointers to the beginning and end of an array slice must be non null? example: `void T_sort(T start[static 0], T end[static 0])`, where both pointers can point the element past the end of an array but must not be null? – chqrlie Aug 11 '23 at 13:05
  • @chqrlie: The C standard does not provide a way to specify everything. – Eric Postpischil Aug 11 '23 at 13:05
  • @chqrlie `static 1` would work. An pointer to an array parameter with `static 1` may point 1 item beyond the array. Actually just as any scalar variable in C is regarded as an array with size 1, for the purpose of determining if pointer arithmetic goes out of bounds or not. – Lundin Aug 11 '23 at 13:07
  • 1
    @Lundin: `static 1` in an array declarator means the resulting pointer must point to the **first** element of an array with at least one element. A pointer to just beyond the end of an array (that does not coincidentally have another object there) does not point to the first element of an array with at least one element. – Eric Postpischil Aug 11 '23 at 13:10
  • ... however, if the pointer pair is supposed to indicate the bounds of a non-empty slice then it would be reasonable to declare the *lower* bound as `T start[static 1]`. But I don't see an alternative to declaring the upper bound as `T end[]` or an equivalent. So if one is trying to get the compiler's help to detect invalid usage then a start + length style is probably more amenable to that. – John Bollinger Aug 11 '23 at 13:42
  • Alternatively, if one is willing to change the meaning of the parameters then one could use an *inclusive* endpoint instead of an exclusive one. This presents no particular issue when you require the slice to be nonempty anyway, and then you can declare dimension `[static 1]` for both start and end. – John Bollinger Aug 11 '23 at 13:50
5

It is not compliant since arrays cannot have size 0. And therefore not "at least 0" either.

C17 6.7.6.2 emphasis mine:

Constraints

In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero.

(In C99 the same text is found below 6.7.5.2)

Lundin
  • 195,001
  • 40
  • 254
  • 396