【问题标题】:Sizeof operator with variable-length array type具有可变长度数组类型的 Sizeof 运算符
【发布时间】: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)。
  • 这是关于sizeof+vla的,不是那个问题的重复

标签: c gcc sizeof variable-length-array


【解决方案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 递增。

      【讨论】:

        猜你喜欢
        • 2019-01-10
        • 2015-11-12
        • 1970-01-01
        • 1970-01-01
        • 2011-10-28
        • 1970-01-01
        • 2019-06-25
        • 2020-01-15
        相关资源
        最近更新 更多