【问题标题】:How does sizeof know the size of the operand array?sizeof 如何知道操作数数组的大小?
【发布时间】:2011-04-04 12:12:29
【问题描述】:

这可能是一个愚蠢的问题,但是当您不传入数组中元素的数量时,sizeof 运算符如何知道数组操作数的大小。我知道它不会返回数组中的总元素,而是返回字节的大小,但要得到它仍然必须知道数组何时结束。只是好奇这是如何工作的。

【问题讨论】:

  • +1 表示sizeof 是编译时运算符,-1 表示它在运行时跟踪指针指向的数组大小。
  • 我想 +1 所有那些说它也是编译时运算符的答案。但遗憾的是,他们不认为这个也被标记为“c”,并且应该详细说明 C 的 VLA,其中sizeof 需要评估其参数并产生运行时值以获取大小。所以我 +1 代表 c++ 部分,-1 代表回答问题的 C 部分,我总共得到 0 票。
  • @Johannes:虽然确实如此,但绝大多数 C 代码仍然是 C89/C90 代码,其中不允许使用 VLA。
  • @Billy 即使是这样,也不会使我的观点无效。但我怀疑。混合代码/声明是 c99,仍然有很多人这样做,并且经常阅读人们使用运行时 i 编写 i,甚至没有考虑 C99 - 他们认为这是老式 C。鉴于这个问题,我我什至不确定@marchinram 是否知道其中的区别,因此告诉他这一点更为重要。

标签: c++ c sizeof


【解决方案1】:

除了在一种情况下,sizeof 在编译时执行它。在编译时,编译器会跟踪对象的 full 类型'不包括大小,尝试使用sizeof 将失败],sizeof 基本上只是将编译器中的一条信息“导出”到正在编译的代码中,因此它在生成的代码中本质上是一个常量。

例外情况是sizeof 应用于可变长度数组 (VLA)1。当应用于 VLA 时,sizeof 评估其操作数(否则它不会),并产生 VLA 的实际大小。在这种情况下,结果不是常数。


1。 VLA 在 C99 中正式成为 C 的一部分,但在此之前一些编译器支持它们。虽然不是 C++ 的正式组成部分,但一些编译器(例如 g++)也包含 VLA 作为 C++ 的扩展。

【讨论】:

  • 编译器会跟踪完整的类型……除非因为类型不完整而不这样做。在这种情况下,sizeof 无法使用。
  • 这不是真的。在编译时不处理可变长度数组 sizeof 调用。您应该更新您的答案或将其删除。 en.wikipedia.org/wiki/Sizeof
【解决方案2】:

sizeof 在编译时计算。这就是为什么当你创建一个动态数组时,你会按照以下方式创建它。

char * array;
int size;
//Get size somehow.
array = malloc(size*(sizeof(char)));

// 现在在编译期间,编译器肯定知道 char 的大小。因为它必须在内存上对齐它们。此时,操作系统知道它必须分配多少大小。

另一方面,

可变长度数组是在堆栈上创建的。但是任何 malloc 分配的内存都会在堆上创建。

【讨论】:

    【解决方案3】:

    sizeof通常在编译时进行评估。值得注意的例外是 C99 的可变长度数组。

    int main(int argc, char **argv)
    {
        if (argc > 1)
        {
            int count = atoi(argv[1]);
            int someArray[count];
    
            printf("The size is %zu bytes\n", sizeof someArray);
        }
        else puts("No");
    }
    

    【讨论】:

      【解决方案4】:

      Sizeof 总是在编译时计算。在多通道编译器生成符号表的同时,编译器必须确定声明的每个符号的大小,以进一步生成中间代码。因此,对于代码中的所有 sizeof 引用都替换了确切的值。在中间代码生成阶段,所有运算符、语句都被转换为正确的中间代码(ASM/其他格式)。最后,m/c代码生成阶段将其转换为机器码。

      上面看到的一些关于与 sizeof 相关的动态分配的讨论根本不在上下文中。任何对 size(*p) 的引用,其中 p 是任何数据类型的指针,编译器只是找出 *p 的数据类型并替换它的大小,而不是去检查分配块的 MCB 头以查看分配的内容内存大小。它不在运行时。例如双 *p; sizeof(*p) 仍然可以在不为指针 p 分配任何内存的情况下完成。怎么可能?

      【讨论】:

      • "Sizeof 总是在编译时计算。"正如其他答案和问题 cmets 中所讨论的,对于可变长度数组而言,情况并非如此。这个答案应该被删除,并且对于当前的 C 语言是错误的(阅读规范)。 en.wikipedia.org/wiki/Sizeof
      【解决方案5】:

      Sizeof 只能应用于完全定义的类型。编译器将能够在编译时确定大小(例如,如果您有类似 int foo[8]; 的声明),或者它将能够确定它必须添加代码来跟踪变量的大小-length 数组(例如,如果您有类似 int foo[n+3]; 的声明)。

      与此处的其他答案相反,请注意,从 C99 开始,sizeof() 不一定必须在编译时确定,因为数组可能是可变长度的。

      【讨论】:

      • 谈论自我实现的预言 :)
      • 这不是真的。在编译时不处理可变长度数组 sizeof 调用。您应该更新您的答案或将其删除。 en.wikipedia.org/wiki/Sizeof
      • @xaxxon:对于 3 年前发布的问题的每个答案,您都不应该向 cmets 发送垃圾邮件。尤其是当 3 年前在 cmets 中就问题本身进行了关于 VLA 的讨论时。
      【解决方案6】:

      导致您难以理解这一点的问题可能是因为您混淆了数组和指针,正如许多人所做的那样。但是,数组不是指针double da[10] 是一个由十个 double 组成的数组,而不是 double*,当您要求它评估 sizeof(da) 时,编译器肯定知道这一点。编译器知道sizeof(double),你不会感到惊讶吗?

      数组的问题在于,它们在许多情况下会自动衰减为指向其第一个元素的指针(例如当它们被传递给函数时)。但是,数组是数组,指针是指针。

      【讨论】:

      • +1 用于解释这里常见误解的根源。
      • 完全正确,sbi。 Peter van der Linden 的杰出著作“Expert C Programming”的第 4、9 和 10 章很好地区分了指针和数组。见鬼,第 4 章的标题是“令人震惊的真相:C 数组和指针不一样”,正如 sbi 所说。它用 15 页来讨论这个主题。查看目录,尤其是。通道。 4、9 和 10:books.google.com/…
      • 所以你说,“你不会对编译器知道 sizeof(double) 感到惊讶吗?”因为编译器看到声明并将其与适当的数据类型相匹配。所以编译器看到声明double da[10],然后在代码中看到sizeof(da),编译器说,“啊哈,我知道da是一个由10个doubles组成的数组,因为我可以看到声明在第 8 行列出数组的大小。”所以编译器在编译时从源代码本身推导出数组的大小。我知道了,谢谢!
      • @sbi 我的问题是“在fgets 中的while 中使用a*b+csizeof(arr) 是否最佳?”现在我知道了。谢谢。
      【解决方案7】:

      sizeof operator“知道”所有原子数据类型的大小,因为结构、联合和数组只能通过组装原子类型来构造,因此很容易确定任何类型数组的大小。它使用基本算法来确定复杂类型(在编译时)。

      【讨论】:

        【解决方案8】:

        引用wiki:

        这是我们的责任 编译器的作者来实现 sizeof 运算符以特定的方式和 对于给定的实现是正确的 语言。 sizeof 运算符 必须考虑到 底层实现 内存分配方案获得 各种数据类型的大小。大小为 通常是编译时运算符,它 表示在编译过程中,sizeof 并且它的操作数被替换为 结果值。这在 由 C 生成的汇编语言代码 或 C++ 编译器。为此原因, sizeof 有资格作为运算符,甚至 虽然它的使用有时看起来像 函数调用。

        【讨论】:

        • 虽然这是真的,但它并没有真正回答 OP 的问题。
        【解决方案9】:

        sizeof 在编译时被解释,编译器知道数组是如何声明的(因此它占用了多少空间)。在动态分配的数组上调用 sizeof 可能不会达到您想要的效果,因为(正如您提到的)未指定数组的终点。

        【讨论】:

        • 很确定这不是真的,根据对问题本身的讨论以及阅读规范 - 至少对于现代 C 而言。最好删除这个答案。 en.wikipedia.org/wiki/Sizeof
        • @xaxxon:标准没有明确说它是在编译时评估的,但它确实说sizeof 不评估它的操作数。操作数表达式只对编译器有意义,所以它真的是唯一可以实现的地方。 VLA 由编译器间接处理。编译器已经必须生成代码来计算为 VLA 分配多少空间(数组成员大小 * 某个变量),因此它可以将该数字重新用于该 VLA 上的任何 sizeof 调用。
        【解决方案10】:

        如果您在局部变量上使用sizeof,它会知道您声明了多少个元素。如果您在函数参数上使用sizeof,它不知道;它将参数视为指向数组的指针,sizeof 给出指针的大小。

        【讨论】:

        • 它确实知道。参数一个指针(指向第一个元素,而不是数组),编译器知道指针的大小。 C 没有数组参数。
        【解决方案11】:

        Sizeof 是编译时运算符;它拥有与编译器一样多的信息。 (显然编译器确实知道数组的大小)。

        这就是为什么如果你在一个指针上调用sizeof 你会得到指针的宽度,而不是指针指向的数组的大小。

        【讨论】:

        • 那么,(几年没用过C++了),如果你请求sizeof并取消引用指向数组的指针会发生什么? sizeof 会简单地失败,还是会查找它记录的有关数组大小的内容并返回?
        • 如果取消引用指针,您将获得对数组存储的对象的引用,并且 sizeof 将返回该引用。 int a[5]; int *p = a; assert(sizeof(*p) == sizeof(int);
        • 这不是真的。在编译时不处理可变长度数组 sizeof 调用。您应该更新您的答案或将其删除。 en.wikipedia.org/wiki/Sizeof
        • @xaxxon:在这里我们达到了语言不一致的地步。在 C++14 中,尽管添加了堆栈分配的可变长度内置数组,但它仍然是编译时运算符。在 C99 中,它不是编译时间,但我认为 use 并没有流行起来,因为它在 C11 中是可选的,并且根本不受主要编译器之一的支持。当区别对所提出的问题没有影响时,我认为没有任何理由为初学者混淆一个很少使用的语言功能的答案。而且,当我在 3 年前写下答案时。
        • 是的,我们需要阻止人们将东西标记为 C 和 C++,因为这意味着你不知道发生了什么。
        【解决方案12】:

        编译器知道您的应用程序中每种类型的大小,sizeof 只是请求编译器为您生成该值。

        【讨论】:

        • 这不是真的。在编译时不处理可变长度数组 sizeof 调用。您应该更新您的答案或将其删除。 en.wikipedia.org/wiki/Sizeof
        • @xaxxon:到目前为止,可变长度数组在 C++ 中是无效的,所以答案是:所有有效的 C++ 类型的大小是在编译时计算的。上次会议向委员会提出了 C++ 的可变长度数组,该提案得到了好评,但在下一个标准 (C++14) 获得批准之前,这将不是有效的 C++。即使使用 VLA,编译器也知道大小(它知道创建数组的表达式,因此它可以将数字保存在一边),因为 VLA 无法限制编译器可以注入的函数的范围在需要的地方使用相同的值。
        猜你喜欢
        • 2010-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多