具有可变长度数组类型的 Sizeof 运算符

Posted

技术标签:

【中文标题】具有可变长度数组类型的 Sizeof 运算符【英文标题】:Sizeof operator with variable-length array type 【发布时间】:2018-07-17 13:56:39 【问题描述】:

据cppreference:

如果表达式的类型是变长数组类型,表达式 被评估并计算它评估的数组的大小 在运行时。

意思是:如果expression的类型是VLA类型,则expression被求值。例如:

#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 Demo。

【问题讨论】:

a[i++] 不是 VLA 类型的表达式。它最终是一个下标表达式,类型为int。除此之外,即使是 VLA 的 a[0] 也是违反约束的。 VLA 的长度在定义时确定。在上面的代码中,长度将为 0,因此 VLA 将没有元素。大小在定义后不会改变。此外,长度需要大于零,否则是未定义行为 @byxor - C++ 除了作为编译器扩展之外没有它们(因为编译器也支持 C)。 Why does sizeof(x++) not increment x?的可能重复 这是关于sizeof+vla的,不是那个问题的重复 【参考方案1】:

首先,请注意数组的大小不能为零,无论它是否是 VLA。所以你的代码会调用未定义的行为。

C11 6.7.6.2/5

"如果 size 是一个不是整数常量表达式的表达式:" /--/ "...每次对其求值时,它的值应大于零。"


至于实际问题,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;

这里 i 最终为 0,因为第一个 sizeof 由于 VLA 被评估,但 j 仍然为 1,因为 --j 是常规数组的 sizeof 的一部分。

【讨论】:

偏离原始问题的主题,但知道为什么该标准为 VLA 类型的表达式提供特殊情况吗? @Ajay 假设int a[i];,表达式sizeof a必须能够在运行时计算出正确的结果,因为i的值在编译时是未知的。 @AjayBrahmakshatriya 因为大小只在运行时设置,所以不一定在编译时预先计算。 @Lundin,它只需要 sizeof a[--i] 的值作为运行时值。无需评估操作数。从操作数的类型而不是从其值推断的大小【参考方案2】:

您的示例中 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 Standards#6.5.3.4p2 [强调我的]

sizeof 运算符产生其操作数的大小(以字节为单位),它可以是表达式或类型的括号名称。大小由操作数的类型决定。 结果是一个整数。如果操作数的类型是变长数组类型,则计算操作数;否则,不计算操作数,结果为整数常量

在表达式中:

sizeof(a[i++])

a[i++] 不是 VLA,而是一个导致整数的下标运算符表达式。因此,操作数不会被评估,出于同样的原因,编译器会对此语句发出警告:

warning: expression with side effects has no effect in an unevaluated context

【讨论】:

【参考方案4】:

取一个规范性引用的克隆词为它:

6.5.3.4 - The sizeof and _Alignof operators

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;

Prints 2 on the last line,因为i 递增。

【讨论】:

以上是关于具有可变长度数组类型的 Sizeof 运算符的主要内容,如果未能解决你的问题,请参考以下文章

C语言中计算数组长度的方法是啥

为啥零长度 VLA 是 UB?

C++ length()、size()、sizeof()三者的区别

指针数组与sizeof运算符

详解C语言中sizeof的使用

数组的长度(c写法)