从内存角度解释C语言变量属性

1.存储类
描述C语言变量存储在内存中的地方。
内存有多种管理方法:栈,堆,数据段,bss段,.text段(代码段)
(1)局部变量分配在栈上。
(2)显示初始化为非0的全局变量,分配在数据段
(3)没有初始化(默认为0)的全局变量分配在bss段

2.作用域
描述这个变量起作用的范围

3.生命周期
什么时候诞生(运行时分配内存空间给这个变量)
什么时候死亡(运行时收回这个内存空间,或者访问这个内存地址已经和这个变量无关了)
4.链接属性
从源代码到最终可执行程序经历的过程:编译,链接
编译:就是把源代码搞成.o目标文件,目标文件里面有很多符号和代码段,数据段,bss段等分段,已经处理好了
怎么知道放哪个段?通过符号识别
符号:就是变成中的变量名,函数名等,运行时变量名,函数名能够和相应的内存对应起来,靠符号来在做链接的。
.o的目标文件链接生成最终可执行程序,其实就是把符号和相对应的段给链接起来。包含有内链接属性,外链接属性,无链接属性

linux下C程序的内存映像:
<0x0804 8000 保留区域 就是操作系统自己保留的一些区域,内部程序运行的

=0x0804 8000 .init,.text(代码段),.redata(只读段) 从可执行程序加载
.data,.bss(读写段) 从可执行文件中加载
heap(运行时堆)通过brk/sbrk系统调用扩大堆,向上增长
空闲内存
<0x4000 0000
=0x4000 0000 文件映射区
空闲内存
<0xc000 0000 stack(用户栈) ESP指向栈顶
=0xc000 0000 内核虚拟存储器 用户代码不可见区域
代码段,只读数据段
(1)对应着程序中的代码。
(2)只读数据段就是在程序运行期间只能读不能写的数据,const修饰的常量就有可能存放在只读数据段(但不同平台不一样)
数据段,bss段
(1)数据段:1.显示初始化非0的全局变量;2.显示初始化非0的static局部变量
(2)bss段:.显示初始化为0(默认为0)或者未初始化的全局变量;2.显示初始化为0的或者未初始化static局部变量
堆(如果后面给该变量赋值了,)
(1)C语言不会自动向堆中存放东西,需要手工操作。
文件映射区
(1)文件映射区就是进程打开了文件后,将这个文件的内容从硬盘督导进程的文件映射区,以后就直接在内存中操作这个文件,都写完以后在保存时将内存中的文件写到硬盘中去

(1)1.局部变量分配在栈上;2.函数调用传参过程
内核映射区
(1)操作系统内核程序映射到这个区域了
(2)对于linux中的进程来说,都以为这个系统中只有它自己和内核程序,认为0xc000 0000以下都是它自己的活动空间,以上都是内核程序空间
(3)
OS下和裸机下C程序加载执行的差异

存储类关键字:
auto:
只有一个作用,修饰局部变量,表示这个局部变量是自动局部变量,自动局部变量分配在栈上,说明它不初始化那么值就是随机的,也说明了它的生命周期。
平时都用到,只是省略了。

static:
在C语言中有两种用法,且彼此没有任何关联。
1.用来修饰局部变量,形成静态局部变量
静态局部变量和非静态局部变量的区别:
本质:存储类不同(例如决定了生命周期不同等)
非静态局部变量分配在栈上,而静态局部变量分配在数据段/bss段上。
2.用来修饰全局变量,形成静态全局变量。
目的:使全局变量由外链接变成内链接,只作用域当前c文件,避免重名,
这种两种用法区别在于链接属性上不同。
分析:
1.静态局部变量在存储类方面和全局变量一样(存储类)
2.静态局部变量在生命周期方面和局部变量一样。(生命周期)
3.静态局部变量和全局变量的区别:作用域,链接属性。
4.静态局部变量的作用域是代码块(和普通局部变量一样),链接属性是无连接。
5.全局变量作用是(文件作用域和函数是一样的),链接属性是外连接。
6.静态全局变量的链接属性是内链接,虽然是全局变量,但只作用于当前c文件。

register:
1.编译器会尽量将变量它分配在寄存器中(读写效率高很多,但编译器只是承诺将register修饰的变量放在寄存器,但不保证一定放在寄存器,因为寄存器数量有限,所以满了会分配到内存)
使用场景:变量被反复高频率的使用,所以register是一种提升程序运行效率,但一般用得很少
cpu从内存拿数据(内存->cache缓存->寄存器->cpu)

extern:
extern主要用来声明全局变量,告诉编译器我这个变量在别的文件中,将来在链接的时候链接器会在别的.o文件中找到这个同名变量。
定义包含声明。
volatile:
表示这个变量可以被编译器之外的东西改变。
编译器之内:指变量的值的改变是代码的作用。
编译器之外:不是代码造成的,不是当前代码造成的,编译器在编译器当前代码无法预知。
比如说别的线程改了该变量的值,该变量为线程共用。
有什么作用?
编译在遇到volatile修饰的变量不会对该变量的访问进行优化,就不会出现错误。
什么叫编译器优化:
例如:
int a,b,c;
a=3;
b=a;
c=b;
//无优化:内存读3次,即将3读到寄存器,然后写给a,然后再读a,写到b,再读b,写到a
//编译器优化:内存读一次(只用读一次3到寄存器,然后分别写给abc),写3次,提升程序效率
但有时候又必须要加volatile,避免编译器优化,因为编译器优化会导致执行上的错误,而且这种错误很难被发现。
总结:vllatitle避免错误优化,但使用后会降低效率,正确区分,该加的加,不该加的不加。

restrict:
在C99以后才支持,gcc支持。
1.restrict也是和编译器的行为特征有关
2.只用来修饰指针,修饰普通变量没有意义。
3.作用和volatile相反,加上以后是让编译器优化,不加则不优化(也不是说编译器就不优化了,而是某种情况下编译器不敢优化)
例子:
int f(int* a,int* b){
*a = 0;
*b = 1;
return *a;(原本可以直接返回0的,但是编译器不敢做优化,所以要加restric)
}

int f(int* restric a,int* restric b){
*a = 0;
*b = 1;
return *a;(此时返回值在编译后是直接写的0)
}

作用域详解:
1.局部变量的代码块作用域
2.函数名和全局变量的文件作用域

变量的生命周期:
1.变量生命周期的意义:
(1)了解变量的行为特征
2.栈变量的生命周期
(1)生命周期为临时的,临时的意思:代码执行过程中按照需要去创建,使用,消亡。
例如一个函数内的局部变量,在函数返回的时候消亡。
换个角度看
一个函数内的局部变量为什么在函数外不能使用,或者是无连接的?
因为别人在使用它的时候它已经消亡了
局部变量为什么分配在栈上,或者说局部变量为什么是临时的生命周期?
因为栈就是这样的特点,临时的
3.堆变量的生命周期
堆是由操作系统维护,程序只能去申请,然后使用后释放。
生命周期从申请时诞生,然后使用,直到free消亡。
4.数据段,bss段变量的生命周期
(1)全局变量的生命周期是永久的,永久的意思是程序开始诞生,程序终止消亡。
(2)全局变量所占用的内存是不能被程序自己释放的。
5.代码段,只读段的生命周期
(1)其实就是我们的代码,其实就是函数,它的生命周期是永久的。
(2)有时候放在代码段的不只是代码,还有const类型的常量,还有字符串常量(看平台,有时候放在只读段)

1.符号保存在哪?在代码段中吗?然后通过链接到相应的内存段?
2.如果bss段中的变量在后面被赋值了,它会调到数据段吗?还是只是符号和内存段的链接变化了?

链接属性:
程序由多个.c文件和.h文件组成
程序的生成过程就是编译+链接。
编译是将函数/变量等变成.o的二进制机器码。
链接是为了将各个独立的二进制函数链接起来形成一个整体的二进制可执行程序。
编译时以文件为单位,而链接以工程为单位。
先编译后链接

为啥不声明函数或者变量就无法使用,其中一个原因:
不声明就无法单个文件进行编译,编译器就不知道你这里引用的东西是啥。

链接属性:外链接,内链接,无链接
1.外链接:普通的函数,全局变量,其实就是整个程序范围内,跨文件
2.内链接:只能在当前c文件内部范围内进行链接,包括(static修饰的函数或者全局变量)
3.无链接:这个符号不参与链接,所有的局部变量(不管auto,static)都是无链接的

因为C语言没有命名空间namespace,所以使用了3种链接属性的方法,来解决全局变量同名问题。
使用static修饰,让全局变量/函数成为内链接属性,使其中一个或2个为内链接属性就行了。

总结:
普通变量分配在栈上,未初始化时值是随机的,变量分配到的地址也是栈范围内随机的,作用域为代码块,生命周期是临时的,链接属性为无连接。
静态局部变量分配在数据段(初始化非0)或者bss段(未初始化或初始化为0),数据段和bss本质无区别,只是优化以后的结果,数据段是需要将值赋值到数据段,
bss段不需要,因为都是0,程序就可以更快的加载,同时变量地址在加载器加载后就确定了,且不会变,作用域为代码段,生命周期是永久的,连接属性为无连接。
静态全局变量和普通全局变量唯一的差别:
将链接属性由外链接变成内链接,是为了解决全局变量重名问题,所以在能确定其它文件不会用到的全局变量一定要static修饰
宏和inline函数(因为会直接展开在代码中)的链接属性为无连接
存储类决定生命周期,作用域决定链接属性

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值