用Linux命令浅析C内存机制

    有人会问,知道C语言的内存分配有什么用?

有很大一部分人,对这些都不太懂,不是也写出了很多c代码吗?

但是我要告诉你的是,不懂C语言的内存分配,不是一个好程序猿

先上一张我在网上找的一张图


这一张图内容有点多,只需要了解其中一部分即可(其实其他的我也不太清楚吐舌头

text segment:可以理解为代码段,存储程序代码,其实就是机器指令,可读可执行,占程序文件大小

data segment:可以理解数据段,存储初始化为非0的全局和静态变量,初始化为0的静态和全局变量,已经被编译器优化在另外一个bss段,可读写,占程序文件大小

bss segment:Block Started by Symbol,存储未初始化或初始化为0的全局和静态变量,它只是记录所有的这些变量的大小总和,并没有内容,所以它不占程序文件的大小,占运行时内存

.rodata:这个上图并没有说明,但是还是有的,实际就是data segment,不过它是只读的,但是不是所有常量都存储在这个区,全局的const、全局字符串是存放在.rodata,函数内的字符串和常量一般会成为立即数和指令一起编译在text段,ro:是只读的意思

stack:栈存放的是一些局部变量和函数的形参,这些不需要程序员申请和释放,这些都是系统自行分配和释放的,地址是向下往低地址扩张的,我们只需要知道他的作用域和生命周期

扫描二维码关注公众号,回复: 1683628 查看本文章

heap:堆存放的是malloc申请的内存,这个是是程序员自行申请和free的,地址是从低地址向高地址增长的,我们要大概理解一下,系统会维护一个空闲的内存链表,当我们需要申请一段内存,就会在这个链表寻找,找到合适就把这一段从空闲链表删除,“合适”:这个实现实际比较复杂的,可以看看malloc的实现原理

malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块

可以思考一下为什么分这么区,是不是有点复杂和麻烦?(答案放在最下面)

首先贴上一段代码


下面我们在用Linux命令来分析这段代码


objdump -h命令:显示各个不为0的section头部摘要信息

可以看到我们上面说到的数据段和代码段等上面都有,段的属性,其中READONLY代表只读、CONTENTS代表占用文件空间大小。

可以看到.bss段没有CONTENTS这个属性,所以不占用文件空间大小

看一下各个变量的存储位置


上面.var变量是在.bss段,这个实际是我改动上面的代码int var = 0;才得到的,如果是int var;var对应的段是*com*,这个是什么鬼,搞不懂

代码段和数据段是编译时就已经存在的,堆和栈是运行时才存在的

再来看一下里面的字符串和常量存储的位置


可以看出来指针变量指向的字符串常量是放在.rodata段的,非指针变量的的右值字符串常量和常量放在可读写.data或仅读.text(放在代码段,成为立即数和指令一起放在代码段)

再来看一下这段代码的部分汇编代码


可以看到

char string4[10] = "byebye"; 对应的一条指令是 movabsq $11157201626570, %rax

很明显是用立即数送寄存器。所以beybye是已经放在代码段里面了

很多的赋值语句都是变成了立即数送寄存器


现在我们可以用一张图概括一下


知道这些有什么用呢?

例如:

代码运行阶段,我们不要试图去修改.text和.rodata里面的数据,会提示段错误


.data和.bss里面的数据是程序运行时,一直存在的

stack:不要试图返回一个栈指针

heap:malloc要进行成功判断,malloc和free要配对使用,free之后最好将指针指向NULL

懂了C语言的内存分布,实际上面这些例子其实就很好理解,写代码就不会写出这些垃圾了

分这么多区是为了什么呢

1.一部分数据仅可读,一部分数据却可读写,分了区以后就可以保护只读的地方的数据被修改

2.cpu的缓存分为数据缓存和指令缓存,分了区以后可以有效提高缓存的命中率(缓存的命中率:想要的数据在缓存即是命中。缓存的速度大于内存的速度,所以提高命中率是很有必要的)

3.可以节约内存,把程序相同的只读部分进行共享,只需要一份拷贝就行。


猜你喜欢

转载自blog.csdn.net/follow_blast/article/details/79202332