static 和 extern 关键字
目录
一、static 和 extern 概述
二、作用域与生命周期概念
1、作用域(scope)
2、生命周期(lifetime)
局部变量的生命周期:
全局变量的生命周期:
三、static 修饰局部变量
对比示例分析
代码1:普通局部变量
代码2:static局部变量
关键结论
1、存储位置变化:
2、生命周期变化:
3、初始化特性:
四、static 修饰全局变量
对比示例分析
代码1:普通全局变量
代码2:static全局变量
关键结论
1、链接属性变化:
2、访问范围限制:
3、避免命名冲突:
五、static 修饰函数
对比示例分析
代码1:普通函数
代码2:static函数
关键结论
1、链接属性变化:
2、访问范围限制:
3、封装性优势:
六、总结对比表(超重要!!!)
一、static 和 extern 概述
static 和 extern 都是C语言中的关键字,具有重要的变量和函数修饰作用。
-
static:表示"静态的",可以用于:修饰局部变量、修饰全局变量、修饰函数
-
extern:用于声明外部符号,使得跨文件访问变量和函数成为可能。
二、作用域与生命周期概念
在深入理解 static 和 extern 之前,需要明确两个重要概念:
1、作用域(scope)
作用域是指程序中变量或函数名有效的代码范围。
-
局部变量的作用域:仅限于定义它的函数或代码块内部
-
全局变量的作用域:从定义点开始到文件末尾,且可通过 extern 声明扩展到整个工程
2、生命周期(lifetime)
生命周期指变量从创建(内存分配)到销毁(内存回收)的时间段。
局部变量的生命周期:
-
进入作用域时创建
-
离开作用域时销毁
-
每次进入作用域都会重新初始化
全局变量的生命周期:
-
程序启动时创建
-
程序结束时销毁
-
整个程序运行期间持续存在
三、static 修饰局部变量
对比示例分析
代码1:普通局部变量
#include <stdio.h>void test() {int i = 0; // 每次调用都会重新初始化i++;printf("%d ", i);
}int main() {for(int i = 0; i < 5; i++) {test();}return 0;
}
在代码1的test函数中,每次调用时都会先创建局部变量i并初始化为0。随后执行i++操作并打印结果,当函数执行完毕时,该变量的生命周期结束,内存将被释放。
代码2:static局部变量
#include <stdio.h>void test() {static int i = 0; // 只初始化一次i++;printf("%d ", i);
}int main() {for(int i = 0; i < 5; i++) {test();}return 0;
}
在代码2的输出结果中,我们可以观察到变量i的值具有累加特性。这是因为test函数中的变量i在创建后并不会随着函数执行结束而销毁,当再次进入函数时,系统不会重新创建变量i,而是会继续使用之前累积的数值进行计算。
关键结论
1、存储位置变化:
- 普通局部变量存储在栈区、static局部变量存储在静态区
2、生命周期变化:
-
static修饰使局部变量的生命周期延长至整个程序运行期间
-
但作用域保持不变,仍然只能在定义它的函数内访问
3、初始化特性:
-
static局部变量只初始化一次
-
程序运行时在静态区分配内存并初始化
-
后续函数调用会保持上次的值
使用建议:当需要在函数调用之间保持变量值时,使用static修饰局部变量。
四、static 修饰全局变量
对比示例分析
代码1:普通全局变量
add.c:
int g_val = 2018; // 全局变量定义
test.c:
#include <stdio.h>extern int g_val; // 外部变量声明int main() {printf("%d\n", g_val);return 0;
}
正常运行,输出:2018
代码2:static全局变量
add.c:
static int g_val = 2018; // 静态全局变量
test.c:
#include <stdio.h>extern int g_val; // 外部声明无效int main() {printf("%d\n", g_val); // 链接错误return 0;
}
extern
用于声明外部符号。当某个全局符号在A文件中定义,而B文件需要使用时,可以通过extern
进行声明后直接调用。
编译时会出现链接错误,提示未定义的g_val引用。
关键结论
1、链接属性变化:
-
普通全局变量具有外部链接属性(external linkage)
-
static全局变量具有内部链接属性(internal linkage)
2、访问范围限制:
-
static修饰使全局变量仅限于定义它的源文件内部使用
-
其他文件即使使用extern声明也无法访问
3、避免命名冲突:
-
static全局变量不会与其他文件的同名全局变量冲突
使用建议:当需要限制全局变量仅在当前文件内使用时,使用static修饰。
五、static 修饰函数
对比示例分析
代码1:普通函数
add.c:
int Add(int x, int y) {return x + y;
}
test.c:
#include <stdio.h>extern int Add(int x, int y); // 函数声明int main() {printf("%d\n", Add(2, 3));return 0;
}
正常运行,输出:5
代码2:static函数
add.c:
static int Add(int x, int y) {return x + y;
}
test.c:
#include <stdio.h>extern int Add(int x, int y); // 声明无效int main() {printf("%d\n", Add(2, 3)); // 链接错误return 0;
}
其实 static 修饰函数和 static 修饰全局变量是一模一样的,所以编译时会出现链接错误,提示未定义的Add函数引用。
关键结论
1、链接属性变化:
-
普通函数默认具有外部链接属性
-
static函数具有内部链接属性
2、访问范围限制:
-
static函数只能在定义它的源文件内部调用
-
对其他文件不可见,即使使用extern声明
3、封装性优势:
-
可以隐藏实现细节
-
避免与其他文件的同名函数冲突
使用建议:当需要限制函数仅在当前文件内使用时,使用static修饰。
六、总结对比表(超重要!!!)
特性 | static局部变量 | static全局变量 | static函数 |
---|---|---|---|
存储位置 | 静态区 | 静态区 | 代码区 |
作用域 | 定义它的函数内 | 定义它的文件内 | 定义它的文件内 |
生命周期 | 整个程序运行期 | 整个程序运行期 | N/A |
链接属性 | 不适用 | 内部链接 | 内部链接 |
主要用途 | 保持函数调用间的状态 | 文件内部使用的全局数据 | 文件内部使用的函数 |
理解static和extern的关键在于掌握变量的存储位置、作用域和链接属性这三个核心概念。合理使用这些关键字可以提高代码的封装性和模块化程度。