4

In C, an array normally isn't allowed to have size 0 (unless I use the one or other compiler-side extension).

OTOH, there are VLAs whose length might turn out to be 0.

Are they allowed?

I am talking about the following code:

void send_stuff()
{
    char data[4 * !!flag1 + 2 * !!flag2];
    uint8_t cursor = 0;
    if (flag1) {
        // fill 4 bytes of data into &data[cursor]
        cursor += 4;
    }
    if (flag2) {
        // fill 2 bytes of data into &data[cursor]
        cursor += 2;
    }
}

The result is a data array with a length of 0, 2, 4 or 6, depending on the combination of the flags.

The question is now: Is this valid code for the case the array turns out to have length 0?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
glglgl
  • 89,107
  • 13
  • 149
  • 217
  • @AntonH It does not apply, as it is about an array in a struct, not a **variable-length** array. – glglgl Jun 06 '14 at 13:11
  • 3
    @AntonH This has nothing to do with that. That question is about a compiler extension that was only justifiable pre-C99. This question is about variable length arrays, which only became available in C99. There is **no** intersection between the two. – Pascal Cuoq Jun 06 '14 at 13:11
  • @glglgl My mistake, read too quickly. – AntonH Jun 06 '14 at 13:15
  • @pascal-cuoq My mistake, read too quickly. – AntonH Jun 06 '14 at 13:16
  • BTW: Suggested work-around: `char data[1 + 4 * !!flag1 + 2 * !!flag2];`. Of course this has trouble with later calls of `sizeof data`. – chux - Reinstate Monica Jun 06 '14 at 14:54

1 Answers1

11

This is not valid, if we go to the draft C99 standard section 6.7.5.2 Array declarators paragraph 5 says (emphasis mine):

if the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero.[...]

In fact with clang enabling the sanitizer for undefined behavior using the -fsanitize=undefined flag can generate a run-time warning for this case see it live:

runtime error: variable length array bound evaluates to non-positive value 0

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • In C11: 6.7.6.2 §5 It moved due to additions. Just added for reference for those looking in the newer docs. – Deduplicator Jun 06 '14 at 13:18
  • @Deduplicator interesting but the question is specifically tagged `C99`. – Shafik Yaghmour Jun 06 '14 at 13:19
  • Thank you for your answer. So I'll have to put something like `uint8_t datalen = 4 * !!flag1 + 2 * !!flag2]; char data[datalen ? datalen : 1];`. I suppose that declaring the array in the originally intended way produces UB, even if I don't access it? (As `cursor` is 0 in the given case, e. g. a `memcpy()` will get size 0 to be copied, so effetively no access will occur... – glglgl Jun 06 '14 at 13:20
  • 0-length arrays (including VLAs) are a valid extension. 0-length constant-size arrays must be diagnosed on first use though, because they are a compile-time constraint violation. – Deduplicator Jun 06 '14 at 13:25
  • @glglgl yes, violating a `shall` requirement is undefined behavior and as you can see `clang` will flag it as such when using the sanitizer. – Shafik Yaghmour Jun 06 '14 at 13:29
  • 2
    @glglgl: Simply allocating the array unconditionally one element larger than necessary is probably more efficient than trying to do so only when "necessary". Better yet would be for compiler writers to openly declare that any compiler that isn't stupid should be able to handle the zero-size corner case regardless of whether the Standard mandates it. – supercat Oct 01 '17 at 06:47