变长数组类型的Sizeof运算符

13
根据cppreference:
如果表达式的类型是变长数组类型,则会评估表达式并计算它在运行时的数组大小
这意味着:如果expression的类型是VLA类型,则将对其进行评估。例如:
#include <stdio.h>

int main() {
    int i = 0;
    int a[i];
    printf("%zu\n",sizeof(a[i++]));
    printf("%d\n",i); // Here, print 0 instead of 1
    return 0;
}

根据参考文献,这里的i变成了1。但是,使用我的GCC编译器,i打印为0

请参见Wandbox演示版本


9
a[i++]不是VLA类型的表达式,它最终是下标表达式,并且具有类型为int。除此之外,即使是对于VLA,a[0]也违反了约束条件。 - StoryTeller - Unslander Monica
3
VLA的长度在定义时就被确定了。在上面的代码中,长度将为0,因此VLA将没有任何元素。定义后大小不会改变。此外,长度必须大于零,否则就是未定义行为。 - Ian Abbott
1
@byxor - 除了编译器扩展(因为这些编译器也支持C语言),C++并没有其他的扩展。 - StoryTeller - Unslander Monica
1
可能是[为什么sizeof(x ++)不会增加x?]的重复问题(https://dev59.com/6Wsy5IYBdhLWcg3w6yUN) - Maquefel
5
好的,我会尽力进行翻译。以下是需要翻译的内容:it's about sizeof+vla, it's not a duplicate of that question这个问题涉及sizeof和vla,它不是与那个问题重复。 - Uprooted
显示剩余2条评论
4个回答

20

首先,请注意数组无法具有大小为零,无论是 VLA 还是非 VLA。因此,您的代码会引发未定义行为。

C11 6.7.6.2/5

“如果大小是不是整数常量表达式的表达式:” /--/ “每次评估它时,它应该具有大于零的值。”


至于实际问题,a[i++] 的类型为 int,而不是 VLA 类型。

为了得到副作用,您必须涉及 VLA 数组类型本身,例如 sizeof(a)。只有这样才能对操作数进行副作用评估。以下是一个例子:

#include <stdio.h>

int main() {
    int i=1, j=1;
    int a[i][j];
    int b[1][1];

    (void) sizeof(a[--i]);
    (void) sizeof(b[--j]);
    printf("%d %d", i, j);

    return 0;
}

由于VLA的存在,第一个sizeof被求值,导致i最终为0,而j仍然为1,因为--j是普通数组中sizeof的一部分。


1
与原问题无关,但有什么想法为什么标准针对VLA类型的表达式提供了特殊情况? - Ajay Brahmakshatriya
1
假设 int a[i];,表达式 sizeof a 必须能够在运行时计算出正确的结果,因为 i 的值在编译时是未知的。 - user694733
@AjayBrahmakshatriya 因为大小只在运行时设置,所以不能保证在编译时预先计算。 - Lundin
@Lundin,这只需要sizeof a[--i]的值是运行时值。没有必要评估操作数。大小是从操作数的类型推断出来的,而不是从其值推断出来的。 - tstanisl

9
在你的示例中,sizeof表达式是int类型,而不是vla。如果它是vla,那么一切都可以工作:
#include <stdio.h>

int main() {
    int i = 5;
    int a[i][i];
    printf("%zu\n",sizeof(a[--i]));
    printf("%d\n",i); // Here, print 4
    return 0;
}

3

来自C标准#6.5.3.4p2 [我强调]

sizeof 操作符返回其操作数的大小(以字节为单位),其可以是表达式或用括号括起来的类型名称。大小由操作数的类型确定。结果是一个整数。如果操作数的类型是变长数组类型,则对操作数进行求值;否则,不对操作数进行求值并且结果是一个整数常量

在表达式中:

sizeof(a[i++])

a[i++]不是VLA,而是下标运算符表达式,其结果为整数。因此,操作数未被评估,出于同样的原因,编译器在此语句上发出警告:

警告:具有副作用的表达式在未评估的上下文中没有效果


3

为了引用规范参考的克隆词:

6.5.3.4 - sizeof和_Alignof运算符

sizeof运算符返回其操作数的大小(以字节为单位),操作数可以是表达式或带括号的类型名称。大小取决于操作数的类型。结果是一个整数。如果操作数的类型是可变长度数组类型,则会评估操作数;否则,操作数不会被评估,结果为整数常量。

如果您将示例修正为生成具有VLA类型的表达式,则将对其进行评估,一种方法如下:

#include <stdio.h>

int main() {
    int i = 1;
    int a[5][i];
    printf("%zu\n",sizeof(a[i++]));
    printf("%d\n",i);
    return 0;
}

在最后一行打印2, 因为i被增加了。


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