这个文题似乎和C无关,但是C作为经久不衰的编程语言,作为软件基石构造砖块这么多年,用它来理解程序(可被CPU执行的整体)本质含义再好不过了,更上层的语言都有可能是C或者C++的封装与调用,对其他我没有深入了解,不做过多推测。
一个程序的本质是由数据和能被CPU执行的指令构成的,用专业的术语来说,一个程序如.exe, elf等文件,它们内部本质都是有text section, bss section, data section来组成。
text section 是什么意思了?它是代码段的意思,就是存储的可被CPU 执行的指令,如下一段代码,
int a = 1;
int b = 2;
int c = a + b;
/*
当上面这段代码经过编译后,生成的汇编会有这样一段伪代码,
MOV R0, #1
ADD R1 ,R0, #2
这两句都没有数据的参与,所以它们将会保存到text段中。
*/
bss section 是什么含义了,它是未被初始化的全局变量,它们会存在一个段中。
data section 则是已初始化的全局变量。
至于局部变量,它们是在栈中临时被分配的,还要一些用malloc申请的,它们是在堆中的,什么是堆,什么是栈?栈这个概念,要往后一点再做解释,堆了,就是在一段线性地址区间,拿出一段空间,用于动态分配用,这个动态分配是需要用软件来管理的,例如你调用了malloc(20),就是说你在这段空间用了连续的20个字节,假设分配了2K(2048bytes)堆空间。所以你的堆就变成了2048 - 20 = 2028 bytes,你可以继续在这段空间申请,但不能超过这个剩余余量,这些都是软件来管理的。那这段空间怎么分配了,一般在系统起来的时候,就选取好一段空间来分配,很多有集成工具的可以通过UI来配置,如常用的keil 。记住,你申请了,不用的时候一定要换回来,不然这段空间就被分出去了,它不会自动回来,慢慢的,别人就不能动态申请内存了。很多单片机程序,可能不会碰到堆空间的问题,因为ROM+RAM 够用,直接都是静态变量足以。但是概念你们必须要理解清楚。讲到这里,我还是想讲讲栈空间,一般的时候,我们的栈指针是被设定到可支援的物理内存最后,因为压栈是地址减小的操作,出栈是增大的过程,这样很多时候,不用担心入栈太深,导致出问题,但一段出问题了,你就要小心,是不是函数调用嵌套太深了,导致栈踩了前面的内存,有可能是数据,也有可能是代码,如果有操作系统,用的是线程自己的栈空间,这些你都需要注意,时时提醒自己,特别是有些毫无头绪的问题,很多时候就是栈被踩了,调整一下线程栈大小,调整一下函数调用,就可能复现不到的问题,你就要时刻注意了。好了,很难得问题,我也算讲完了。至于里面的内容,只有真正做过项目的同学才有体会,朋友们,你们慢慢体会。
很多时候,一个定义,需要讲讲它的历史和来源,但这三个段,它真的没有,也没找到,
只找到这句话 “这样的概念,不知道最初来源于哪里的规定,但在当前的计算机程序设计中是很重要的一个基本概念。”
当然,在不同地方(如keil, gcc ),它们对这些段可能有不同名称,但万变不离其宗,你理解它们的内涵,你就不怕它们的变化了。
要理解它们,你必须要理解编译链接的过程,下面这个图,从哪里来,我不记得了,但不重要,我们都要传递一个想法,理解清楚C语言的编译过程,
预处理是干什么? 它是将宏展开,一些配置(如define|)替换.
编译是什么? 它是将预处理后的文件转成汇编文件。
汇编是什么?它是将汇编文件转成目标文件,什么是目标文件了?
链接是干嘛呢? 是将一个个分段的目标文件,通过链接脚本,将其整合到一起,转成最终可以被执行的文件。
//在gcc工具链的时候,大家可以试试看
预处理
Gcc -E test.c –o test.i
编译 :
Gcc -S test.i -o test.s
汇编 :
Gcc -c test.s -o test.c / gcc –s test.s –o test.o
还有一些很有用的工具,也推荐给大家,
Objdump
Readelf
Size
Nm
/*
objdump -h xxxx.o
打印主要段的信息
objdump -x xxxx.o
打印更多的详细信息
objdump -s xxx.o
将所有段的内容以16进制方式打印出来
objdump -d xxx.o 或者-S
将所有包含指令的段反汇编
objdump -t xxx.o
查看所有的符号以及他们所在段
readelf -h xxx.o
查看.o文件的文件头详细信息
readelf -S xxx.o
显示.o文件中的所有段,即查看段表
size xxx.o
查看.o文件中各个段所占大小
nm xxx.o
*/
好了,今天就到这里,欢迎大家评论和点赞关注。谢谢 !