长度为0的变长数组?

4

在C语言中,通常不允许数组大小为0(除非使用某些编译器端的扩展)。

但是,有些可变长数组(VLA)的长度可能为0。

它们是否被允许呢?

我指的是以下代码:

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;
    }
}

结果是一个 data 数组,长度为0、2、4或6,这取决于标志的组合。
现在的问题是:如果数组长度为0,这段代码是否有效?

@AntonH 这不适用,因为它是关于结构体中的数组,而不是可变长度数组。 - glglgl
3
这与那个问题没有任何关系。那个问题是关于编译器扩展的,只有在 C99 之前才能被证明是合理的。而这个问题是关于可变长度数组的,这只在 C99 中才能使用。两者之间没有交集。 - Pascal Cuoq
@glglgl 我的错误,读得太快了。 - AntonH
@pascal-cuoq 我的错,读得太快了。 - AntonH
顺便提一下:建议的解决方法是 char data[1 + 4 * !!flag1 + 2 * !!flag2];。当然,这在后续调用 sizeof data 时会有问题。 - chux - Reinstate Monica
1个回答

11

如果我们查看C99标准草案6.7.5.2节的数组声明符段落5重点在于):

如果大小是一个不是整数常量表达式的表达式:如果它出现在函数原型作用域的声明中,则被视为*替换;否则,每次评估它必须具有大于零的值。[...]

实际上使用clang启用未定义行为的Sanitizer,使用-fsanitize=undefined标志可以为此情况生成运行时警告(查看实时演示)

运行时错误:变长数组边界计算结果为非正值0


在C11标准中的6.7.6.2 §5节中,它因为添加而移动。这只是为那些查阅更新文档的人提供参考。 - Deduplicator
@Deduplicator 很有趣,但问题特别标记为 C99 - Shafik Yaghmour
谢谢您的回答。所以我需要放置类似于uint8_t datalen = 4 * !!flag1 + 2 * !!flag2]; char data[datalen ? datalen : 1];这样的内容。我猜想,即使我不访问数组,按照最初的意图声明数组也会产生未定义行为吗?(例如,在给定情况下,cursor为0,例如memcpy()将获得要复制的大小为0,因此实际上不会发生任何访问... - glglgl
0长度数组(包括可变长度数组)是有效的扩展。但是,必须在第一次使用时诊断0长度常量大小数组,因为它们违反了编译时约束。 - Deduplicator
@glglgl 是的,违反“必须”要求是未定义的行为,正如您所看到的,当使用 sanitizer 时,clang 将标记它。 - Shafik Yaghmour
2
@glglgl:简单地分配比必要的多一个元素的数组可能比仅在“必要”时这样做更有效。更好的方法是编译器编写者公开声明,任何不愚蠢的编译器都应该能够处理零大小的边角情况,无论标准是否规定。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接