存储类、作用域、生命周期、链接属性的笔记

存储类:
描述C语言变量在存储在内存中的什么地方。
内存的管理方法有多种:栈,堆,数据段(.data)(显式初始化不为零的全局变量),bss段(显式初始化为0的全局变量),代码段。
一个变量的存储类属性就是描述这个变量存储在内存的哪个段中。


作用域:
就是这个变量起作用的代码块范围,就是这个变量在哪个代码块有效,在哪个代码块无效。
代码段分为多个代码块。在变量同名的情况下,留下小范围的代码块中的变量。


生命周期:
一个变量的诞生到结束,什么时候诞生呢,运行时在内存中分配给这个变量内存空间,这个变量就诞生了。
什么时候死亡呢,程序在内存中运行时,收回这段内存空间,此后这个变量不能在访问这块内存空间。或者访问这个内存空间已经和别的变量相关了。

链接属性:
C语言的源代码到升成可执行程序经历了一些步骤:源文件.c 预处理.i 编译.S 汇编.o 链接.elf可执行程序、
简单来讲究是编译和链接。编译阶段就是把源代码搞成目标文件.o,目标文件中有很多符号,代码段(函数体),数据段,bss段等分段。 0000054b T add 符号就是函数中的变量名,函数名等。函数名就是为了和我们的代码段进行绑定。运行时,我们的变量名,函数名能和我们的内存相对应起来,就是靠符号进行链接的。
符号和我们内存中的不同段对应的,我们在一个函数中调用了一个函数时,我们就知道了这个函数的符号,就会找到这个符号对应的内存中的那个段,完了将他链接进来。


linux C程序下的内存映像:
c语言程序,在linux下的内存里运行,是什么样的。
一、代码段、只读数据段
在内存中,代码段就是存放程序的文本段(函数).text,
只读数据段:const修饰的常量有可能就是放在这段内存空间的,在程序运行时是不可改的。


二、数据段,bss段
在内存中,数据段时存放显示初始化不为零的全局变量,和显示初始化为非零的static局部变量。
bss段存,显示初始化为零或未显示初始化的全局变量,和显示初始化为零的和未显示初始化的static局部变量。

三、堆
在C语言中,C语言不会自动向堆中存放东西的。

四、文件映射区
就是某个程序进程打开了文件后,从硬盘中将文件映射到内存中的文件映射区,这样我们就可以在内存中去操作这个文件了,当我们写完这个文件,或者对这个文件的操作执行完毕后,我们将这个文件在内存中在保存到我们的硬盘中。

五、栈
栈内存区:用来存放局部变量的,还有就是函数传参的过程也会用到栈内存,这也就是为什么递归函数是耗费栈内存的。因为他相当于不断的在调用函数,还携带者参数。参数赋值给一个中间变量。

六、内核映射区

在linux中。0xc0000000以上都是属于内核的映射区,将来操作系统内核的程序就会在这里运行。
对于每一个应用进程来说,它都以为自己独享除了操作系统(OS)内核的空间以外的那些内存空间(3G),对于32
位的操作系统来说,内存空间应该是4G。
也就是说对于每一个应用程序而言,它都认为自己是独享这些内存空间的,除了操作系统内核的那部分内存空间,它自己和操作系统之间有一道门(API接口),通过这道门来跟操作系统内核进行打交道。
而对于每一个应用进程来说,他们看到的都是同一个操作系统,就是因为运用了虚拟地址技术,让每一个进程都有0-3G的内存空间来使用。

OS下和裸机下的C运行的差异:
在裸机的C下,我们需要给C构建出来一个C运行时环境或者叫加载运行代码,清BSS段,给全局变量赋值。而在OS下,操作系统,别人已经为我们构建好了这一C运行时环境了,这些代码是在重定位期间(运行地址中的内容加载链接地址中的内容)完成的,包括了清BSS段。




存储类相关的关键字:
auto: 在C语言中,auto只有一个作用,就是修饰局部变量的。修饰的局部变量,表示这个局部变量是自动的局部变量,自动的局部变量是分配在栈上的。分配在栈上,说明如果不初始化这个自动局部变量,那么他的值就是随机的。我平时定义的局部变量就是auto的,自动局部变量。只是我们平时定义局部变量的时候省略了auto关键字。
static:
在C语言中,static关键字有两种用法,这两种用法彼此完全没有关联,是完全两种不同的概念,彼此完全独立。
static 的第一种用法:就是用来修饰局部变量,形成静态的局部变量。改变存储类,生命周期。
这个静态局部变量如果初始化为非零就是分配在数据段上,如果初始化为0就是分配在bss段上的。静态局部变量和非静态局部变量的区别在于,本质区别是存储类不同,存储类不同就觉得了很多方面不同,比如说生命周期不同,栈上的生命周期是临时的。数据段上的,bss段上的生命周期是全局的生命周期。

静态局部变量和全局变量的区别在于,存储类(数据段或者bss段)和生命周期是一样的。静态局部变量的作用域是代码块作用域,链接属性是无链接。
全局变量的作用域是文件作用域,链接属性是外链接属性。
static的第二种用法:是修饰全局变量的,形成静态的全局变量。这个静态全局变量和非静态全局变量的区别是在链接属性上。一定要搞清楚,static的这两种用法彼此没有任何关联。是完全独立的两种用法。


register关键字:
用来修饰变量的,让编译器尽量将这个变量放在寄存器中,因为在我们这个哈佛架构体系中,CPU要想访问到内存,首先要经过寄存器,在经过高速缓冲区icache,在到内存,如果我们把变量修饰成register的,编译器就会尽量把这个变量放在寄存器中,这样CPU就直接可以通过寄存器就访问到了这个变量,那么就可以提高程序的运行效率速度,但是我们要慎用,因为寄存器有限,一般在我们把整个程序写好,在必要的时候,我在把个别的变量变成register的。

extern关键字:
声明全局变量的,如果在a.c中定义的全局变量我们要在b.c中用到,就需要在b.c中去声明这个全局变量,让我们可以在b.c中去用,在链接时,链接器就会到a.c中找到这个全局变量拿给我们b.c用。定义本身就有声明,但是声明本身是没有定义的,所以我们一个文件中,不会去定义一个全局变量,还要将这全局变量在声明一次,因为定义就包含了声明。

volatile关键字:
编译器不知道这个变量的值是否会改变时,适合用到volatile关键字,在中断的Isr中,多线程中,硬件造成的变量值的改变(寄存器中的变量),就是这个变量是易变的,可变的,可以在编译之外被改变,编译之内被改变就是在编译的过程中,就将这个变量的值改变了,但是在编译之外的话,编译器是不知道这个变量将来是否会被改变的,在这种情况下适合用volatile关键字去修饰这个变量。中断Isr中的变量,多线程中共用的变量,硬件会更改的变量,这三种情况适合用volatile去修饰这个变量。
编译器知道volatile修饰的变量后,就不会对这个变量的访问作优化,不会带来一些错误。


restrict关键字:
这个关键字在C99中是支持的,C89不支持,在gcc中是支持的,他用来修饰指针变量的,告诉这个编译器,将来这个这指针指向的内容,只有可能通过这个指针去改,不会通过其他的指针去改变这个指针指向的这个变量的值,可以让编译器做更好的优化,编译成更好效率更高的汇编代码,但是一般很少用到这个关键字。

typedef关键字:
在C语言中这个关键字是被划分在存储类中的一种关键字,但是他跟存储类没有关系。

const关键字




作用域:
局部变量的作用域是代码块作用域,代码块就是大括号{}括起来的部分。且是在这个局部变量定义之后的范围。

全局变量的作用域是文件作用域,就是在一个.c文件中的,全局的作用域。但也要是在定义或声明之后的全局。

函数名的作用域和全局变量一样,也是文件作用,也是在一个.c文件中,全局的作用域。但也要是在定义或声明之后的全局。


在C89标准的编译器中(目前很多编译器还在延续使用C89的标准),定义的变量必须放在最前面,在定义之前是不能有执行代码的。
在C99的标准中(gcc就支持c99的标准)变量定义之前是可以有执行代码的。


同名变量的掩蔽规则
同名变量的作用域没有交叠的时候,不会出错,同名变量的作用域有交叠的时候是有同名变量的掩蔽规则的。
作用域小的把作用域大的变量掩蔽掉。就是屏蔽掉,县官不如现管。


变量的生命周期:
栈变量的生命周期是临时的。

堆变量的生命周期:
malloc申请完堆内存后,在堆内存中定义变量之后的范围,到free掉堆内存之前生存。


数据段和bss段的生命周期:
全局变量的生命周期,程序开始执行,到程序结束运行,在程序开始时就会给这个全局变量分配内存在数据段上或者bss段上,一直到整个程序的结束,所以我们在写程序时应该尽量的少用全局变量,因为全局变量多了会耗费很多的内存。


代码段、只读段的生命周期:
其实就是代码的生命周期,函数、生命周期是永久的,一般我们不太关注代码的生命周期。

但是有时候放在代码段的不只是代码,还有可能是const类型的常量,还有字符串常量。const类型的常量,字符串常量有的时候是放在只读数据段rodata的,有的时候放在代码段。取决于平台,一般单片机中的这些常量是放在代码段的,在GCC中,这些常量放的地方两者都有可能。


链接属性:
链接就是将编译后得到的二进制目标文件.o(包含变量和函数),里面的.o代码段大家都是独立的,链接就是将这些独立的.o二进制机器码按照一定的规律和格式进行链接起来,形成一个有用的可执行程序。

编译是以文件为单位的,链接是以工程为单位的。




三种链接属性:外链接、内链接、无链接
外链接:就是外部的链接属性,那个东西可以在整个程序范围中进行链接,譬如:普通的函数和全局变量就是外链接。
内链接:就是内部链接属性,这个家伙将来链接是在自己的c文件(.o文件内部)进行链接的。譬如:static修饰的函数和全局变量就是内链接属性的,因为只能在自己的文件中被调用,不能被外部调用。

无链接:就是这个符号没有链接属性,在链接的时候跟这个符号没有任何关系,就叫做无链接。所有的局部变量就是无链接属性的,包括(auto的,static的局部变量都是无连接属性的。)



函数和全局变量同名的冲突解决问题:
在C语言中,对于不同文件.c中的函数和全局变量的同名解决方法是,用链接属性解决的,如果是只在一个文件中调用或引用的,不会被其他文件引用的函数和全局变量,我们就用static修饰他们,使他们成为内链接属性的,这样外面的文件用不会调用到他们了,从而解决了同名的问题。
虽然这种方法在一定程度上解决了这个问题,但是没有在根本上解决这个问题,因为如果你这个函数是在b.c中的,可是你就只是想在a.c中去用这个函数,而在其他成千上百个.c文件中却不去用他,你还是不能把这个函数用static修饰成内链接的,这样这个函数就是外链接属性的了,难免其他的c文件中也会出现同名的情况,。更何况在一个很大的工程中,即使你修饰了很多内链接属性的全局变量和函数,但是你还是会有很多的全局变量和函数要是外链接属性的,所以这还是存在着很大程度上的同名的危险性的。
所以这就表明,如果用C语言很难完成了一个很大的工程。



static的第二种用法:修饰全局变量和函数
static修饰的函数和全局变量就是将全局变量和函数的链接属性由外部的链接属性变成了内链接属性,这样可以解决同名的问题。

static的第一种用法:修饰局部变量
static修饰的局部变量就是改变了变量的存储类,将原先存储在栈上的变量,变成了存储在数据段或者bss段上的变量了。因而生命周期也不一样了。链接属性是无链接属性。因为所有的局部变量链接属性都是无链接属性。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值