一个典型的 C 程序的内存布局是以下几个分区(section)组成:
对于这些分区的理解是:
代码段顾名思义储存的是二进制的可执行指令。数据段储存的是已初始化的内容,例如字符串,数字等,ex.char s [] =“ hello world”;
。未初始化的数据段,声明一个全局变量但不赋值,将存储在 bss 段,static int i;
。程序员 malloc/new 的区域会在堆上,堆可伸缩,同时会产生大量的碎片内存。栈的结构是 LIFO 的结构,存储着临时变量,指针,由 OS 进行管理。数据段和代码段是在可执行文件中,运行时由 OS 读取到内存中的。BSS 段不在可执行文件中,可执行文件中 BSS 段仅仅是一个数字,标记所有未初始化的变量占用空间的总和。在读取可执行文件时,OS 在数据段旁边,开辟这些区域并将它们赋值为 0 。
这是我的理解,有错误的地方还请指教。以下是我的问题:
如果一个未初始化的变量定义如,static int i;
,然后用 malloc 进行分配,这块内存位于何处呢?还有如char s [];
,从硬盘读取文件内容到内存,应该会在堆区,然后赋值给s
,这块内存位于何处呢?
1
codehz 2020-11-09 11:36:05 +08:00
1. 全局变量不是从 malloc 获取的,malloc 拿到的是新的指针。。。
2. char s[]; 是非法的变量声明,直接编译不过 |
2
chinuno 2020-11-09 11:37:23 +08:00 via Android
1.静态变量编译好位置就是在 bss 里,malloc 要怎么给他分配? malloc 出来的是个指针,顶多取值给他赋值
2.char s[];你试试能不能编译。后面带字符串的情况下才可以省略长度,因为静态字符串数据以及固定了,编译的时候确定下来了。假设你制定了 s 的长度特别长够你用了,那你只能把堆里的文件数据复制到 s 本身的内存空间( bss 段)内 |
3
codehz 2020-11-09 11:52:34 +08:00
3. 硬盘读取文件内容到内存的过程,不会帮你分配内存,所以你可以直接传递一个够大的全局变量数组。。。
|
5
fffang OP |
6
XiaoxiaoPu 2020-11-09 12:04:10 +08:00
@fffang 全局变量定义时就确定了长度,不可能运行时才确定长度
|
7
codehz 2020-11-09 12:06:31 +08:00 via Android
@fffang 不能做到,bss 段的东西长度必须是编译的时候已知的,没有任何例外。。。
但是这不妨碍你放一个指针,指针的大小是固定的,但指向的内容可以不确定大小(也不确定是哪个内存区域,或者无效) 这时候就可以 malloc 了 |
8
raaaaaar 2020-11-09 12:07:45 +08:00 via Android
有书专门讲这些东西么,要看内存管理还是啥,我以前也看过这个,不过不是很透彻
|
9
sockpuppet9527 2020-11-09 12:39:33 +08:00 1
1. malloc 分出来的东西是逻辑地址,给到你返回的是一个指向该逻辑地址的指针,具体硬件上的地区要经过 MMU 。
2. 硬盘读取的内容你可以放到堆,也可以放到栈。栈是要先声明长度,比如 char s[100]; ,这就是栈和堆最大的不同,栈的深浅是固定的,堆是动态的,就算到了汇编层面,你写一个方法,栈里面变量的使用也需要先声明长度,越了栈就违法了。 另外关于内存(逻辑地址)在何处,这个是动态的,参考一本内核书上的 hello world 例子: ``` 08048368 <greeting>: 8048368: 55 push %ebp 8048369: 89 e5 mov %esp,%ebp 804836b: 83 ec 08 sub $0x8,%esp 804836e: 83 ec 0c sub $0xc,%esp 8048371: 68 84 84 04 08 push $0x8048484 8048376: e8 35 ff ff ff call 80482b0 <printf@plt> 804837b: 83 c4 10 add $0x10,%esp 804837e: c9 leave 804837f: c3 ret 08048380 <main>: 8048380: 55 push %ebp 8048381: 89 e5 mov %esp,%ebp 8048383: 83 ec 08 sub $0x8,%esp 8048386: 83 e4 f0 and $0xfffffff0,%esp 8048389: b8 00 00 00 00 mov $0x0,%eax 804838e: 83 c0 0f add $0xf,%eax 8048391: 83 c0 0f add $0xf,%eax 8048394: c1 e8 04 shr $0x4,%eax 8048397: c1 e0 04 shl $0x4,%eax 804839a: 29 c4 sub %eax,%esp 804839c: e8 c7 ff ff ff call 8048368 <greeting> 80483a1: c9 leave 80483a2: c3 ret 80483a3: 90 nop ``` 你可以看到 main 的入口是 0x8048380,这其实是给到 CPU 的 EIP 寄存器使用的,指向的虚拟地址。 |