小言_互联网的博客

第七章 存储类、作用域、生命周期、链接属性

199人阅读  评论(0)

一、概念概述

  1. 存储类
    存储类就是存储类型的位置,即变量在内存中的哪个地方存储,如:局部变量的存储类是栈;初始化为0或未初始化的全局变量的存储类是bss段;初始化非0的全局变量的存储类是.data段;

  2. 作用域
    作用域是指变量起作用的代码范围,一般的作用域是当前的代码块(当前变量定义之后{}之间的范围)作用域,因此变量的定义一般都放在当前代码块的最前面。

#include <stdio.h>

int a = 10;		//全局变量
int main(void)
{
	int a = 5;	//局部变量
	if(1)
	{
		a = 1;
		printf("%d\n", a);	//此时a = 1
	}
	printf("%d\n", a);		//此时a = 5
	return 0;
}
void func(void)
{
	printf("%d\n", a);		//此时a = 10
}
  1. 生命周期
    生命周期是指运行时从函数给这个参数分配内存空间,到该内存空间失效(无法访问或已重新分配给其他参数);

  2. 链接属性
    程序在生成可执行文件过程中,需要经过编译和链接,编译后的.o文件里面包含了很多符号、代码段、数据段、bss段等分段;符号就是参数名及函数名等,通过这些符号,将不同的.o文件链接在一起;

  3. linux C的内存映像
    (1)只读段:代码段:可执行的程序;只读数据段:只读数据,const修饰的数据(平台不同也不一定);
    (2)读写段:初始化不为0的全局变量及static修饰的局部变量;bss段:初始化未0或未初始化的全局变量及static修饰的局部变量;
    (3)堆:存放的变量取决于程序员自己的malloc和free;
    (4)文件映射区:程序运行是在内存中运行的,因此需要先将程序从flash中读取内存映射去,然后程序在内存映射区执行,将结果保存到flash中;
    (5)栈:局部变量、实参、返回值,都会保存在栈中;
    (6)内核映射区:将操作系统的内核映射到该区域,由于采用虚拟内存技术,因此每个进程都认位独享3G内存,0xC0000000以上的区域属于内核

  4. 裸机C程序与OS下的C程序的区别
    C代码的运行需要环境,裸机中的代码运行需要自己搭建运行环境,如清bss,设置栈,调用main函数等;而在OS的状态下操作系统已经将该环境搭建好,可以直接运行C代码。

二、存储类详解

  1. auto关键字
    auto关键字只有一个作用,就是修饰局部变量,成为自动局部变量。平时定义局部变量时,默认定义为自动局部变量,存储在栈中,即自动将auto关键字省略;

  2. static关键字
    static关键字有两个作用:
    (1)修饰全局变量,成为静态全局变量。静态全局变量与普通全局变量的区别在链接属性上,见下面链接属性详解;
    (2)修饰局部变量,成为静态局部变量。静态局部变量与普通局部变量的区别在存储类与生命周期上:静态的存储类是.data段或.bss段,普通的是栈;静态的生命周期和全局变量一样,**因此下一次执行某函数时,该函数内静态局部变量的值依然是上一次该函数执行的结果,**普通的是函数结束;
    (3)静态局部变量与普通全局变量的对比:二者存储类和生命周期相同,但作用域与链接属性不同:静态局部变量的作用域是代码块作用域,和普通局部变量相同,而全局变量是文件作用域,和函数相同;静态局部变量的链接属性是无链接属性,而全局变量是外链接属性;

  3. register关键字
    register关键字只有一个作用,就是编译器会将修饰的变量尽量分配在寄存器中,而非内存中。若变量分配在寄存器中,可以和普通变量同样使用,但可以极大地提高该变量的访问效率,一般用来修饰访问频率极高的变量。但修饰过的变量不一定真正的能分配到寄存器中,因为寄存器个数有限,可能还是会分配到内存中。

  4. extern关键字
    (1)extern关键字的作用是声明全局变量,可以跨文件引用同一个全局变量。当在a.c中定义了一个全局比那辆int a = 5;,在b.c文件中,就可以通过extern声明变量a,extern int a;从而使b.c文件也可以同a.c一样使用变量a;
    (2)由于编译时,是以源文件为单位,因此当a.c未声明变量a就直接使用的话,编译器会因为无法识别而报错;而声明后就是告知编译器将在链接时将该变量链接到相应的文件;

  5. volatile关键字
    (1)字面意思时易变的、可变的;在C中的作用就是提醒编译器不要优化某个参数或程序的运行,否则可能导致程序会出错:

int func()
{
	int a, b, c;
	//voiatile int a, b, c;
	a = 5;
	b = a;
	c = b;
	return c;
}

(2)当未加volatile关键字修饰a,b,c时,编译器为了提高程序运行效率,会优化程序,即程序只执行一行代码,让c == 5,直接return 5,但若在a = 5b = a之间因为突发中断、其他线程、硬件等的原因而修改了a的值,由于编译器的优化,返回值仍会是5;而添加volatile后,程序会逐条执行程序,即当a的值改变了后,在执行b = a时,会将a的新值赋给b,避免程序运行错误;

  1. restrict关键字
    (1)restrict只用来修饰指针参数,其他类型无意义。作用是告知编译器,该指针所指向的内容,若有修改必须居于该指针,其他方式无法修改该指针指向的内容,以此来提高编译器效率;
int func (int* x, int* y)	//int func(int * restrict x, int * restrict y)
{
	*x = 0;
	*y = 1;
	return *x;
}

(2)编译器为了保证正确,在return *x仍然会访问x,而不会return 0;而加了restrict关键字后,告知了编译器只能x才能访问x,其他方式无法修改*x,因此编译器可以放心大胆的直接renturn 0,提高了效率;

三、作用域详解

  1. 局部变量作用域
    局部变量的作用域是代码块(即大括号{}之间的部分)中,自己定义后的部分。由于函数中还可能包含判断if或for、while等语句也有大括号,因此代码块小于等于函数;代码块中局部变量定义之前并非其作用域。

  2. 全局变量和函数名作用域
    (1)全局变量和函数名的作用域是文件,即整个.c文件中,在该全局变量或函数定义了之后,都可以访问该变量或函数名。
    (2)全局变量和函数定义之前并非作用域,在函数定义之前访问可以通过声明,声明之后的范围也属于函数的作用域;

  3. 同名掩蔽原则
    (1)若同名文件作用域未交叠,则相互之间无影响;
    (2)若同名文件作用域有交叠,则在交叠的地方,作用域小的屏蔽作用域大的。

四、生命周期

  1. 局部变量
    局部变量存储在栈中,生命周期是临时的,函数的每次执行,都会分配,使用,释放一次该变量的内存,每次之间无关联;

  2. 堆变量
    堆内存空间是客观存在的,有操作系统维护。代码只是根据需要,申请一块空间使用然后释放,释放后堆内存仍然存在;堆变量的生命周期是在Malloc和free之间,以外的时候无法访问原来的数据;

  3. 数据段、bss段变量
    全局变量的生命周期,从程序开始执行到程序结束都存在。全局变量所占用的内存无法自己释放,因此若程序定义了过多全局变量,会一直占用大量的内存。

  4. 代码段、只读数据段
    代码段即程序执行的代码,其实就是函数,根据平台不同,有时也会存放const修饰的变量和字符串常量。其生命周期也是持续到整个程序结束。

五、链接属性

  1. C工程的组织架构
    (1)一个工程代码由众多.c文件与.h文件组成,程序生成就是通过编译和链接:编译将每个源文件编译生成.o文件,链接就是将多个由编译生成的.o文件链接在一起生成这个工程的可执行程序;
    (2)而链接的标志就是函数名、变量名等符号,如a.c中调用了b.中func1()函数,在链接时,连接器就会通过a.o中的func1函数名的符号,到b.o中找func1函数名的符号,找到后将b.o中的func1与a.o宏的func1链接在一起,程序运行时,b.o的func1函数名作为指针跳转到函数体中执行程序;

  2. 三种链接属性
    (1)外链接:外链接就是可以跨文件链接,如普通全局变量和函数,通过extern声明或.h头文件就可以在整个工程内使用;
    (2)内链接:内链接就是只能在当前所在的.c文件内链接,如static修饰的全局变量和函数,只能在本文件内被使用,而无法跨文件;
    (3)无链接:无链接就是本身不参与链接,如所有的局部变量,宏,inline函数;

  3. 全局变量与函数的同名冲突
    (1)由于全局变量与函数是外链接属性,因此同一个工程之中之间不能出现同名的全局变量/函数;
    (2)若两个.c文件中各自定义了一个全局变量的名字相同并都进行了初始化,系统认位定义了两个同名的全局变量;但若有一个或两个都未初始化,则系统会认位只是定义了一个全局变量,而将另一个.c文件中未初始化的全局变量作为声明,此时并不会报错误;
    (3)为了避免同名冲突,可以通过static修饰全局变量/函数,更改其链接属性为内链接,但所有该命名的全局变量/函数都无法进行外链接;


转载:https://blog.csdn.net/weixin_45093118/article/details/101438076
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场