在C语言中,static 和extern 是两个重要的存储类说明符,核心作用是控制变量/函数的作用域 (可见范围)和存储周期(生命周期)。
一、extern:外部链接与跨文件访问
extern的核心功能是声明外部符号(变量或函数),表明该符号的定义在其他文件或当前文件的其他位置,用于实现跨文件的符号共享
(1)extern声明外部变量(跨文件共享变量)
当需要在A文件中使用B文件定义的全局变量时,需在A文件中用extern声明该变量。
cpp
// file1.c(定义全局变量)
int g_val = 10; // 全局变量的定义,分配内存,默认具有外部链接属性
// file2.c(使用file1.c中的全局变量)
#include <stdio.h>
extern int g_val; // 声明外部变量,不分配内存,告诉编译器g_val在其他文件定义
int main() {
printf("g_val = %d\n", g_val); // 输出:10,成功访问file1.c中的g_val
return 0;
}
注意:
-
全局变量默认具有外部链接属性,因此file1.c中的g_val无需加extern即可被其他文件访问;
-
file2.c中的extern仅为声明,不能初始化(如extern int g_val = 20; 会被编译器视为定义,若多个文件同时定义同名全局变量,会导致链接错误)
(2)extern声明外部函数(跨文件共享函数)
C语言中函数默认具有外部链接属性,因此跨文件调用函数时,extern声明可省略(编译器会自动查找函数定义),但显式声明更规范。
cpp
// func.c(定义外部函数)
#include <stdio.h>
void print_msg() { // 函数默认具有外部链接属性
printf("Hello, extern!\n");
}
// main.c(调用func.c中的函数)
extern void print_msg(); // 显式声明外部函数,可省略
// 省略写法:void print_msg(); (编译器默认按外部函数查找)
int main() {
print_msg(); // 输出:Hello, extern!
return 0;
}
若函数被static修饰(见下文),则失去外部链接属性,无法用extern跨文件声明调用。
二、static:内部链接与生命周期延长
static的核心功能有两个:
-
修饰变量:延长变量的存储周期(被修饰的局部变量由栈区储存为静态变量区储存。静态变量存储在全局数据区,程序运行期间始终存在);
-
修饰变量/函数:将其链接属性改为"内部链接",仅在当前文件可见,无法被其他文件访问(实现"封装")。
2.1 static修饰局部变量
普通局部变量存储在栈区,生命周期仅限于函数调用期间(函数执行结束后栈区释放,变量销毁);static修饰的局部变量存储在全局数据区,生命周期与程序一致(函数执行结束后变量不销毁,值保留),但作用域仍限于当前函数。
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test()
{
int b = 0;
b++;
printf("b = %d ", b);
}
int main()
{
int i = 0;
for (int i = 0; i < 5; i++)
{
test();
}
return 0;
}

b为普遍变量,储存在栈区。生命周期仅限于函数调用期间,所以每次for循环之后销毁,输出5个1
加static 修饰b:
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test()
{
static int b = 0;
b++;
printf("b = %d ", b);
}
int main()
{
int i = 0;
for (int i = 0; i < 5; i++)
{
test();
}
return 0;
}

加上static后b储存在静态变量区,生命周期与程序一致,每次for循环之后不会被销毁。直达程序结束才销毁。当b的作用域只在test()函数中。
2.2 static修饰全局变量
普通全局变量具有外部链接属性,可被其他文件访问;static修饰的全局变量变为"内部链接"属性,仅在当前文件可见,其他文件无法通过extern声明访问(链接时会提示"未定义的引用")。
cpp
// file1.c
static int g_static_val = 30; // static全局变量,仅file1.c可见
// file2.c
#include <stdio.h>
extern int g_static_val; // 错误:无法访问file1.c中的static全局变量
int main() {
printf("%d\n", g_static_val); // 链接错误:undefined reference to `g_static_val'
return 0;
}
用途:当全局变量仅需在当前文件使用时,用static修饰可避免与其他文件的同名变量冲突,实现变量的"文件级封装"。
2.3 static修饰函数
普通函数具有外部链接属性,可被其他文件调用;static修饰的函数变为"内部链接"属性,仅在当前文件可见,其他文件无法通过extern声明调用。
cpp
// func.c
static void static_func() { // static函数,仅func.c可见
printf("This is a static function.\n");
}
// main.c
extern void static_func(); // 错误:无法访问func.c中的static函数
int main() {
static_func(); // 链接错误:undefined reference to `static_func'
return 0;
}
用途:当函数仅需在当前文件内部调用时(如辅助函数),用static修饰可避免与其他文件的同名函数冲突,减少全局符号污染。
三、static与extern的核心差异对比
| 对比维度 | static | extern |
|---|---|---|
| 核心作用 | 1. 延长变量存储周期(局部变量);2. 限制符号作用域为当前文件(内部链接) | 声明外部符号,实现跨文件访问(外部链接) |
| 对变量的影响 | 修饰局部变量:栈区→全局数据区,生命周期延长;修饰全局变量:作用域限制为当前文件 | 仅声明变量,不分配内存;若初始化则视为定义 |
| 对函数的影响 | 函数作用域限制为当前文件,无法跨文件调用 | 声明外部函数,支持跨文件调用(可省略) |
| 初始化要求 | static变量未初始化时,默认初始化为0 | 仅声明时不能初始化;初始化则视为定义 |
| 链接属性 | 内部链接(仅当前文件可见) | 外部链接(跨文件可见) |
五、总结
- extern 的核心是"共享":用于声明外部符号(变量/函数),实现跨文件访问,依赖外部链接属性; 2. static的核心是"限制"与"延长":限制符号作用域为当前文件(内部链接),延长局部变量的存储周期; 3. 实际开发中,应遵循"最小权限原则":仅需跨文件共享的符号用extern声明(或默认外部链接),仅当前文件使用的符号用static修饰,减少全局符号冲突,提升代码可维护性。