变量的存储区域:
-
静态存储区域
-
动态存储区域。
变量的存储类型:
- 自动型(auto)
- 自动变量用关键字auto作为存储类别的申明,可以省略。
- 静态型(static)
- 寄存器型(register)
- 场景:C 语言中允许将局部变量的值放在CPU中的寄存器中,需要时直接从寄存器中读取数据,不必再到内存中读取数据,这种变量称为寄存器变量,用关键字“register”声明
- register,寄存器变量,告诉编译器它所声明的变量在程序中使用的频率非常高,请编译器尽量将此变量放在寄存器中,这样程序执行速度更快。但实际上编译器不一定这么做,可以忽略此选项。
- 注意:
- 只有auto修饰的局部变量和形式参数可以作为寄存器值
- 变量必须是CPU接受的类型,单个值,长度小于等于整数的长度
- 五路寄存器变量是否存放在寄存器中,它的地址都是不能访问的(取&)
- 自己理解: 这里的register 跟 volatile有点相反的意思;
- register仅仅限于局部变量或者形参。
- volatile表示变量随时可能被修改,且系统对实时性要求很高,请一定从内存中读取内容,不要直接拷贝寄存器中的数据,有可能数据老旧。常见的使用场合包括中断服务程序和嵌入式系统的寄存器相关操作。
- 外部型(extern)
局部变量的存储类型
- 自动变量
- 静态变量
- 场景:有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的内容空间不被释放,在下次调用时,该变量已有值,这时就可以声明局部变量为“静态局部变量”,用关键字“static”进行声明。
- 说明:局部静态变量是在静态存储区,在整个程序运行期间都不释放;局部静态变量是在程序编译过程中被赋值的,且只赋值一次,在程序运行时其初值已经确定,以后每次调用函数时不再赋值,而是保留上一次函数调用结束时的值;
- 寄存器变量
全局变量的存储方式
全局变量都是在静态存储区
- 静态型(static)
- 场景:在设计一个程序时,有时不希望某些全局变量被其它文件引用,这时可以用关键字static对全局变量进行声明
- 说明:无论是否对全局变量进行static声明,全局变量均是静态存储方式
- 外部型(extern)
- 场景:全局变量是在函数的外部定义,它的作用域是从变量的定义处开始到本程序文件的结束。如果在定义点之前的函数想要引用该变量,则应该在引用之前用extern对该变量进行声明,声明该变量为外部全局变量
- 说明:extern不能用来初始化变量,extern的作用是扩展全局变量的作用域,在系统编译遇到extern时,先在本文件中寻找全局变量的定义,如果找不到,在链接时从其它文件中全局变量的定义
存储区域
一个程序,是存在硬盘上的。当你运行它时,需要将它拷贝入内存中才能运行。在内存中分别存放在动态存储区(栈区,堆区),静态存储区,代码区(程序区)。
- 程序区:
- 存放程序语句
- 静态存储区:
- 常量字符串、常变量(const 修饰的变量)、静态变量static、全局变量
- 初始化的全局变量和静态变量在一块区域,
- 未初始化的全局变量和未初始化的静态变量在一块区域。 - 有的编译器会自动将这块区域清0
- 常量字符串、常变量(const 修饰的变量)、静态变量static、全局变量
- 动态存储区:
- 栈:
- 由编译器自动分配释放
- 存放函数的参数、局部变量、返回地址
- 堆:
- 一般由程序员分配释放, 若程序员不释放,程序结束时由系统回收
- 栈:
RAM、CPU、寄存器之间的关系
CPU由运算器、控制器、寄存器、以及实现它们之间联系的数据、控制及状态的总线构成。
CPU的运作原理可分为四个阶段:提取(Fetch)、解码(Decode)、执行(Execute)、写回(Writeback)。
我们都知道编译好的程序最终运行是在RAM中运行的,可是我很长一段时间都把RAM和CPU,寄存器搞混淆了。
-
cpu里包含有各种寄存器,ram并不是在cpu里的
-
ram保存着代码中的变量,常量,指令;cpu负责从ram中取出指令来运行,运行后的结果(比如加减乘除的结果)最初是保存在各种寄存器里的,然后再由寄存器通过指令把寄存器里的值保存到ram即内存中(比如arm的STR指令)
-
在arm架构中“数据从内存到CPU之间的移动只能通过LDR/STR指令来完成. 而MOV只能在寄存器之间移动数据,或者把立即数移动到寄存器中,并且数据的长度不能超过8位”。
其实程序的运行就是不断地从ram(内存)中取出指令或者数据,通过寄存器给CPU中的各种运算器运算后,再通过寄存器传出到ram中(ram中的数据就会不断地变化,反应到程序中就是变量的变化,比如数字的自加),而具体是把运算后的结果放到ram的哪里(地址)则由程序员在程序中定义的变量的地址决定的。
ELF文件格式
ELF文件格式:是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式。
通常gcc -o test test.c,生成的test文件就是ELF格式的,执行elf文件,则内核会使用加载器来解析elf文件并执行。
elf格式:
elf头 |
---|
.text |
.data |
.rodata |
.bss |
bss、data、rodata 是全局内存。
注意:全局变量和全局内存的区别
全局变量是放在全局内存中的,但反过来却未必成立。用static修饰的局部变量就是放在放全局内存的,它的作用域是局部的,但生命期是全局的
.bss:
-
未初始化的全局变量和静态变量(static)。
-
可读写, 非const变量
-
在程序执行之前BSS段会自动清0。
-
因为在程序执行之前清0, 所以bss区域的全局变量只占用运行空间,不占用文件空间。
注意:大多数操作系统,在加载程序时,会把所有的bss全局变量全部清零,无需要你手工去清零。 但为保证程序的可移植性,手工把这些变量初始化为0也是一个好习惯。
.data:
-
初始化过(非零)的全局变量、静态变量(static)
-
可读写, 非const变量
-
data区即占文件空间,又占用运行时内存空间。
.rodata
ro代表read only,即只读数据(const)。
- #define定义的常量 就在这个段内。
- const变量
个人理解:
堆栈都是在RAM中,而.data .rodata .bss .text 段指的是elf文件中的分类,而可执行程序在执行时早已加载到RAM中,而.data .rodata .bss .text 各个段加载的RAM地址不同,有的就被加载到特定的地址。
相关问题
- 对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO)中只存在一份拷贝。
- .rodata是在多个进程间是共享的,这可以提高空间利用率。
static value1;
static value2
void process()
{
int value3 = 0;
int *pvalue = malloc(64);
}
以上变量编译后存储位置?
value1 .bss
value2 .data
value3 栈
pvalue 栈,由malloc分配的64个字节存放在堆中。