-1

Why doesn't the following code snippet produce error about array having negative size? I am using x86-64 gcc 12.2 on godbolt.org.

#include <stdio.h>
#define STATIC_ASSERT(x)    ((void)sizeof(char[1 - 2*!(x)]))

int main(void)
{
    STATIC_ASSERT("State"[0] == 'j');
    sizeof(char[-"State"[0]]);
    printf("%c, %d, %d", "State"[0], 1 - 2*!("State"[0] == 'j'), -"State"[0]);

    return 0;
}
user1806687
  • 934
  • 1
  • 10
  • 27
  • 1
    Why don't you print the result of `sizeof(char[-"State"[0]]);`? – mch Apr 24 '23 at 11:21
  • Apparently, `-"State"[0]"` gets converted to an unsigned type (`size_t`?) in this context. Hence, the array does not have negative size, but a very large positive size. – nielsen Apr 24 '23 at 11:24
  • MSVC *does* complain. It doesn't like `sizeof(char[-"State"[0]]);` ("expected constant expression"), but when replaced with `sizeof(char[-83]);` it refuses with "negative subscript". – Weather Vane Apr 24 '23 at 11:30
  • https://stackoverflow.com/questions/3385515/static-assert-in-c <- C11 has an actual _Static_assert, more hacks for pre-C11 – teapot418 Apr 24 '23 at 11:32

2 Answers2

3

char[-2] is a compile time error: https://godbolt.org/z/3aGEh6ozo

The compiler knows that it is negative at compile time.

int u = 0;
scanf("%d", &u);
printf("%zu\n", sizeof(char[u]));

this cannot be a compile time error, the compiler does not know that the user might enter -2. https://godbolt.org/z/GGcbohzM3

The size of the VLA gets converted to a size_t, which changes the -2 to 18446744073709551614 ( 2^64 - 2 ), so it is a huge array.

The compiler does not complain about sizeof(char[1-2*!("State"[0] == 'j')]) (your macro expansion) for the same reason.

mch
  • 9,424
  • 2
  • 28
  • 42
  • 1
    I suspect for a VLA, violating [this](https://port70.net/~nsz/c/c11/n1570.html#6.7.6.2p5) clause - "each time it is evaluated it shall have a value greater than zero" - results in undefined behavior as it's a run-time issue. – Andrew Henle Apr 24 '23 at 11:42
1

When you pass a non-integer constant expression as x ("string_literal"[0] doesn't qualify as an integer constant expression as C defines it), the macro will create a VLA. While it is undefined behavior for VLA sizes to be negative upon VLA creation, this is not guaranteed to be diagnosed at compile time and it is also completely fine if such a invalidly-sized VLA happens in a dead branch (i.e., when the size expression isn't evaluated)

if (0){ int x = -1; char vla[x]; } //ok
if (0){ char nonvla[-1]; } //reliable compiletime error

Better _Static_assert emulations rely on bitfield sizes:

#define STATIC_ASSERT(x)    sizeof(struct{ int _:((x)?1:-1); })

where there's no variable-width version and and non-integer constant expression are simply forbidden. The above will reliably fail at compile time whenever x is either 0 or something that's not an integer constant expression. C11's _Static_assert also behaves like that.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142