变量的存储类别

在C语言指针中提到了每一个变量有两个物理意义,一个是它本身的内容,另一个是变量的地址。同时每个变量有两个属性,变量的数据类型和变量的存储类别。 一般在定义一个变量或数组时首先定义的是数据类型,但实际上还应该定义它的存储类别。变量的存储类别决定了变量的生存期及给它分配在哪个存储区。
变量定义的一般形式为:

存储类别标识符 数据类型说明符 变量1,变量2,…,变量n;

C语言中共有4种存储类别标识符:auto(自动的),static(静态的),register(寄存器的),extern(外部的)

在了解变量存储类别之前需要先搞清楚几个概念:
1.变量生存期:是指变量在内存中占据存储空间的时间
2.存储空间的分类:程序代码区、静态存储区、动态存储区
存放在不同存储空间内的变量的生存期不同。分配在静态存储区的变量在程序运行期间始终占据存储空间;分配在动态存储区或CPU寄存器中的变量,只在程序运行的某段时间占据存储空间。
同时还要区分作用域和生存期。

自动变量

在一般情况下使用最多的变量就是自动变量。自动变量用关键字auto作存储类别标识符。函数或分程序内定义的变量(包括形参)可以定义为自动变量,可以显示定义也可以隐式定义。如不存在指定存储类别,即隐式定义为自动变量。而一般在使用时,无特殊需要都不会定义存储类别,所以大多数的变量为自动变量。

auto int x,y;
int x,y; 

如上两行代码所示,都是定义了两个整型的自动变量,区别在于第一行是显式定义,第二行是隐式定义。
调用函数或执行分程序时,在动态存储区为自动变量分配存储单元,函数或分程序执行结束,所占内存空间即刻释放。定义变量时若没有给变量赋值,变量的初值不确定;如果赋初值,则每次函数被调用式执行一次赋初值操作。
在函数或分程序执行期间,自动变量占据存储单元。函数调用或分程序执行结束,所占存储单元即刻被释放。自动变量的作用域是它所在函数内或分程序内。

静态变量

静态变量用static作存储类别标识符。静态变量包括两种,一种是局部静态变量(内部静态变量),另一种是全局静态变量(外部静态变量)。

static int a;/*全局静态变量*/
f1()
{
	static int b=2;/*局部静态变量*/
	...
}

如上所示,a是全局静态变量,b是局部静态变量。其区别主要在变量定义的位置,若静态变量定义在所有函数外部则称为全局静态变量,作用域为全局;若静态变量定义在某个函数的内部则称为局部静态变量,作用域只在该函数内。
编译时,系统在内存的静态存储区为静态变量分配存储空间,程序运行结束释放其所占的存储空间。若定义时没有赋初值,在编译时,系统自动赋初值为0;若赋初值,则仅在编译时赋初值一次,程序运行后不再给变量赋初值。 对于局部静态变量,其存储单元中保留上次调用结束时的值。静态变量的生存期是整个程序的执行期间。
因为局部静态变量在程序执行期间始终保持在内存中,所以变量中的数据在函数调用结束后仍然存在。当再一次调用其所占函数时,该变量的值继续有效(该值为上一次函数调用保留的值)。局部静态变量的作用域与自动变量一样,只在它所在的函数或分程序内有效。全局静态变量的作用域是从定义处开始到源文件结束。

外部变量

在函数外部定义的变量若没有用static说明,则是外部变量。外部变量只能隐式定义为extern类别,不能显式定义,但在需要时可以用关键字extern声名其为外部变量类别。
编译时,系统把外部变量分配在静态存储区,程序运行结束释放该存储单元。若定义变量时未对外部变量赋初值,在编译时,系统自动赋初值为0。 外部变量的生存期是整个程序的执行期间。外部变量的作用域是从定义处开始到源文件结束。
既然外部变量只能隐式定义,那么在什么时候需要用到extern声名呢,例如

#include <stdio.h>
int x=2,y=2;/*定义外部变量x,y*/
float f1()
{
	extren char c1,c2;/*对外部变量c1,c2声名*/
	scanf("%c%c",&c1,c2);
}
char c1,c2;/*定义外部变量c1,c2*/
void main()
{
	int m,n;
	f1();
	printf("%c+%c=%d\n",c1,c2,x+y);
}

在上面的程序中,外部变量c1,c2是在f1函数之后定义的,它可以在其后的main函数中引用。若想在f1函数中引用它,必须要在f1函数中进行外部变量声明,使c1,c2的作用域扩展到f1函数中。
在多文件程序结构中,如果一个文件中的函数需要用到使用其他文件中定义的全局变量,也可以使用extern关键字声明所要使用的全局变量。

/*file1.c文件*/
#include <stdio.h>
int i;
void main()
{
	...
}
/*file2.c文件*/
#include <stdio.h>
extern int i;
void f1()
{
	...
}

如上两端代码所示,它们分别是两个不同的文件file1.c和file2.c。
在file1.c文件中定义了一个外部变量i,它在main函数中有效,而file2.c文件的开头有一个对外部变量i的声名语句,这使得file1.c文件中定义的外部变量i的作用域扩展到file2.c文件中,因而在file.2文件中的f1函数可以引用外部变量i。
对外部变量声名时,系统不分配存储空间,只是让编译系统知道该变量是一个已经定义过的外部变量,与函数声名类似。

寄存器变量

寄存器变量的值保存在CPU的寄存器中。由于CPU中寄存器的读/写速度快,因此,可以将程序中使用频率高的变量定义为寄存器变量,这样可以提高程序的执行速度。
寄存器变量只能是char、int和指针类型的变量。寄存器变量的生存期与自动变量相同。但随着计算机硬件性能的提高,寄存器变量使用的比较少。

总结

1.定义方式:
(1)自动变量:可以显式定义也可以隐式定义
(2)静态变量:显式定义
(3)外部变量:只能隐式定义,在需要时可以用关键字extern声名其为外部变量类别
如何识别3种变量:在函数内部定义没有存储类别标识符的或带有auto标识符变量的为自动变量;在函数外部定义没有static标识符的或带有extern标识符的变量是外部变量;有static标识符的是静态变量。
2.存储空间:
(1)自动变量:动态存储区
(2)静态变量:静态存储区
(3)外部变量:静态存储区
3.赋初值:
(1)自动变量:定义变量时若没有给变量赋值,变量的初值不确定;如果赋初值,则每次函数被调用式执行一次赋初值操作。
(2)静态变量:若定义时没有赋初值,在编译时,系统自动赋初值为0;若赋初值,则仅在编译时赋初值一次,程序运行后不再给变量赋初值。
(3)外部变量:若定义变量时未对外部变量赋初值,在编译时,系统自动赋初值为0。
4.生存期:
(1)自动变量:调用函数(执行分程序)开始到函数(分程序)结束。
(2)静态变量:整个程序的执行期间。
(3)外部变量:整个程序的执行期间。
5.作用域:
(1)自动变量:自动变量所在的函数内或分程序内。
(2)静态变量:局部静态变量的作用域是它所在的函数或分程序。全局静态变量的作用域是从定义处开始到本源文件结束。
(3)外部变量:从定义处开始到本源文件结束。

<think>我们被要求解释C语言变量存储类别,并通过示例说明。根据引用内容,C语言变量存储类别主要分为四种:自动变量(auto)、静态变量(static)、寄存器变量(register)和外部变量(extern)。同时,引用[3]还提到了静态局部变量和静态全局变量的概念。下面我们将逐一解释这些存储类别,并给出示例。 ### 1. 自动变量(auto) - **定义**:自动变量是局部变量的默认存储类别。在函数内部或代码块内部定义的变量,如果没有指定存储类别,则默认为auto。它们存储在动态存储区(栈)中,生命周期从进入函数或代码块开始,到离开时结束。 - **特点**: - 未初始化时值为随机数(垃圾值)。 - 每次进入函数或代码块时重新分配存储空间。 - **示例**: ```c #include <stdio.h> void auto_example() { auto int x = 5; // auto可省略,因为局部变量默认为auto printf("自动变量x: %d\n", x); } int main() { auto_example(); // 输出:自动变量x: 5 // 再次调用,x重新初始化为5 auto_example(); // 输出:自动变量x: 5 return 0; } ``` ### 2. 静态变量(static) - **定义**:静态变量可以是全局的或局部的。它们存储在静态存储区(数据区),生命周期为整个程序运行期间。静态局部变量虽然生命周期长,但作用域仍限于定义它的函数或代码块内。 - **特点**: - 未初始化的静态变量自动初始化为0。 - 只初始化一次,且保留上一次调用后的值。 - **示例(静态局部变量)**: ```c #include <stdio.h> void static_example() { static int count = 0; // 静态局部变量 count++; printf("静态局部变量count: %d\n", count); } int main() { static_example(); // 输出:count=1 static_example(); // 输出:count=2(保留上一次的值) return 0; } ``` - **示例(静态全局变量)**:在文件作用域内使用static修饰的全局变量,作用域仅限于当前源文件,不能被其他文件访问(通过extern)[^3][^5]。 ```c // file1.c static int file_global = 10; // 静态全局变量,仅在此文件内可见 void print_global() { printf("file_global: %d\n", file_global); } // 其他文件无法通过extern引用file_global ``` ### 3. 寄存器变量(register) - **定义**:寄存器变量用于将局部变量存储在CPU寄存器中,以提高访问速度。通常用于频繁使用的变量(如循环计数器)。 - **特点**: - 不能取地址(因为寄存器没有内存地址)。 - 编译器可能忽略register请求(如果寄存器不足,则作为普通auto变量处理)。 - **示例**: ```c #include <stdio.h> int main() { register int i; // 寄存器变量 for (i = 0; i < 10000; i++) { // 频繁使用i,建议使用寄存器存储 } printf("循环结束,i=%d\n", i); // 注意:不能对i取地址,如&i会编译错误 return 0; } ``` ### 4. 外部变量(extern) - **定义**:用于声明在其他文件中定义的全局变量(或函数),以实现跨文件访问。全局变量默认具有外部链接属性(external linkage)[^5]。 - **特点**: - 在多个文件间共享全局变量时使用。 - 声明(declaration)不分配内存,定义(definition)分配内存。 - **示例**: ```c // file1.c int global_var = 100; // 全局变量定义 // file2.c #include <stdio.h> extern int global_var; // 声明外部变量 void print_global() { printf("global_var: %d\n", global_var); // 访问file1.c中的全局变量 } ``` ### 总结表格 | 存储类别 | 关键字 | 存储位置 | 生命周期 | 作用域 | 初始化特点 | |----------|----------|----------|----------------|----------------------|----------------------| | 自动变量 | auto | 栈 | 函数/块内有效 | 定义它的函数/块内 | 未初始化则值随机 | | 静态变量 | static | 数据区 | 整个程序运行期 | 局部:函数/块内;全局:当前文件 | 未初始化自动为0,只初始化一次 | | 寄存器变量 | register | 寄存器 | 同自动变量 | 同自动变量 | 同自动变量,但无法取地址 | | 外部变量 | extern | 数据区 | 整个程序运行期 | 整个程序(多个文件) | 在定义处初始化 | ### 注意事项 1. **静态局部变量**:虽然生命周期长,但作用域不变,仍受限于定义它的函数。 2. **全局变量**:默认具有外部链接属性,但使用`static`修饰后变为内部链接(仅当前文件可见)。 3. **寄存器变量**:现代编译器优化能力很强,通常不需要手动指定,编译器会自动优化。 ### 相关问题 1. 静态局部变量与全局变量有何异同? 2. 为什么寄存器变量不能取地址? 3. 如何避免多个文件中的全局变量命名冲突? 4. 静态全局变量与普通全局变量的区别是什么? 5. 自动变量在递归函数中的表现如何?</think>### C语言变量存储类别详解及示例 在C语言中,变量的**存储类别**决定了变量的生命周期、作用域和存储位置。根据引用内容,主要分为以下四类: --- #### 1. **自动变量(auto)** - **特性**: - 默认存储类别(关键字`auto`可省略) - 存储在**栈内存**中 - 生命周期:函数调用时创建,函数结束时销毁 - 未初始化时值为随机数(垃圾值) - **示例**: ```c #include <stdio.h> void demo_auto() { auto int x = 5; // 显式声明auto(通常省略) int y; // 隐式auto变量(未初始化) printf("x=%d, y=%d(随机值)\n", x, y); } int main() { demo_auto(); // 输出:x=5, y=随机值 // x和y在此处已销毁 return 0; } ``` --- #### 2. **静态变量(static)** - **特性**: - 存储在**数据区**(非栈内存) - 生命周期:整个程序运行期间 - 未初始化时自动赋值为0 - 只初始化一次 - **两种类型**: **a) 静态局部变量**(作用域限于函数内)[^3]: ```c void counter() { static int count = 0; // 只初始化一次 count++; printf("计数: %d\n", count); } int main() { counter(); // 输出:计数: 1 counter(); // 输出:计数: 2(保留上次值) return 0; } ``` **b) 静态全局变量**(作用域限于当前文件)[^5]: ```c // file1.c static int file_global = 10; // 仅当前文件可见 // file2.c extern int file_global; // 编译错误!无法访问 ``` --- #### 3. **寄存器变量(register)** - **特性**: - 请求将变量存储在**CPU寄存器**中 - 用于频繁访问的变量(如循环计数器) - 不能取地址(`&`操作无效) - 编译器可能忽略此请求[^2] - **示例**: ```c #include <stdio.h> int main() { register int i; // 请求寄存器存储 for (i = 0; i < 1000000; i++) { // 高速循环操作 } // printf("%p", &i); // 错误!不能取地址 return 0; } ``` --- #### 4. **外部变量(extern)** - **特性**: - 用于声明**其他文件中定义的全局变量** - 存储在**数据区** - 生命周期:整个程序运行期间 - **示例**: ```c // file1.c int global_var = 100; // 全局变量定义 // file2.c #include <stdio.h> extern int global_var; // 声明外部变量 void print_global() { printf("全局变量: %d\n", global_var); // 访问file1.c中的变量 } ``` --- ### 存储类别对比表 | 类别 | 关键字 | 存储位置 | 生命周期 | 作用域 | 初始化特点 | |-------------|----------|----------|----------------|----------------------|----------------------| | **自动变量** | `auto` | 栈内存 | 函数内有效 | 定义它的代码块内 | 未初始化=随机值 | | **静态变量** | `static` | 数据区 | 整个程序运行期 | 局部:函数内;全局:文件内 | 未初始化=0,只初始化一次 | | **寄存器变量**| `register`| CPU寄存器 | 函数内有效 | 定义它的代码块内 | 同自动变量,不能取地址 | | **外部变量** | `extern` | 数据区 | 整个程序运行期 | 整个程序(跨文件) | 在定义处初始化 | --- ### 关键原理说明 1. **作用域规则**: - 局部变量(auto/register/static局部)优先于全局变量[^1] - 静态全局变量(`static`)限制作用域到当前文件[^5] 2. **内存管理**: ```mermaid graph LR A[内存区域] --> B[栈] A --> C[数据区] A --> D[寄存器] B -->|存储| E[自动变量] C -->|存储| F[静态/全局变量] D -->|存储| G[寄存器变量] ``` 3. **初始化差异**: - 静态变量未初始化时自动为0 - 自动变量未初始化时值为随机数 - 全局变量默认具有外部链接性(可通过`extern`访问) --- ### 最佳实践建议 1. 优先使用局部变量(auto)提高封装性 2. 慎用全局变量,必要时用`static`限制作用域 3. 寄存器变量仅用于性能关键场景 4. 跨文件共享变量时,用`extern`声明而非重复定义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tao'note

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值