读书笔记 ——《系统程序员成长计划》篇5:数据放在哪?

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  《系统程序员成长计划》

上一篇:读书笔记 ——《系统程序员成长计划》篇4:拥抱变化
下一篇:读书笔记 ——《系统程序员成长计划》篇6:写的又快又好的秘诀

一、数据放在哪里?

  书中以双链表几种方式存字符串为示例演示由于不明白数据的存放位置导致代码出错或存在问题(详细查看书籍)。
  关于内存布局也可查看:C语言——C程序的内存空间布局

二、内存段

1、未初始化的全局变量(.bss段)

1️⃣示例引入

int bss_array[1024 * 1024];
int main(int argc, char* argv[])
{
	return 0;
}

  编译生成可执行文件:# gcc -g bss.c -o bss.exe
  查看文件大小:# ls -l bss.exe

-rwxr-xr-x 1 root root 17656 Apr 18 21:29 bss.exe

可知:文件大小大概在17k左右。
  查看执行时文件大小:# objdump -h bss.exe|grep bss

bss.exe:     file format elf64-x86-64
 23 .bss          00400020  0000000000004020  0000000000004020  00003010  2**5

可知:文件大小大概在400k左右。
  由此可见,bss类型的全局变量只占运行内存,不占文件内存
2️⃣指令说明
详细请查看:gcc -g -o -c分别是什么意思objdump选项说明objdump objdump 二进制文件分析
  gcc -g :可执行程序包含调试信息
  objdump -h 文件名:显示目标文件各个section的头部摘要信息
  |管道符,格式:命令1|命令2|...:管道符左边命令的输出作为管道符右边命令的输入,依此类推。
  grep查找文件中符号条件的字符串
3️⃣bss 简述
  bss段被用来存放那些没有初始化或初始化为0的全局变量
  bss类型的全局变量只占运行的内存而不占用文件空间
  现在大多数操作系统在加载程序时,会把所有bss全局变量清零。但最好手动初始化为0.
  作为全局变量,在整个程序运行周期内,bss数据是一直存在的。

2、初始化的全局变量(.data段)

1️⃣ 示例引入

int bss_array[1024 * 1024]= {1};
int main(int argc, char* argv[])
{
	return 0;
}

  与前面的区别就是该全局变量进行初始化了。当然若初始化的数据全为0为了优化考虑会将其当做bss处理。
  编译生成可执行文件:# gcc -g data.c -o data.exe
  查看文件大小:# ls -l data.exe

-rwxr-xr-x 1 root root 4211984 Apr 18 22:07 data.exe

可知:文件大小大概在4M左右。
  查看执行时文件大小:# objdump -h data.exe|grep \\.data

22 .data         00400020  0000000000004000  0000000000004000  00003000  2**5

可知:文件大小大概在400k左右。
即:data类型的全局变量既占文件空间,又占用运行时内存空间,同样作为全局变量,在整个程序运行周期内,data数据也一直存在。
2️⃣ data 简述
  data段用来存放着数据,被用来存放那些初始化非0值的全局变量。
  若数据全是0,则为了优化考虑,会当作bss处理。
  data类型的全局变量既占文件空间,又占运行内存空间。
  作为全局变量,在整个程序运行周期内,data数据是一直存在的。

3、常量数据(.rodata段)

  ro:read only(只读),用来存放常量数据。
  需注意几点:
    (1)常量不一定就放在rodata里,有的立即数直接和指令编码在一起,存放在代码段(.text)中。
    (2)对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO)中只存在一个副本。
    (3 ) rodata是在多个进程间共享的,这样可以提高运行空间利用率。
    (4)在有的嵌入式系统中,rodata放在ROM(或者NOR闪存芯片)里,运行时直接读取,无需加载到RAM中。
    (5)在嵌入式Linux系统中,也可以通过一种叫作XIP(就地执行)的技术直接读取常量数据,而无需加载到RAM中。
    (6)常量是不能修改的,修改常量在Linux下会出现段错误。
  由此可见,把在运行过程中不会改变的数据设为rodata类型是有好处的。在多个进程间共享,可以大大提高空间利用率,甚至能不占用RAM空间。同时由于rodata在只读的内存页面中是受保护的,任何试图对它进行修改的行为都会被及时发现,这样一来还可以提高程序的稳定性。
  字符串会被编译器自动放到rodata中,其他数据要放到rodata中,只需要为其加const关键字修饰即可。

4、代码(.text段)

  text段存放代码(如函数)和部分整数常量,它与rodata段很相似,相同的特性我们就不重复了,主要的区别在于text段是可以执行的。

5、栈(stack)

  存放临时变量函数参数
  常用来实现函数调用递归操作
  栈是向下(低地址)增长的,每向栈中PUSH一个元素,栈顶就向低地址扩展,每从栈中POP一个元素,栈顶就向高地址回退。
  要注意,存放在栈中的数据只在当前函数及下一层函数中有效,一旦函数返回了,这些数据也就自动释放了,继续访问这些变量会造成意想不到的错误。

6、堆(heap)

  最灵活的一种内存,生命周期完全由使用者控制,标准C提供一下几个函数使用堆内存:
    函数名    用途
    malloc    分配一块指定大小的内存
    realloc    调整/重分配一块存在的内存
    free     释放不再使用的内存。

需注意:
  alloc/free要配对使用,否则分配内存而不释放的话会出现内存泄露(memory leak)。过多则会出现内存不足(Out of memory)
  分配多少用多少,否则若读多了,会读到随机数据。若写多了,则会造成随机破坏。而这种情况被称为缓冲区溢出(buffer overflow)

手动检查有无内存泄露缓冲区溢出是困难的,但可使用工具,如:valgrind

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值