006 C语言基础:C存储类

一:概述

存储类用来定义C程序中变量、函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。

 

二:分类

存储类可以分为:
	auto
	register
	static
	extern

 

三:auto存储类

auto存储类是所有局部变量的默认存储类,下面的实例定义了两个带有相同存储类的变量,可以理解为这两行等价,auto只能用在函数内,即auto只能修饰局部变量。

{
	int mount;
	auto int mount2;
}

 

四:register存储类

先看一段非人类的解释:
register存储类用于定义存储在寄存器中而不是RAM中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个字节),且不能对它应用一元的’&'运算符(因为它没有内存位置)

是不是一脸懵逼?要明白上面的话,首先要明白,什么是寄存器?什么是RAM?什么是一元的’&'运算符?

先看什么是RAM?
Random-Access Memory随机储存器 ,就是电脑的内存条。用于存放动态数据。(也叫运行内存)系统运行的时候,需要把操作系统从硬盘(ROM)中读取出来,放在RAM中运行。

再看看什么是寄存器?
百度百科的解释:
寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。其实寄存器就是一种常用的时序逻辑电路,但这种时序逻辑电路只包含存储电路。寄存器的存储电路是由锁存器或触发器构成的,因为一个锁存器或触发器能存储1位二进制数,所以由N个锁存器或触发器可以构成N位寄存器。寄存器是中央处理器内的组成部分。寄存器是有限存储容量的高速存储部件,它们可用来暂存指令、数据和位址。

还是一脸懵逼对不对?

然后我在网上看到一位大佬的这么一句话,很方便理解。这里引用下:

知道什么是寄存器?那见过太监没有?没有?其实我也没有。没见过不要紧,见过就麻烦大了。_,大家都看过古装戏,那些皇帝们要阅读奏章的时候,大臣总是先将奏章交给皇帝旁边的小太监,
小太监呢再交给皇帝同志处理。这个小太监只是个中转站,并无别的功能。那我们再联想到我们的CPU。CPU 不就是我们的皇帝同志么?大臣就相当于我们的内存(RAM),数据从他这拿出来。
那小太监就是我们的寄存器了(这里先不考虑CPU 的高速缓存区)。数据从内存里拿出来先放到寄存器,然后CPU 再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,
CPU 不直接和内存打交道。这里要说明的一点是:小太监是主动的从大臣手里接过奏章,然后主动的交给皇帝同志,但寄存器没这么自觉,它从不主动干什么事。一个皇帝可能有好些小太监,
那么一个CPU 也可以有很多寄存器,不同型号的CPU 拥有寄存器的数量不一样。
为啥要这么麻烦啊?速度!就是因为速度。寄存器其实就是一块一块小的存储空间,只不过其存取速度要比内存快得多。进水楼台先得月嘛,它离CPU 很近,CPU 一伸手就拿到数据了,
比在那么大的一块内存里去寻找某个地址上的数据是不是快多了?那有人问既然它速度那么快,那我们的内存硬盘都改成寄存器得了呗。我要说的是:你真有钱!

最后看看’&'运算符
简单地说就是取地址符。
什么是取地址符?顾名思义,就是获取当前变量的内存地址,想要获得那个变量的地址,就用&后面跟上那个变量。

vim quzhifu.c 写入如下内容:
	#include <stdio.h>              // 头文件
	char a;                         // 定义变量
	short b;
	int c;
	void main(){                    // 程序入口
		a = 1;                      // 给变量赋值
		b = 2;
		c = 3;

		printf("%x %x %x \n", &a, &b, &c);
	}
Kali Linux运行结果如下:
	┌──(root💀kali)-[~/Desktop/c_test]
	└─# ./quzhifu
	357ba034 357ba036 357ba038 

现在返回来继续看register存储类:

1、验证了register不能对它应用'&'运算符:
	vim register.c 写入如下内容:
		#include <stdio.h>
		int main(){
			register int a;
			printf("%x \n", &a);
			return 0;
		}
	编译时会报错,这就验证了,register不能对它应用'&'运算符。
		┌──(root💀kali)-[~/Desktop/c_test]
		└─# gcc register.c -o register
		register.c: In function ‘main’:
		register.c:5:5: error: address of register variable ‘a’ requested
			5 |     printf("%x \n ", &a);
			  |     ^~~~~~
2、只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量、结构体、共用体内部变量)不行。特别地,静态局部变量不能定义为寄存器变量。
	struct _STRUCT_NAME_
	{
		register int a;	//错误
	};

	union _UNION_NAME_
	{
		register int b;	//错误
	};

	register int c; //错误

	void main()
	{
		register static int d = 0; //错误
	}

 

五:static存储类

static关键字不仅可以用来修饰变量,还可以用来修饰函数。在使用static关键字修饰变量时,我们称此变量为静态变量。
静态变量的存储方式与全局变量一样,都是静态存储方式。但这里需要特别说明的是,静态变量属于静态存储方式,属于静态存储方式的变量却不一定就是静态变量。

例如,全局变量虽然属于静态存储方式,但并不是静态变量,必须由static加以定义后才能成为静态全局变量。

隐藏与隔离的作用:
全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,全局变量在各个源文件中都是有效的。如果我们希望全局变量仅限于在本源文件中使用,在其他源文件中不能引用,也就是说限制其作用域只在定义该变量的源文件内有效,而在同一源程序的其他源文件中不能使用。这时,就可以通过在全局变量之前加上关键字 static 来实现,使全局变量被定义成为一个静态全局变量。这样就可以避免在其他源文件中引起的错误。也就起到了对其他源文件进行隐藏与隔离错误的作用,有利于模块化程序设计。

保持变量内容的持久性
有时候,我们希望函数中局部变量的值在函数调用结束之后不会消失,而仍然保留其原值。即它所占用的存储单元不释放,在下一次调用该函数时,其局部变量的值仍然存在,也就是上一次函数调用结束时的值。这时候,我们就应该将该局部变量用关键字 static 声明为“静态局部变量”。当将局部变量声明为静态局部变量的时候,也就改变了局部变量的存储位置,即从原来的栈中存放改为静态存储区存放。这让它看起来很像全局变量,其实静态局部变量与全局变量的主要区别就在于可见性,静态局部变量只在其被声明的代码块中是可见的。

实例验证一下:

实例:
	┌──(root💀kali)-[~/Desktop/c_test]
	└─# vim static.c
		#include <stdio.h>
		void func(){
			static int i = 1;
			i += 1;
			printf("%d \n", i);
		}
		int main(){
			func();
			func();
			return 0;
		}
	┌──(root💀kali)-[~/Desktop/c_test]
	└─# gcc static.c -o static      																					
	┌──(root💀kali)-[~/Desktop/c_test]
	└─# ./static   
	2 
	3 
结论:
	static int i = 1;则输出2 3
	int i = 1;则输出2 2
	所以说:static所占用的存储单元不释放,在下一次调用该函数时,其局部变量的值仍然存在,也就是上一次函数调用结束时的值。

 

六:extern存储类

extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 ‘extern’ 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。当有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。extern 是用来在另一个文件中声明一个全局变量或函数。

extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候。

实例说明:
	在同一目录下,创建两个文件。
	第一个文件:extern_main.c
		#include <stdio.h>
		int count;
		extern void write_extern();
		main(){
			count = 5;
			write_extern();
		}
	第二个文件:extern_support.c
		#include <stdio.h>
		extern int count;
		void write_exteren(){
			printf("count is %d \n", count);
		}
	在这里,第二个文件中的 extern 关键字用于声明已经在第一个文件 main.c 中定义的 count。现在 ,编译这两个文件,如下所示:
		┌──(root💀kali)-[~/Desktop/c_test]
		└─# gcc extern_main.c extern_support.c -o extern
		extern_main.c:5:1: warning: return type defaults to ‘int[-Wimplicit-int]
			5 | main(){
			  | ^~~~
																									   
		┌──(root💀kali)-[~/Desktop/c_test]
		└─# ./extern                                    
		count is 5 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值