C语言的变量作用域及头文件

C语言的变量作用域及头文件关于C语言的变量作用域和头文件的问题都是比较基础的问题,但是这些问题在实际使用过程中的概念不清和混乱会对一个多文件的项目的组织结构及文件结构造成很大的影响,使得项目本身的脉络也变的很模糊。在项目中,多人相互协作完成的项目,这个问题就更加突出。所以也就有了我写(总结)这个文档。

一.C语言的变量作用域及相关

1.作用域:

作用域描述了程序中可以访问一个标识符的一个或多个区域。即变量的可见性。一个C变量的作用域可以是代码块作用域、函数原型作用域,和文件作用域。 函数作用域(Function Scope),标识符在整个函数中都有效。只有语句标号属于函数作用域。标号在函数中不需要先声明后使用,在前面用一个goto语句也可以跳转到后面的某个标号,但仅限于同一个函数之中。 文件作用域(File Scope),标识符从它声明的位置开始直到这个程序文件的末尾都有效。例如下例中main函数外面的sum,add,还有main也算,printf其实是在stdio.h中声明的,被包含到这个程序文件中了,所以也算文件作用域的。 块作用域(Block Scope),标识符位于一对{}括号中(函数体或语句块),从它声明的位置开始到右}括号之间有效。例如上例中main函数里的num。此外,函数定义中的形参也算块作用域的,从声明的位置开始到函数末尾之间有效。 函数原型作用域(Function Prototype Scope),标识符出现在函数原型中,这个函数原型只是一个声明而不是定义(没有函数体),那么标识符从声明的位置开始到在这个原型末尾之间有效。例如void add(int num);中的num。

下面再介绍另一种分类形式:它分为代码块作用域和文件作用域。代码块作用域和文件作用域也有另一种分类方法,局部作用域和全局作用域。

代码块作用域:代码块是指一对花括号之间的代码,函数的形参虽然是在花括号前定义但也属于代码作用域。在C99中把代码块的概念扩大到包括由for循环、while循环、do while循环、if语句所控制的代码。在代码块作用域中,从该变量被定义到代码块末尾该变量都可见。 文件作用域:一个在所有函数之外定义的变量具有文件作用域。具有文件作用域的变量从它的定义处到包含该定义的文件结尾都是可见的。

2.链接

一个C语言变量具有下列链接之一:外部链接(external linkage),内部链接(internal linkage)或空链接(no linkage)。 空链接:具有代码块作用域或者函数原型作用域的变量就具有空链接,这意味着他们是由其定义所在的代码块或函数原型所私有。 内部链接:具有文件作用域的变量可能有内部或外部链接,一个具有文件作用域的变量前使用了static标识符标识时,即为具有内部链接的变量。一个具有内部链接的变量可以在一个文件的任何地方使用。 外部链接:一个具有文件作用域的变量默认是具有外部链接的。但当起前面用static标识后即转变为内部链接。一个具有外部链接的链接的变量可以在一个多文件程序的任何地方使用。

例: static int a;(在所有函数外定义)内部链接变量 int b; (在所有函数外定义) 外部链接变量 main() {

int b;//空链接,仅为main函数私有。 ..

} 3.存储时期

一个C语言变量有以下两种存储时期之一:(未包括动态内存分配malloc和free等) 静态存储时期(static storage duration)和自动存储时期(automatic storage duration)和动态存储时期。 静态存储时期:如果一个变量具有静态存储时期,他在程序执行期间将一直存在。具有文件作用域的变量具有静态存储时期。这里注意一点:对于具有文件作用域的变量,关键词static表明链接类型,而不是存储时期。一个使用了static声明了的文件作用域的变量具有内部链接,而所有的文件作用域变量,无论他具有内部链接,是具有外部链接,都具有静态存储时期。自动存储时期:具有代码块作用域的变量一般情况下具有自动存储时期。在程序进入定义这些变量的代码块时,将为这些变量分配内存,当退出这个代码块时,分配的内存将被释放。

举例如下:

//example_1.c#include <stdio.h>#include <stdlib.h>void add(int num);//文件作用域,外部链接,void chang_sum();//文件作用域,外部链接int sum=1; //文件作用域 外部链接,静态存储时期int main(int argc, char *argv[]){int num = 5;//函数作用域,空链接add(num);printf("main num=%d/n",num); /*输出5*///内层定义覆盖原则,当内层代码块定义了一个与外层代码块相同时名字的变量时,//运行到内层代码块时,使用内层定义的变量,离开内层代码块时,外层变量恢复//此时sum为for中定义的sum,而不是全局的sumfor(int sum=0, num=0;num<5;num++)//代码块作用域,空链接,自动存储时期{sum+=num;printf("====================/n");printf("for num=%d/n",num);//输出0-5printf("for sum=%d/n",sum);//输出0-5的叠加和}printf("====================/n");{int i;//代码作用域。仅在该大括号内可见。空链接,自动存储时期for(i=0;i<10;i++);printf("i=%d/n",i);}//printf("i=%d/n",i);//编译通不过printf("main sum=%d/n",sum);//输出0。printf("main num=%d/n",num);// 输出5chang_sum();printf("file sum=%d/n",sum);//输出1。全局的sum。内层定义覆盖原则。system("PAUSE");return 0;}void add(int num)//代码作用域{num++;printf("add num= %d/n",num); /*输出6*/}void chang_sum(){sum++;printf("chang_sum = %d/n",sum); /*输出1*/}

以上示例须在在C99标准下编译。(gcc支持c99的方法,编译时加入参数 –std=C99)。从上例中可以比较清楚明白代码作用域和文件作用域的概念。另外注意文件作用域不仅限于变量也包括函数。在文件作用域中函数也是以其声明开始到文件结尾结束。而且当拥有文件作用域与拥有代码作用域变量同名时,不会发生冲突,而是以最小作用域的变量可见。

4.存储类修饰符(Storage Class Specifier)有以下几种关键字,可以修饰变量或函数声明:

static,用它修饰的变量的存储空间是静态分配的,用它修饰的文件作用域的变量或函数具有Internal Linkage(内部链接)。 auto,用它修饰的变量在函数调用时自动在栈上分配存储空间,函数返回时自动释放,例如上例中main函数里的num其实就是用auto修饰的,只不过auto可以省略不写(此处与编译器有关,参照编译器不同而有所变动),auto不能修饰文件作用域的变量。register,编译器对于用register修饰的变量会尽可能分配一个专门的寄存器来存储,但如果实在分配不开寄存器,编译器就把它当auto变量处理了,register不能修饰文件作用域的变量。现在一般编译器的优化都做得很好了,它自己会想办法有效地利用CPU的寄存器,所以现在register关键字也用得比较少了。 extern,上面讲过,链接属性是根据一个标识符多次声明时是不是代表同一个变量或函数来分类的,extern关键字就用于多次声明同一个标识符。

c语言使用作用域,链接和存储时期来定义了5种存储类:自动,寄存器,具有代码块的作用域的静态、具有外部链接的静态,以及具有内部链接的静态。

五种存储类

存储类

时期

作用域

链接

声明方式

自动

自动

代码块

代码块内

寄存器

自动

代码块

代码块内,使用register

具有外部链接的静态

静态

文件之间

外部

所有函数之外

具有内部链接的静态

静态

文件之内

内部

所有函数之外使用关键字static

空链接的静态

静态

代码块

因为冲动会做下让自己无法挽回的事情。

C语言的变量作用域及头文件

相关文章:

你感兴趣的文章:

标签云: