VLAs和sizeof操作数的副作用

21

我知道sizeof从不计算其操作数的值,除非该操作数是VLA,或者我曾经以为。


void g(int n) {
    printf("g(%d)\n", n);
}

int main(void) {
    int i = 12;

    char arr[i]; // VLA

    (void)sizeof *(g(1), &arr); // Prints "g(1)"
    (void)sizeof (g(2), arr);   // Prints nothing

    return 0;
}

发生了什么?

以防万一,这是使用GCC 5.1在Coliru编译的。

1个回答

19
在发布之前,我似乎应该三思而后行,因为我刚发布完就意识到了这一点。
我的理解关于sizeof如何与VLAs交互实际上是正确的,如下面的引用所证实的(感谢@this!):
6.5.3.4 sizeof_Alignof运算符 如果操作数的类型是变长数组类型,则评估操作数;否则,不评估操作数,结果是一个整数常量。
这不是导致这种令人惊讶(对我来说)的行为的原因。
(void)sizeof (g(2), arr);

在子表达式(g(2), arr)中,逗号操作符触发了arr的数组指针衰减。因此,sizeof的操作数不再是VLA,而是普通的char*,并且它会回退到不评估其操作数的状态。 显然,在C++中已经改变了这种行为,逗号运算符不再衰减数组。

3
你可能想在某个地方添加这个内容:6.5.3.4 sizeof和_Alignof运算符。如果操作数的类型是可变长度数组类型,则对操作数进行评估;否则,不对操作数进行评估,结果为整数常量。 这样可以避免读者的困惑。 - this
也许我漏掉了什么,但是sizeof()是一个编译时运算符,而不是运行时函数。因此,(void)sizeof()是无意义的,因为sizeof()返回一个size_t(实际上是long int)值,而不是指针,并且sizeof()只期望一个参数。 - user3629249
@user3629249 sizeof 是一个严格的编译时运算符,不会评估其操作数,除非其操作数是VLA,那么它会在运行时评估它。(void) 强制转换与指针无关:它明确地丢弃了刚刚计算出来的 sizeof 值。它实际上不会影响程序的行为,但比仅仅计算并将表达式丢弃在地板上(对此,GCC会产生警告)更好的风格。[cont.] - Quentin
最后,不要让语法欺骗你:sizeof是一个运算符,而不是函数,当它的操作数是表达式时,它不需要括号(对于类型则需要)。这里的括号是为了给逗号运算符(不是参数列表中的逗号)优先级。sizeof仍然只接受一个操作数,即括号内的结果。 - Quentin

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