【问题标题】:How sizeof(array) works at runtime?sizeof(array) 在运行时如何工作?
【发布时间】:2012-04-22 03:00:51
【问题描述】:

我读过 C 中的 sizeof 运算符是在编译时解释的,因为在编译时编译器知道数组大小及其类型,sizeof 能够计算数组占用的字节数。但是 sizeof 如何为以下代码:

 #include<stdio.h>
 #include<string.h>
 int main()
 {
    int n;
    scanf("%d",&n);
    int a[n];
    int s=sizeof(a);
    printf("%d",s);
    return 0;
 }

这里的数组大小在编译时是未知的,那么它是如何正常工作的呢?

【问题讨论】:

  • 可变长度数组是个例外,对它们来说,sizeof 在运行时计算,而不是在编译时计算。
  • @MДΓΓБДLL 不,那是关于 malloced 的东西,而不是关于 VLA。
  • @MДΓΓБДLL:您可能的副本正在处理不同的情况(malloc(),不是 VLA,可变长度数组)。

标签: c sizeof


【解决方案1】:

sizeof 在 C89 中总是在编译时计算。由于 C99 和可变长度数组,当可变长度数组是 sizeof 操作数中表达式的一部分时,它是在运行时计算的。

sizeof 操作数的求值也是如此:如果操作数是可变长度数组类型,则在 C89 中不求值,但在 C99 中求值。例如:

int n = 5;
sizeof (int [n++]); 

// n is now 6

【讨论】:

  • int [n++] 是什么意思?第一次看到C语言
  • @TheMask int [N] 是 C 类型名称。这是int 的数组N。当N 不是答案中的常量时,它是一个可变长度数组。
  • @ouah :因为早些时候我们已经研究过 sizeof 运算符中的表达式并没有真正评估。 IE。 sizeof(n++) 将 n 保留为 5(如果 n=5 最初)。那为什么这里的 n 值变了?
  • @rforritz 可变长度数组类型操作数是该规则的一个例外,在这种情况下操作数被评估
【解决方案2】:

由于您将sizeof 应用于可变长度数组,其大小在编译时并不完全已知,因此编译器必须生成代码以在运行时执行部分操作。

gcc 4.6.3 的高级优化器将您显示的代码转换为

scanf ("%d", &n);
t12 = (long unsigned int) n;
t13 = t12 * 4;
__builtin_alloca (t13);
t16 = (unsigned int) t12;
t17 = t16 * 4;
s18 = (int) t17;
printf ("%d", s18);

这是否解释了幕后发生的事情? (不要被看起来很傻的临时变量数量吓到——这是程序在我要求中间代码转储时处于static single assignment 形式的产物。)

【讨论】:

  • 将要编译的文件单独放在一个目录中,然后从该目录中的 shell 调用 gcc,在命令行选项中添加 -fdump-tree-all(您可能需要 -S 和-O2 以及)。您需要将文件隔离在临时目录中,因为这将生成大约 100 个中间代码文件(这只是优化管道的前半部分;-fdump-rtl-all 将为您提供另外 60 个文件)。然后您可以按顺序阅读它们(它们已编号)
  • @TheMask(续)“树”转储通常比“rtl”转储更有用,除非您尝试调试 GCC 的“后端”之一(特定架构的代码生成)。您可以通过说-fdump-(tree|rtl)-PASSNAME 而不是-all 将输出限制为感兴趣的特定传递,其中PASSNAME 是转储文件名中数字后面的单词。有关详细信息,请参阅gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/…(向上滚动一点,然后从那里阅读到文档末尾)。
  • @TheMask ...我可能还应该指出internals manual for GCC,它解释了中间表示是什么以及它们是如何工作的。
  • 非常感谢!我会检查所有这些。 :)
【解决方案3】:

来自 C99 标准:

6.5.3.4

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

【讨论】:

    【解决方案4】:

    在这种情况下,sizeof() 在运行时进行评估。编译器因为知道a 的大小是基于数组声明时n 的值,所以生成代码以使用n 的适当值来返回sizeof() 的合理值.

    在 C99 中,并非所有 sizeof() 的使用都可以在编译时完全评估并减少为运行时常量。

    【讨论】:

    • 请注意,编译器可能会为n 使用旧值;例如int n = 5; char a[n]; n = 10; return sizeof(a); 总是返回 5,而不是 10。
    • @DavidHeffernan:我明白了,希望我更新的答案更清楚。
    • doh 当我的意思是“运行”时,我不敢相信我输入了“编译”。早上太早了。 :)
    • @greg 是的,一个词可以产生真正的影响!!
    【解决方案5】:

    我读过 C 中的 sizeof 运算符解释为 编译时间

    sizeof 在所有情况下都在编译时确定,除了 VLA。对于 VLA,sizeof 在运行时进行评估。

    【讨论】:

    • Indeed (C11): "如果操作数的类型是变长数组类型,则计算操作数;否则,不计算操作数,结果为整数常量。"跨度>
    【解决方案6】:

    不管sizeof是在编译时还是运行时计算(或者更正式地说,它的结果是否是一个整数常量表达式),sizeof的结果完全基于它的参数的类型 而不是伴随可变长度数组本身的任何隐藏数据。当然,当sizeof 应用于可变修改类型时,生成的程序必须在某处跟踪该大小。但是如果表达式足够简单并且它最初导出长度的变量不能改变,它可能会简单地重新计算它。或者,它可以将类型的大小存储在某处(本质上是在隐藏的局部变量中),但这不会以任何可观察的方式连接到 VLA 对象(例如,如果您将指针传递给 VLA 的第一个元素到另一个函数,该指针不能用于恢复 VLA 长度)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-12
      • 2015-10-14
      • 2014-06-07
      • 2020-04-12
      • 1970-01-01
      相关资源
      最近更新 更多