1
昨天有一个群里的同学问我,他问我的问题是 c 语言函数是存在哪里的?是如何执行的?我下意识的觉得这位同学应该是个初学者,所以今天就写下一些基础方面的内容,「C语言的内存布局」。
程序代码可以看做是一个人,是人就需要有房子住,操作系统就是政府,政府有义务给找个位置给你住,如果你是高级人才,他就给你个 vip ,给你分个大房子,但是如果你对社会没有帮助( 手上没钱),那你可能就需要住天桥下。
我们刚开始学习单片机的时候,还没有进程,线程,操作系统的概念,程序直接下载到 rom 里面,等你有了操作系统概念,反过来看看,会觉得以前自己写的代码都是单线程的。
2
C 语言内存分配的框图如下,一个正常的执行代码, 操作系统需要给他分配一段内存区域,这一大块内存区域还要分为几个小区域。
1.文本段(Text segment)
2.初始化数据段(Initialized data segment)
3.未初始化数据段(Uninitialized data segment)
4.堆栈(Stack)
5.堆 (Heap)
3
1.文本段:
文本段,也称为代码段,是目标文件或内存中包含可执行指令的程序的一部分。
作为存储区域,文本段可以放置在堆或堆栈下方,以防止堆和堆栈溢出覆盖它。
通常,文本段是可共享的,因此对于频繁执行的程序(例如文本编辑器,C 编译器,shell 等),只需要一个副本就可以在内存中。此外,文本段通常是只读的,以防止程序意外修改其指令。
文本段可以看做 这段代码 的 大脑,需要怎么执行,做什么,都把数据保存在这个位置了。
2.初始化数据段:
初始化数据段,通常简称为数据段。数据段是程序的虚拟地址空间的一部分,其包含由程序员初始化的全局变量和静态变量。
请注意,数据段不是只读的,因为变量的值可以在运行时更改。
该段可以进一步分类为初始化的只读区域和初始化的读写区域。
例如,C 中的 char s [] =“hello world”定义的全局字符串,和 int debug = 1 之类的 C语句 将存储在初始化的读写区域中。像 const char * string =“hello world”这样的全局 C语句 会把 字符串文字“hello world”存储在初始化的只读区域中,而字符指针变量字符串存储在初始化的读写区域中。
例如:static int i = 10 将存储在数据段中,global int i = 10 也将存储在数据段中
3.未初始化的数据段:
未初始化的数据段,通常称为“bss”段,以古代汇编运算符命名,代表“由符号启动的块”。此段中的数据在程序启动之前由内核初始化为算术 0 执行
未初始化的数据从数据段的末尾开始,包含初始化为零或在源代码中没有显式初始化的所有全局变量和静态变量。
例如,变量声明为static int i; 将包含在BSS部分中。
例如,一个声明为int j的全局变量; 将包含在BSS部分中。
4.堆栈:
我们很多时候说的堆栈,实际上就是栈( stack )跟堆( heap )是没有关系的。我们学习上经常说的 压栈,说的就是压堆栈,因为堆栈有 「先进后出的原则」,所以我们操作的是栈尾。
堆栈区域传统上与堆区域相邻并向相反方向增长; 当堆栈指针遇到堆指针时,可用内存耗尽(因为理论上这个两个家伙是不可能 相遇的) 。(使用现代大地址空间和虚拟内存技术,它们几乎可以放置在任何地方,但它们通常仍会朝着相反的方向发展。)
堆栈区域包含程序堆栈,LIFO结构,通常位于存储器的较高部分。在标准的PC x86计算机体系结构上,它向零地址发展; 在其他一些架构上,它朝着相反的方向发展。“堆栈指针”寄存器跟踪堆栈的顶部; 每次将值“推”到堆栈上时都会调整它。
5.堆:
堆是通常发生动态内存分配的段。
堆区域从BSS 段的末尾开始,并从那里增长到更大的地址。堆区域由 malloc,realloc 和 free 管理,可以使用 brk 和 sbrk 系统调用来调整其大小;它们也可以使用mmap实现,将不连续的虚拟内存区域保留到进程的“虚拟地址空间”中。堆区域由进程中的所有共享库和动态加载的模块共享。
4
实用 size 命令可以分析生成的可执行程序每个段的大小,单位是(bytes 字节)。我们可以用这个命令验证上面的论证。
代码实例
#include<stdio.h>
int main()
{
return 0;
}
Linux@dev:~/cStudy$ gcc memory-test.c -o memory-test && size memory-test
text data bss dec hex filename
1099 544 8 1651 673 memory-test
Linux@dev:~/cStudy$
现在代码里面什么都没有,可以看到每个段内容的大小。
#include<stdio.h>
int g;
int main()
{
static int i;
return 0;
}
--------------------------------------------------
Linux@dev:~/cStudy$ gcc memory-test.c -o memory-test && size memory-test
text data bss dec hex filename
1099 544 16 1659 67b memory-test
Linux@dev:~/cStudy$
我们声明了一个未初始化的全局变量和一个未初始化的静态变量后,bss 段发生了变化。
再修改一下
#include<stdio.h>
int g = 99;
int main()
{
static int i = 100;
return 0;
}
--------------------------------------------------
Linux@dev:~/cStudy$ gcc memory-test.c -o memory-test && size memory-test
text data bss dec hex filename
1099 552 8 1659 67b memory-test
Linux@dev:~/cStudy$
初始化后 bss 段变成原来 8 了, data 段多了 8 个字节。
再修改一下
#include<stdio.h>
int g ;
int main()
{
static int i = 100;
return 0;
}
--------------------------------------------------
Linux@dev:~/cStudy$ gcc memory-test.c -o memory-test && size memory-test
text data bss dec hex filename
1099 548 12 1659 67b memory-test
Linux@dev:~/cStudy$
这样 bss 段和 data 段都比初始值增加了 4 个字节。
5
推荐之前跟这个相关的文章,也是写 内存 区域的东西,两篇文章联系起来看会非常好。
堆和栈的区别(转过无数次的文章)
还是推荐一下我的知识星球,加入星球的福利我会慢慢增加,当然还是以自愿为主,加入星球了同学,希望还是在星球提问题,这样的问答环境会更好,也方便追溯问题。
我的知识小密圈
昨天大概看了一下文章的情况,发现收藏的人数很多,但是转发的人数很少,各位大哥哥,大帅哥,能不能给小弟一点点活路稍微转发一下,在知乎的时候,就是收藏的数量远远超过点赞,已经让我很伤心了。
完,各位共勉~