C 程序对静态对象、自动对象和动态分配的对象使用不同的内存区域

C 程序对静态对象、自动对象和动态分配的对象使用不同的内存区域,c,C,我在阅读《C初级读物+》一书时遇到了一个理解记忆区域的问题。书中说: 通常,程序使用不同的内存区域来存储静态对象、自动对象和动态分配的对象。清单12.15说明了这一点 这本书的结论是: 如您所见,静态数据(包括字符串文字)占用一个区域,自动数据占用第二个区域,动态分配数据占用第三个区域(通常称为内存堆或空闲存储) 我可以看出他们的地址不同。我如何确保它们属于不同的区域?表示一个对象可以具有4种不同的存储持续时间中的一种。这些是: 静止的 自动的 分配 线 上面的代码解决了其中的前3个问题 使

我在阅读《C初级读物+》一书时遇到了一个理解记忆区域的问题。书中说:

通常,程序使用不同的内存区域来存储静态对象、自动对象和动态分配的对象。清单12.15说明了这一点

这本书的结论是:

如您所见,静态数据(包括字符串文字)占用一个区域,自动数据占用第二个区域,动态分配数据占用第三个区域(通常称为内存堆或空闲存储)

我可以看出他们的地址不同。我如何确保它们属于不同的区域?

表示一个对象可以具有4种不同的存储持续时间中的一种。这些是:

  • 静止的
  • 自动的
  • 分配
  • 线
上面的代码解决了其中的前3个问题

使用
static
修饰符在文件范围或本地范围声明static对象。字符串文字也是静态对象

一个自动的对象,通常称为局部变量,它在函数或封闭范围内声明

分配的对象是其内存由分配函数(如
malloc
)返回的对象

实际上,编译器通常会将这些对象类型中的每一种放在不同的内存区域中。静态对象通常放在可执行文件的数据部分,自动(读取:本地)对象通常存储在堆栈上,分配的对象通常存储在堆上

字符串文字尤其是静态对象,通常放在标记为只读的数据段的特殊部分

这些区域通常位于不同的内存区域中,但不需要这样做。因此,虽然在实践中,这些区域中的每个对象的地址都会有明显的不同,但它们并不需要如此


因此,您不需要真正“确保”不同类型的变量位于不同的区域。编译器会根据您如何定义它们来为您处理这些问题。

不同的区域有非常不同的地址。如果他们在同一地区,他们会有相似的地址。更好的示例是,我们在每个区域分配2个对象:

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
  int stack1;
  int stack2;
  static int bss1;
  static int bss2;
  static int data1=1;
  static int data2=1;
  int* heap1 = malloc(1);
  int* heap2 = malloc(1);  
  char* rodata1 = "hello";
  char* rodata2 = "world";

  printf(".stack\t%p %p\n",  &stack1,  &stack2);
  printf(".bss\t%p %p\n",    &bss1,    &bss2);
  printf(".data\t%p %p\n",   &data1,   &data2);
  printf(".heap\t%p %p\n",   heap1,    heap2);
  printf(".rodata\t%p %p\n", rodata1,  rodata2);

  free(heap1);
  free(heap2);
}

正如您所见,同一段中的两个变量具有几乎相同的地址,唯一的区别是对象的大小(可能还有一些对齐空间)。与其他段中的变量相比,它们的地址非常不同。

nm命令可能有助于获得分配给程序的实际段,例如,您可以看到静态存储偏移量

静态存储:0x600b00处30
==>
0000000000 600b00 D静态存储

在coliru现场观看:

注意附加的nm命令:
gcc main.cpp&&./a.out&&nm a.out

但是,您必须记住,您通常在一个带有MMU的系统上,因此虚拟内存地址映射到实际内存


查找更多信息,例如,在

上,我想尝试以更简单的方式对此进行解释

0x…
是表示二进制位字符串的十六进制字符串。你可以把它想象成一个数字,但简写是因为你不需要知道这个数字,只需要知道它相对于其他类似编码数字的相对值。这意味着“地址值”实际上只是一个数字

为什么用数字来表示内存位置?因为不管出于什么目的,内存只是一个真正的大字节数组,谁的值都可以通过索引读取。C在逻辑上(而非物理上)将该内存阵列划分为不同的部分,以实现高效存储。因此,内存中的两个地址位置越近,它们在字节数组表示中的距离就越近


任何应用程序可用的地址范围都是在运行时确定的,实际上不是特定内存空间的任何部分。因此,平心而论,没有理由确定某些项目在特定的内存区域中。只是在统计上,内存中两个相邻的对象位于不同的区域是非常不可信的。

您可以看到
静态存储
字符串文字
带引号的字符串
大致位于同一个“内存区域”(
0x10A6xxx
)。
自动存储
自动字符数组
0x7ffee55df
)也是如此。其余的(动态分配)仍然在另一个“区域”(
0x7fbf1d40
)(字符串文字不一定以.rodata结尾,但您知道了)为什么要在同一上下文(在
main
内)中显示两个
static int
变量(
bss1
data1
)?区别似乎是一个是初始化的,另一个不是,但这有关系吗?也许你想把其中一个放在
main
?@FabioTurati之外,对于某些编译器来说,它很重要,而对于其他编译器来说,它并不重要。@FabioTurati因为我想说明,bss和数据最终位于完全不同的地址空间,如输出示例所示。是的,初始化很重要。为了理解不同的部分可能会有所帮助(它是为通用的嵌入式系统编写的,除非PC上没有真正的NVM)。@标记它与编译器无关,而是与链接器格式标准和在调用main()之前执行的“CRT”无关。
static_store: 30 at 0x10a621040
  auto_store: 40 at 0x7ffee55df768
         *pi: 35 at 0x7fbf1d402ac0
  String Literal at 0x10a620f00
 Auto char Array at 0x7ffee55df770
  Dynamic String at 0x7fbf1d402ad0
   Quoted String at 0x10a620f9b
#include <stdio.h>
#include <stdlib.h>

int main (void)
{
  int stack1;
  int stack2;
  static int bss1;
  static int bss2;
  static int data1=1;
  static int data2=1;
  int* heap1 = malloc(1);
  int* heap2 = malloc(1);  
  char* rodata1 = "hello";
  char* rodata2 = "world";

  printf(".stack\t%p %p\n",  &stack1,  &stack2);
  printf(".bss\t%p %p\n",    &bss1,    &bss2);
  printf(".data\t%p %p\n",   &data1,   &data2);
  printf(".heap\t%p %p\n",   heap1,    heap2);
  printf(".rodata\t%p %p\n", rodata1,  rodata2);

  free(heap1);
  free(heap2);
}
.stack  000000000022FE2C 000000000022FE28
.bss    0000000000407030 0000000000407034
.data   0000000000403010 0000000000403014
.heap   0000000000477C50 0000000000477C70
.rodata 0000000000404000 0000000000404006