从变量的作用域进行区分,变量可分为全局变量
和局部变量
。
从变量的生存期进行区分,变量可分为静态存储方式
和动态存储方式
。
固定的
存储空间的方式。动态分配
存储空间的方式。内存中供用户使用的存储空间情况(方便理解,大致划分):
数据分别存放在静态存储区和动态存储区中。
全局变量全部存放在静态存储区中,在程序开始执行
时给全局变量分配存储区
,程序执行完毕就释放
。在整个程序执行过程中占据固定的存储单元
,而不是动态地进行分配和释放。
局部变量全部存放在动态存储区中,除此之外还有函数的形式参数、函数中定义的没有用关键字static声明的变量、函数调用时的现场保护和返回地址等都存放在动态存储区中。 对于这些数据,在函数调用开始时分配动态存储空间
,函数调用结束时释放这些空间
,这种分配和释放是动态的进行。
在C语言中,每一个变量和函数都有两个属性:数据类型(int、float…)和数据的存储类别。
在定义和声明变量和函数时,一般应同时指定其数据类型
和存储类别
,如果用户不指定存储类别,系统会默认
地指定为某一种存储类别。
C语言的存储类别包括四种:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)。
根据变量的存储类别,可以知道变量的作用域和生存期。
系统默认的局部变量存储类别。在调用函数时,系统会给函数的形参、函数内定义的局部变量(包括在复合语句中定义的局部变量)分配存储空间。在函数调用结束时就自动释放这些存储空间,因此这类局部变量称为自动变量
。
自动变量使用关键字 auto 作为存储类别的声明。例如:
int fun(int a) //a为形参
{
auto int b = 3; //b为自动变量
}
auto 关键字可以省略,不写则系统默认
指定为自动变量。自动变量是动态存储方式,在动态存储区内分配内存单元,函数调用结束后立即释放。
有时希望函数中的局部变量的值在函数调用结束后不消失而继续保留原值,即其占用的存储单元不释放,在下一次调用该函数时,函数中的变量已有值(就是上一次函数调用结束时的值)。这时就要指定该局部变量为“静态局部变量”
,使用关键字 static 进行声明。例如:
#include
int fun()
{
static int a = 0; //静态局部变量,调用结束后内存不释放,保留值。
a++;
return a;
}
int main()
{
printf("%d\n", fun()); //1
printf("%d\n", fun()); //2
printf("%d\n", fun()); //3
printf("%d\n", fun()); //4
printf("%d\n", fun()); //5
return 0;
}
静态局部变量属于静态存储类别,在静态存储区内分配内存单元,在程序整个运行期间都不释放。
静态局部变量是需要赋初值的,如果在定义时不赋初值,系统自动赋初值为0,字符型自动赋值为’\0’。
一般变量的值是存放在内存中的,不管是静态存储方式还是动态存储方式。当程序用到哪一个变量时,由控制器
发出指令将内存中该变量的值送到运算器
中。
但如果有一些变量使用频繁,则在存取变量的值上就要损失不少的执行效率。例如:
void fun()
{
int a = 0;
for (int i = 0; i < 100*100*100; i++)
{
a = a + 1;
}
}
为了提高执行效率,C语言允许将局部变量的值存放在CPU的寄存器
中,需要用时直接从寄存器中取出参加运算,因为对寄存器的存取速度远高于对内存的存取速度。 这种把值存放在在寄存器中的变量,被叫做寄存器变量
,使用关键字 register 进行声明。例如:
void fun()
{
register int a = 0; //定义a为寄存器变量
for (int i = 0; i < 100*100*100; i++)
{
a = a + 1;
}
}
一般现在的编译系统都能够自动识别使用频繁的变量而自动将其存放在寄存器中,不需要再进行额外指定,所以使用register声明变量的场景很少很少。只做了解。
全局变量都是存放在静态存储区中的, 因此它们的生存期是固定的,存在于程序的整个运行过程。(全局变量如果不赋初值,系统自动赋值为0。)但对全局变量来说,作用域也可以有不同的指定范围。
可分为四种:
全局变量也被称为外部变量,即在函数外部定义的变量。 如果不是在文件的开头位置定义全局变量,其有效的作用范围只限于
定义处到文件结束,在定义点之前的函数不能引用该变量。
出于某种使用需求,在定义点之前的函数需要引用该全局变量,则应该在引用之前用 extern 关键字对该变量做“外部变量声明”
,表示把该外部变量的作用域扩展到此位置。
例如:
#include
int main()
{
printf("%d\t%d\t%d", A, B, C);
return 0;
}
int A = 1, B = 2, C = 3;
/*
错误 C2065 “A”: 未声明的标识符
错误 C2065 “B”: 未声明的标识符
错误 C2065 “C”: 未声明的标识符
*/
#include
int main()
{
extern int A, B, C; //或者省略类型,extern A, B, C; 也可以
printf("%d\t%d\t%d", A, B, C); //1 2 3
return 0;
}
int A = 1, B = 2, C = 3;
在一个文件中想引用另一个文件中已经定义的外部变量,不能分别在两个文件中各自定义相同名称的外部变量,否则会出现重复定义的错误。
正确的做法是在一个文件中定义
了外部变量A
,而在另一文件中用 extern 对A
做“外部变量声明”
,即“ extern A; ”。在编译时系统会由此知道A有“外部链接”
,可以从别处找到已定义的外部变量A。并将在另一文件中定义的外部变量A的作用域扩展
到本文件,在本文件中合法的引用
外部变量A。
所以,extern 既可以用来扩展外部变量在本文件中的作用域,又可以使外部变量的作用域从一个文件扩展到程序中的其他文件。
那么系统该如何区分
处理二者呢?实际上,在编译时遇到 extern,系统会先在本文件
中找外部变量的定义,如果找到
,就在本文件中扩展作用域
。如果找不到
,就从其他文件
中找外部变量的定义
,如果在其他文件中找到
了,就将作用域扩展
到本文件,如果还是找不到
,就按出错
处理。
如果希望某些外部变量只限于被本文件引用,而不能被其他文件引用,可以在定义外部变量时加一个 static 声明。例如:
//文件1:file1.c
static int A;
int main()
{
//······
return 0;
}
//文件2:file2.c
extern A;
int fun(int n)
{
//······
A = A * n; //报错
//······
}
加上static声明的全局变量,只能作用于本文件内。 这种状态下我们将它称为静态外部变量。如果已确认其他文件不需要引用本文件的外部变量,就可以对本文件的外部变量加上 static 使其成为静态外部变量
,以免被其他文件误用,相当于把本文件的外部变量屏蔽
起来。
需要注意的是,全局变量本来就是存放在静态存储区的,加static对全局变量的存储方式不起影响。
声明局部变量的存储类型,作用是指定变量存储的区域(静态/动态存储区),以及由此产生的生存期问题。
声明全局变量的存储类型,作用是变量作用域的扩展问题。从始至终全局变量都是存放在静态存储区当中的。
用static声明变量的作用是: