进阶:在多文件的程序中声明外部变量
在前面的课程中,我们见过的所有的程序都是在一个C程序文件中实现的,但是,我们在开发中遇到的各种各样的程序,很多都是由多个程序文件组成的,每个文件基本上就是实现了程序的部分功能,然后再把各个功能凑合起来,所以,我们现在要学着把一个文件拆成多个文件。
这是我们第一次把一个程序拆成多个文件!
请看实战演练:
file1.c(文件1)
#include <stdio.h>
int A; /*定义外部变量*/
void main()
{
int power(int); /*函数声明*/
int b = 3, c, result, m;
printf("enter the number A and its power m:\n");
scanf("%d %d", &A, &m);
c = A * b;
printf("%d * %d = %d\n", A, b, c);
result = power(m);
printf("%d ^ %d = %d\n", A, m, result);
}
file2.c(文件2)
extern A; /*声明A为一个已定义的外部变量*/
int power(int n)
{
int i, y = 1;
for(i=1; i <= n; i++)
{
y *= A;
}
return y;
}
我们把这两个文件分别编译一下,都编译通过了,然后,我们点击运行,也是可以运行的。
8.9.6 用static声明外部变量
有时在程序设计中希望某些外部变量只限于被本文件使用,而不能被其它文件引用。这时就可以在定义外部变量时加一个static声明。
小改刚才的程序:
file1.c(文件1)
#include <stdio.h>
static int A; /*这里我们增加了static不让别的文件引用*/
void main()
{
int power(int); /*函数声明*/
int b = 3, c, d, m;
printf("enter the number a and its power m:\n");
scanf("%d %d", &A, &m);
c = A * b;
printf("%d * %d = %d\n", A, b, c);
d = power(m);
printf("%d ^ %d = %d\n", A, m, d);
}
file2.c(文件2)
extern A; /*声明A为一个已定义的外部变量*/
int power(int n)
{
int i, y = 1;
for(i=1; i <= n; i++)
{
y *= A;
}
return y;
}
编译还是没有问题,但是运行就会报错:
static声明除了使一个局部变量变成一个静态变量之外,还能使一个全局变量捆绑在一个固定的文件里面。
8.9.7 辨析:关于变量的声明和定义
对变量而言,声明与定义的关系稍微复杂一些。在声明部分出现的变量有两种情况:一种是需要建立存储空间的(如:int a;),另一种是不需要建立存储空间的(如:extern a;)。
前者称为“定义性声明”(define declaration),或简称定义(definition)。
后者称为“引用性声明”(referencing declaration)。
广义地说,声明包括定义,但并非所有的声明都是定义。
对 “int a;” 而言,它既是声明,又是定义。而对于 “extern a;” 而言,它是声明而不是定义。
一般为了叙述方便,把建立存储空间的声明称为定义,而把不需要建立存储空间的声明称为声明。显然这里指的声明是狭义的,即非定义性声明。
例如:
void main ()
{
extern A; //是声明不是定义。声明A是一个已定义的外部变量
..........
}
int A; //是定义也是声明
存储类别归纳
我们试着从不同角度做些归纳:
(1)从作用于角度分,有局部变量和全局变量。它们采用的存储类别如下:
局部变量 | 自动变量,即动态局部变量(离开函数,值就消失) 静态局部变量(离开函数,值仍保留) 寄存器变量(离开函数,值就消失) (形式参数可以定义为自动变量或寄存器变量) |
全局变量 | 静态外部变量(只限本文件使用) 外部变量(即非静态的外部变量,允许其他文件引用) |
(2)从变量存在的时间(生存期)来区分,有动态存储和静态存储两种类型。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。
动态存储 | 自动变量(本函数内有效) 寄存器变量(本函数内有效) 形式参数(本函数内有效) |
静态存储 | 静态局部变量(函数内有效) 静态外部变量(在文件内有效) 外部变量(其他文件可引用) |
(3)从变量值存放的位置来区分,可分为:
内存中静态存储区 | 静态局部变量(函数内有效) 静态外部变量(函数外部静态变量) 外部变量(其他文件可引用) |
内存中动态存储区 | 自动变量和形式参数 |
CPU中的寄存器 | 寄存器变量 |
(4)关于作用域和生存期的概念
从前面的叙述可以知道,对一个变量的性质可以从两个方面分析:一是变量的作用域,一是变量值存在时间的长短,即生存期。
前者是从空间的角度,后者是从时间的角度,二者有联系但是不是一回事。
(5)static对局部变量和全局变量的作用不同。
对局部变量来说,它使变量由动态存储方式改变为静态存储方式。而对全局变量来说,它使变量局部化(局部于本文件),但仍是静态存储方式。
从作用域角度看,凡有static声明的,其作用域都是局部的,或者是局限于本函数内(静态局部变量),或者局限于本文件内(静态外部变量)。
8.10 内部函数和外部函数
函数本质上是全局的,因为一个函数要被另外的函数调用,但是,也可以指定函数不能被其他文件调用。
我们根据函数能否被其她源文件调用,将函数区分为内部函数和外部函数。
内部函数
如果一个函数只能被本文件中其他函数所调用,它成为内部函数。
在定义内部函数时,在函数名和函数类型的前面加static。
即:static 类型标识符 函数名(形参表)
如:static int fun(int a,int b)
外部函数
(1)在定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其他文件调用。如函数首部可以写为
extern int fun(int a,int b)
这样,函数fun就可以为其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。前面所用的函数都是外部函数。
(2)在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数。
实战演练:
有一个字符串,内有若干个字符,要求输入一个字符,程序便将字符串中该字符删去。要求用外部函数实现。
file1.c
#include <stdio.h>
void main()
{
extern void enter_string(char str[]);
extern void delete_string(char str[], char ch);
extern void print_string(char str[]);
/*以上3行声明在本函数中将要调用的在其他文件中定义的3个函数*/
char c;
char str[80];
enter_string(str);
scanf("%c", &c);
delete_string(str, c);
print_string(str);
}
file2.c
#include <stdio.h>
//定义外部函数enter_string()
void enter_string(char str[80])
{
gets(str); /*向字符数组输入字符串*/
}
file3.c
/*定义外部函数 delete_string() */
void delete_string(char str[], char ch)
{
int i, j;
for (i = j = 0; str[i] != '\0'; i++)
{
if (str[i] != ch)
{
str[j++] = str[i];
}
}
str[j] = '\0';
}
file4.c
#include <stdio.h>
void print_string(char str[])
{
printf("%s\n", str);
}