编译器如何存储有关数组大小的信息?

How does a compiler store information about an array's size?

最近我阅读了 GCC 中 malloc 函数的 IsoCpp about how compiler known size of array created with new. The FAQ describes two ways of implementation, but so basically and without any internal information. I tried to find an implementation of these mechanisms in STL sources from Microsoft and GCC, but as I see, both of them just call the malloc internally. I tried to go deeper and found an implementation,但我无法弄清楚魔法发生在哪里。 是否有可能找到它是如何工作的,或者它是在系统运行时库中实现的?

至少对于 GCC 目标 x86_64,可以通过查看 GCC 为这个简单程序生成的程序集来调查这个问题:

#include <iostream>

struct Foo
{
  int x, y;
  ~Foo() { std::cout << "Delete foo " << this << std::endl; }
};

Foo * create()
{
  return new Foo[8];
}

void destroy(Foo * p)
{
  delete[] p;
}

int main()
{
  destroy(create());
}

使用 Compiler Explorer,我们看到为 create 函数生成的代码:

create():
        sub     rsp, 8
        mov     edi, 72
        call    operator new[](unsigned long)
        mov     QWORD PTR [rax], 8
        add     rax, 8
        add     rsp, 8
        ret

在我看来,编译器正在调用 operator new[] 来分配 72 字节的内存,这比存储对象 (8 * 8 = 64) 所需的内存多 8 个字节。然后就是在本次分配的开始存储对象计数(8),返回前指针加8字节,所以指针指向第一个对象。

这是您链接到的 document 中列出的方法之一:

Over-allocate the array and put n just to the left of the first Fred object.

我在 libstdc++ 的源代码中搜索了一下,看看这是标准库还是编译器实现的,我认为它实际上是由编译器本身实现的,尽管我可能是错的。

这是编译器在 GCC 源代码中存储大小的位置:https://github.com/gcc-mirror/gcc/blob/16e2427f50c208dfe07d07f18009969502c25dc8/gcc/cp/init.c#L3319-L3325

以及 Clang 源代码中的等效位置:https://github.com/llvm/llvm-project/blob/c11051a4001c7f89e8655f1776a75110a562a45e/clang/lib/CodeGen/ItaniumCXXABI.cpp#L2183-L2185

编译器所做的是在 new T[N] returns。这反过来意味着必须在对 operator new[] 的调用中分配一些额外的字节。编译器生成代码在运行时执行此操作。

operator new[](std::size_t x) 本身不起作用:它只是分配 x 字节。编译器使 new T[N] 调用 operator new[](sizeof(T) * N + cookie_size).

编译器不“知道”大小(它是一个 run-time 值),但它知道如何生成代码以在后续 delete[] p.

上检索大小