关于对c代码内存的一些理解
起因是在观看裸机开发教程中在汇编中引入了一个链接文件,教程中并未对其做出过多的解释,所以我就自己查看资料对c的内存管理有了进一步的理解,链接文件如下:
1 | SECTIONS{ |
在这个文件里我们看到了.text,.rodata,.data,.bss我一开始看到这个觉得一头雾水(因为本人c语言基础较弱),首先是不知道这些段是干嘛的,其次是不知道编写这个所谓链接文件的目的,所以我就去网上查看资料,终于有了一些收获
链接文件的目的
我们在做开发时,不可能只有一个c文件,一定是很多文件共同配合组成一个项目,而每个文件都会编译成.o文件,所以需要通过链接器来将二进制数据有序组织起来,并且根据链接脚本中的指示为程序不同部分分配内存,我们知道了链接文件的用处,那么问题来了,我们刚刚那一段链接脚本的代码时干嘛的呢?
在知道那一段链接脚本是干嘛的之前,我们需要知道c语言代码在内存中的存放分区:
- 栈区:这是一段由编译器自己分配并释放的内存,通常存放局部变量,形参,返回值(也就是自定义函数中的内容),所以几乎所有的函数调用都依赖与栈来实现,并且具有先进先出的规则
- 堆区:堆区内存需要程序员手动申请并释放,自己分配的内存除非程序员手动释放否则永不释放(malloc,free)
- 数据段和代码段:这一部分就和我们链接文件相关了
- 在数据段中分为以下部分:
- .bss段:这部分保存着未初始化的全局变量和静态局部变量,由于未初始化,所以会被系统自动初始化为0
- .data段:保存已经初始化的全局变量和静态局部变量
- .rodata段:只读数据段,存放着只读数据一般是程序里的只读变量(const修饰变量),字符串变量,常量数据
- 代码段细分以下几个区域:
- .text段:存放用户代码
- .init段:存放系统初始化代码
- 在数据段中分为以下部分:
有了上面3的解释再回去看上面代码就简单易懂了:
- 首先通过. = 0X87800000;将起始地址置为0X87800000
- 接着通过.text :{start.o main. *(.text) }设置.text段(也即是代码段)内容设置链接到开始位置的文件为start.o,因为 start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方。
- 接着再通过.rodata ALIGN(4) : {(.rodata)}
.data ALIGN(4) : { *(.data) }将rodata和data段放在代码段之后,而ALIGN(4) 这个关键字的作用是4字节对齐 - 最后通过 __bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;设置bss段的起始和终止地址数据以及将bss段存放在data段后面
补:
关于局部变量,全局变量和静态变量:
全局变量:定义在函数外的变量,作用范围是所有函数==当某函数的局部变量与全局变量同名时,全局变量在该函数中不起作用==
局部变量:在函数内部定义的变量,有效范围仅限于函数内部==形参也是局部变量==
静态变量:
局部静态变量:在局部变量前面加上static关键字,==使得局部变量由原来存储在栈内存变成存储在静态存储区==
- 生存周期:当static用来修饰局部变量的时候,他就改变了局部变量的存储位置.局部静态变量在离开作用域之后并没有被销毁,而是任驻留在内存中,直到程序结束例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void f(void)
{
static int b = 200; // 静态局部变量,退出整个程序之前不会释放
printf("%d\n", b);
b++; // b的值加1
}
int main(void)
{
f();
f(); // b只会初始化一次,重复调用函数 f(),会使静态局部变量 b 的值不断增大
return 0;
}
- 生存周期:当static用来修饰局部变量的时候,他就改变了局部变量的存储位置.局部静态变量在离开作用域之后并没有被销毁,而是任驻留在内存中,直到程序结束例如:
全局静态变量:在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。
- 内存中的位置:静态存储区
- 初始化:未经初始化的全局静态变量会被程序自动初始化为0
- 作用域:全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之处开始到文件结尾。
- 定义全局静态变量的好处:
- 不会被其他文件所访问,修改
- 其他文件中可以使用相同名字的变量,不会发生冲突。