作用域
C语⾔变量的作用域分为:
- 代码块作用域(代码块是 {} 之间的一段代码)
- 函数作用域
- 文件作用域
局部变量
局部变量也叫auto⾃动变量(auto可写可不写),一般情况下代码块{}内部定义的变量都是⾃动变量,它有如下特点:
- 在一个函数内定义,只在函数范围内有效
- 在复合语句中定义,只在复合语句中有效
- 如果没有赋初值,内容为随机
#include <stdio.h>
#include <stdlib.h>
void test() {
//auto写不写是一样的
//auto只能出现在{}内部
auto int b = 10;
}
int main(void) {
//b = 100; //err, 在main作用域中没有b
if (1) {
//在复合语句中定义,只在复合语句中有效
int a = 10;
printf("a = %d\n", a);
}
//a = 10; //err离开if()的复合语句,a已经不存在
return 0;
}
程序输出:
a = 10
静态局部变量
- static局部变量的作用域也是在定义的函数内有效
- static局部变量的生命周期和程序运行周期一样,同时staitc局部变量的值
- static局部变量若未赋以初值,则由系统⾃动赋值:数值型变量⾃动赋初值0,字符型变量赋空字符
#include <stdio.h>
#include <stdlib.h>
void fun1() {
int i = 0;
i++;
printf("i = %d\n", i);
}
void fun2() {
//静态局部变量,没有赋值,系统赋值为0,而且只会初始化一次
static int a;
a++;
printf("a = %d\n", a);
}
int main(void) {
fun1();
fun1();
fun2();
fun2();
return 0;
}
程序输出:
i = 1
i = 1
a = 1
a = 2
全局变量
- 在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,须用extern声明
- 全局变量的生命周期和程序运行周期一样
- 不同文件的全局变量不可重名
静态全局变量
- 在函数外定义,作用范围被限制在所定义的文件中
- 不同文件静态全局变量可以重名,但作用域不冲突
- static全局变量的生命周期和程序运行周期一样,同时staitc全局变量的值只初始化一次
全局变量声明——extern
extern int a;
声明一个变量,这个全局变量在别的文件中已经定义了,这里只是声明,而不是定义。
extern是C、C++语⾔中表明函数和全局变量作用范围的关键字。,由于函数的声明如“extern int method();
”与函数定义“ int method(){}
”可以很清晰的区分开来,为了简便起见,可以把extern关键字省略。
extern修饰变量的声明
举例来说,如果文件 a.c 需要引用 b.c 中变量 int v;
,就可以在 a.c 中声明 extern int v;
,然后就可以引用变量v。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是, extern int v;
可以放在 a.c 中的任何地⽅,比如你可以在 a.c 中的函数fun定义的开头处声明 extern int v;
,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都⼼存顾虑。好像extern声明只能用于文件作用域似的。
extern修饰函数声明
从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件 a.c 需要引用 b.c 中的函数,比如在 b.c 中原型是 int fun(int mu);
,那么就可以在 a.c 中声明 extern int fun(int mu);
,然后就能使用fun来做任何事情。就像变量的声明一样, extern int fun(int mu);
可以放在 a.c 中任何地⽅,而不一定非要放在 a.c 的文件作用域的范围中。对其他模块中函数的引用,最常用的⽅法是包含这些函数声明的头文件。
使用extern和包含头文件来引用函数有什么区别
extern的引用⽅式比包含头文件要简洁得多!extern的使用⽅法是直接了当的,想引用哪个函数就用extern声明哪个函数。这⼤概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。
指示C或者C++函数的调用规范
比如在C++中调用C库函数,就需要在C++程序中用 extern “C”
声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同。
extern应用实例
实例一
用C语⾔编写程序的时候,我们经常会遇到这样一种情况:希望在头文件中定义一个全局变量,然后包含到两个不同的c文件中,希望这个全局变量能在两个文件中共用。
举例说明:项目文件夹project下有main.c、common.和common.h三个文件,其中common.文件分别#include在main.c和common.c文件中。现在希望声明一个字符型变量key,在 main.c和common.c中公用。
有人想,既然是想两个文件都用,那就在common.h中声明一个 unsigned char key
,然后由于包含关系,在main.c和common.c中都是可见的,所以就能共用了。
这种想法其实是很多初学者都会想到的,想起来确实有道理,但是实际写出来,我们发现编译的时候编译器提⽰出错,一般提⽰⼤概都类似于: Error: L6200E: Symbol key multiply defined (by common.o and main.o).
也就是说编译器认为我们重复定义了key这个变量。这是因为#include命令就是原封不同的把头文件中的内容搬到#include的位置,所以相当于main.c和common.c中都执行了一次 unsigned char key ,而C语⾔中全局变量是项目内(或者叫工程内)可见的,这样就造成了一个项目中两个变量key,编译器就认为是重复定义。
正确的解决办法:使用extern关键字来声明变量为外部变量。具体说就是在其中一个c文件中定义一个全局变量key,然后在另一个要使用key这个变量的c文件中使用extern关键字声明一次,说明这个变量为外部变量,是在其他的c文件中定义的全局变量。请注意我这里的用词:定义和声明。例如在main.c文件中定义变量key,在common.c文件中声明key变量为外部变量,这样这两个文件中就能共享这个变量key了。 代码如下(只写跟我们所说问题有关的部分):
(1)main.c文件
unsigned char key;
(2)common.c文件
#include "common.h"
extern unsigned char key;
实例二
情景:在一个工程里⾯有两个文件a.c and b.c,其中它们的内容如下:
a.c
#include <stdio.h>
int i = 3;
int p(void) {
printf("%d\n", i);
return 0;
}
b.c
#include <stdlib.h>
extern int p(void);
extern int i;
int main() {
p();
return 0;
}
在b.c里⾯调用a.c里⾯定义的变量和函数,最后在Dev c++里⾯运行,b.c的输出结果为:3。
全局函数和静态函数
在C语⾔中函数默认都是全局的,使用关键字static可以将函数声明为静态,函数定义为static 就意味着这个函数只能在定义这个函数的文件中使用,在其他文件中不能调用,即使在其他 文件中声明这个函数都没用。
对于不同文件中的staitc函数名字可以相同。
注意
- 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不⼲扰。
- 同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用。
- 所有的函数默认都是全局的,意味着所有的函数都不能重名,但如果是staitc函数,那么作用域是文件级的,所以不同的文件static函数名是可以相同的。
总结
类型 | 作用域 | 生命周期 |
---|---|---|
auto变量 | 一对{}内 | 当前函数 |
static局部变量 | 一对{}内 | 整个程序运行期 |
extern变量 | 整个程序 | 整个程序运行期 |
static全局变量 | 当前文件 | 整个程序运行期 |
extern函数 | 整个程序 | 整个程序运行期 |
static函数 | 当前文件 | 整个程序运行期 |
register变量 | 一对{}内 | 当前函数 |
全局变量 | 整个程序 | 整个程序运行期 |