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