C/C++什么时候分配静态内存?在编译时或程序开始时是 运行?
When is static memory allocated in C/C++? At compile time or at the very beginning of when a program is run?
不同的来源对我来说有不同的说法 - 一些 Whosebug 答案说它是在编译时分配的 - 其他人说它是在编译时“定义”的,并在运行时的最开始分配(“加载时间”是有些人怎么称呼它),而另一些人则说它是在编译时分配的。 C/C++ 中的静态内存何时准确分配? (如果它与“定义”变量有关 - 有人可以告诉我在内存级别“定义”变量是什么意思 - 将不胜感激!)
此外,您如何在运行时将指针设置为已分配静态内存的起始位置?
静态内存分两步分配。
步骤 1 由链接器执行,因为它布置可执行映像并说明静态变量在相对地址 space 中的位置。
步骤2是加载程序在实际分配进程内存时执行的。
在C++中,静态对象在进入main
之前被初始化。如果你不小心你的代码,你会看到仍然是零的对象,即使它们有总是会改变它的构造函数。 (编译器会尽可能多地进行持续评估,因此玩具示例不会显示它。)
C 标准只说了这一点:
[...]All objects with static storage duration shall be initialized (set to their initial values) before program startup. The manner and timing of such initialization are otherwise unspecified.
和
2 The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime.34) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
3 An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
但是... as-if 规则使这进一步变得复杂,实际实施只需要在可观察到的副作用发生的情况下执行此操作。
事实上,例如在Linux中,有人可能会争辩说,具有静态存储持续时间的变量是在生成可执行文件时由编译器和链接器初始化和分配的.当程序为运行时,动态链接器(ld.so
)然后准备程序段,以便从可执行映像到RAM的初始化数据为memory-mapped(mmap
),默认 (zero-initialized) 数据从归零页面映射。
虽然虚拟内存是由编译器、链接器和动态链接器分配的,但实际可写的 RAM 页框仅在您第一次写入页面上的变量时分配...
但是在基本情况下你不需要知道这个。 就好像 在输入 main
之前分配和初始化具有静态存储持续时间的变量的内存,尽管实际上并非如此。
在典型的工具中,具有静态存储持续时间的内存被安排在多个步骤中:
- 编译器在目标模块(可能通过某种形式的汇编代码)中生成数据,描述对各种内存的需求:内存初始化为零,内存初始化为特定值,然后是 read-only,初始化为特定值并可以修改的内存,不需要初始化的内存,可能还有其他内存。编译器还包括必要的初始数据、有关引用所需内存中不同位置的符号的信息以及其他信息。此时,内存分配的形式大致为“常量数据段需要8个字节,并为其地址设置一个名为
foo
的符号。”
- 链接器将此信息组合成可执行文件中的类似信息。它还解析有关符号的部分或全部信息。此时,内存的分配形式为“初始化的non-constant数据段需要3048字节,这里是它的初始数据。当它被分配一个虚拟地址时,以下符号应该被调整:
bar
是从节开始的偏移量 124,baz
是在偏移量 900…”
- 程序加载器读取此信息,为其分配虚拟地址space中的位置,并可能将可执行文件中的一些数据读入内存或通知操作系统数据所在的位置在需要的时候发现。至此,代码中引用各种符号的地方已经根据这些符号的最终值进行了修改。
- 操作系统为虚拟地址分配物理内存。通常,当进程试图访问特定页面中的内存时,这是“按需”分段(内存页面)完成的,而不是在程序最初加载时完成的。
All-in-all,在任何特定时间都不会分配静态内存。它是许多活动的组合。对程序的影响主要是它的发生与程序启动时所有分配一样,但物理内存可能仅在指令实际执行之前分配。 (物理内存甚至可以从进程中取出并稍后恢复。)
不同的来源对我来说有不同的说法 - 一些 Whosebug 答案说它是在编译时分配的 - 其他人说它是在编译时“定义”的,并在运行时的最开始分配(“加载时间”是有些人怎么称呼它),而另一些人则说它是在编译时分配的。 C/C++ 中的静态内存何时准确分配? (如果它与“定义”变量有关 - 有人可以告诉我在内存级别“定义”变量是什么意思 - 将不胜感激!)
此外,您如何在运行时将指针设置为已分配静态内存的起始位置?
静态内存分两步分配。
步骤 1 由链接器执行,因为它布置可执行映像并说明静态变量在相对地址 space 中的位置。
步骤2是加载程序在实际分配进程内存时执行的。
在C++中,静态对象在进入main
之前被初始化。如果你不小心你的代码,你会看到仍然是零的对象,即使它们有总是会改变它的构造函数。 (编译器会尽可能多地进行持续评估,因此玩具示例不会显示它。)
C 标准只说了这一点:
[...]All objects with static storage duration shall be initialized (set to their initial values) before program startup. The manner and timing of such initialization are otherwise unspecified.
和
2 The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime.34) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
3 An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
但是... as-if 规则使这进一步变得复杂,实际实施只需要在可观察到的副作用发生的情况下执行此操作。
事实上,例如在Linux中,有人可能会争辩说,具有静态存储持续时间的变量是在生成可执行文件时由编译器和链接器初始化和分配的.当程序为运行时,动态链接器(ld.so
)然后准备程序段,以便从可执行映像到RAM的初始化数据为memory-mapped(mmap
),默认 (zero-initialized) 数据从归零页面映射。
虽然虚拟内存是由编译器、链接器和动态链接器分配的,但实际可写的 RAM 页框仅在您第一次写入页面上的变量时分配...
但是在基本情况下你不需要知道这个。 就好像 在输入 main
之前分配和初始化具有静态存储持续时间的变量的内存,尽管实际上并非如此。
在典型的工具中,具有静态存储持续时间的内存被安排在多个步骤中:
- 编译器在目标模块(可能通过某种形式的汇编代码)中生成数据,描述对各种内存的需求:内存初始化为零,内存初始化为特定值,然后是 read-only,初始化为特定值并可以修改的内存,不需要初始化的内存,可能还有其他内存。编译器还包括必要的初始数据、有关引用所需内存中不同位置的符号的信息以及其他信息。此时,内存分配的形式大致为“常量数据段需要8个字节,并为其地址设置一个名为
foo
的符号。” - 链接器将此信息组合成可执行文件中的类似信息。它还解析有关符号的部分或全部信息。此时,内存的分配形式为“初始化的non-constant数据段需要3048字节,这里是它的初始数据。当它被分配一个虚拟地址时,以下符号应该被调整:
bar
是从节开始的偏移量 124,baz
是在偏移量 900…” - 程序加载器读取此信息,为其分配虚拟地址space中的位置,并可能将可执行文件中的一些数据读入内存或通知操作系统数据所在的位置在需要的时候发现。至此,代码中引用各种符号的地方已经根据这些符号的最终值进行了修改。
- 操作系统为虚拟地址分配物理内存。通常,当进程试图访问特定页面中的内存时,这是“按需”分段(内存页面)完成的,而不是在程序最初加载时完成的。
All-in-all,在任何特定时间都不会分配静态内存。它是许多活动的组合。对程序的影响主要是它的发生与程序启动时所有分配一样,但物理内存可能仅在指令实际执行之前分配。 (物理内存甚至可以从进程中取出并稍后恢复。)