在 C++ 中,存储类说明符 static 从数据区域分配内存。“数据区”是什么意思?
8 回答
我不熟悉术语“数据区”,但内存通常分为“代码段”和“数据段”。代码存在于前者,数据存在于后者。我想这就是这里的意思。
传统上,两者之间没有区别。但是,许多现代操作系统可以禁止在数据段中执行代码(只要 CPU 支持这种区别)。这有时与“NX标志”的流行语相吻合,如“不执行”,可以有效防止某些恶意代码注入情况。
/EDIT:请注意,C++ 标准没有提到“数据区”。
这些区域的名称因平台、编译器和链接器而异。
一般来说,有:
- 程序文本:可执行代码空间。
- 常量:不可执行的常量。
- 栈:栈。
- bss:在 C/C++ 术语中广义上的“静态”。“由符号开始的块”
- 数据:未初始化的全局变量
- heap:运行时分配的存储空间。
在这种情况下,有问题的文档使用名称“数据区”来表示传统上称为 bss 段的内容。
在 C 语言中,存储类说明符“静态”表示在程序的生命周期内存在的内存,并被初始化为零或初始化器的值。在示例中:
static int s_value_one;
static int s_value_two = 123;
s_value_one 的值保证为零,s_value_type 的值在 main() 中的第一条语句处为 123。这如何成为现实是一个实施问题。
除了 Konrad 所说的,将变量声明为静态基本上意味着它的内存在程序加载时被分配,而不是在堆或堆栈上。从历史上看,在关键应用程序中仅使用静态变量意味着应用程序的内存占用量在运行时不会改变,因此由于资源限制而失败的可能性较小。不知道现代操作系统是否仍然如此。
如果您让编译器生成一个映射文件作为其输出的一部分,您可以查看所有各个部分中的内容,包括数据。
康拉德说的。
我想补充一点,如果将数据放在代码部分中,仍然有 CPU 无法读取数据,反之亦然。这些在几十年前更为常见,但它们在嵌入式世界中仍然存在。
简而言之,链接器只是将相同种类的符号组合在一起。在 PC 上,您通常拥有的不仅仅是简单的代码和数据区域。您还将找到未初始化数据、只读数据和其他依赖于操作系统的数据的区域。
几乎没有谷歌搜索,我在这里找到了关于这些主题的更多信息:
数据可能会在很多地方结束。通常,局部变量是在堆栈上分配的,您可以使用 malloc(或默认版本的“new”)在堆上分配东西。然而,静态数据通常在您的程序启动时分配,并且可能在任何地方结束——具体取决于编译器、操作系统和可执行格式。
可执行文件中有很多信息。
一个可执行文件,在其物理文件中存储了许多类型/类的数据。
例如是
- 可执行代码指令
- 资源
- 依赖信息(这个二进制文件依赖的dll)
- 从此二进制文件导出的符号
ETC
需要有某种组织方式
.exe 文件格式中的所有这些信息,以便操作系统可以轻松找到所有信息并加载可执行文件并使其正常工作。为此,在 Windows 世界中使用了一种称为 PE(便携式可执行文件)的常见二进制格式(当然是由 M$ 创建的)。我刚刚列出的所有信息(以及更多信息)都在二进制文件的不同部分进行了详细描述。
.data 部分
一个这样的部分是 .data 部分。.data 部分包含所有已初始化的全局和静态数据,而 .bss 部分包含未初始化的全局数据。
为什么你需要一个单独的部分用于 globals ?
好吧,全局的行为类似于全局,因为它是在程序生命周期内存在的内存区域中创建的,而不是像堆栈这样可能被覆盖/重用的临时数据结构。(就像普通的自动变量一样)。
编译器
因此,这些变量需要分配在堆中的某个永久地址中,不幸的是在编译时无法知道这些地址。所以编译器将所有的全局变量和静态变量都放在了这个.data/.bss段中,而引用这些变量的指令则引用了.data/.bss中这些相对永久的地址。
链接器
当链接器在现实世界中加载可执行文件时,它决定这些部分必须放在哪里,并为这些临时地址创建 FIX UP,以便引用全局变量的指令引用程序内存中现在的真实虚拟地址。
现在您知道 .data 部分/区域是什么以及为什么需要在该区域中为全局变量分配一些空间以及这如何实时帮助程序。谷歌搜索 PE 格式和链接器和 .data 部分等将为您提供链接。
我认为“数据区”是指堆,而局部变量通常位于堆栈上。
或者这意味着为该变量分配的内存位于可执行文件的 .data 部分,但这将特定于 Windows 和 PE 格式。