在编译时分配的内存意味着编译器在编译时解析,其中某些内容将在进程内存映射内分配。
例如,考虑一个全局数组:
int array[100];
编译器在编译时知道数组的大小和的大小int
,因此它在编译时知道数组的整个大小。默认情况下,全局变量还具有静态存储持续时间:它在进程存储空间的静态存储区域(.data / .bss节)中分配。有了这些信息,编译器将在编译期间决定该数组将位于该静态存储区的哪个地址。
当然,内存地址是虚拟地址。该程序假定它具有自己的整个内存空间(例如,从0x00000000到0xFFFFFFFF)。这就是为什么编译器可以进行诸如“好的,数组将位于地址0x00A33211”的假设的原因。在运行时,MMU和OS会将地址转换为实际/硬件地址。
值初始化的静态存储有些不同。例如:
int array[] = { 1 , 2 , 3 , 4 };
在我们的第一个示例中,编译器仅决定将数组分配到的位置,并将该信息存储在可执行文件中。
在使用值初始化的情况下,编译器还将数组的初始值注入可执行文件中,并添加代码,该代码告诉程序加载器在程序开始分配数组后,应使用这些值填充数组。
这是编译器(带有x86目标的GCC4.8.1)生成的汇编的两个示例:
C ++代码:
int a[4];
int b[] = { 1 , 2 , 3 , 4 };
int main()
{}
输出组件:
a:
.zero 16
b:
.long 1
.long 2
.long 3
.long 4
main:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
popq %rbp
ret
如您所见,这些值将直接注入到程序集中。在数组中a
,编译器会生成16字节的零初始化,因为标准表示静态存储的事物应默认初始化为零:
8.5.9(初始化程序)[注]:
静态存储持续时间的每个对象在程序启动时都将其初始化为零,然后再进行其他初始化。在某些情况下,稍后会进行其他初始化。
我总是建议人们反汇编他们的代码,以查看编译器对C ++代码的实际作用。这适用于从存储类/持续时间(如此问题)到高级编译器优化。您可以指示编译器生成程序集,但是在Internet上有许多很棒的工具以友好的方式完成了该程序集。我最喜欢的是GCC Explorer。