在C语言中,所有变量、宏、函数(函数的声明形式即为函数原型)、自定义数据类型以及别名等数据必须先声明后使用。通常在头文件中包含这些声明,如extern int global_data,这个声明只是告诉编译器全局变量的类型和名字,至于对它的定义(分配内存的声明即为定义)则在别处进行。
1、作用域
在C语言中,常见的有代码块(一对花括号之间的代码)作用域和文件作用域。
所有函数之外定义的变量叫作全局变量(也叫外部变量,默认具有静态存储特性)。某个代码块之内定义的变量(包括函数定义中的参数)就是局部变量(默认具有动态存储特性,使用static修饰会使用局部变量具有静态特性)。局部变量只具有空链接特性,即只能在定义它的代码块中被访问。
文件作用域是对全局变量和函数而言的。默认都具有外部链接性(即可以被多文件程序的其他文件中的数据所访问),加static修饰时只具有内部链接性(也就是说只能被本文件的数据所访问)。
知识点:
(1)、具有静态存储期的变量在程序执行过程中一直存在。而具有动态存储期的变量只有在执行到这些变量所属的代码块时才为其分配内存,并且在退出此代码块时,就释放掉所分配的内存。
举例,如清单1:
#include <stdio.h>
char *func(void)
{
char a[] = "hello world";
return a;
}
int main(void)
{
char *p = NULL;
p = func();
printf("%s\n", p);
return 0;
}
用GCC编译时,会警告说函数不能返回局部变量的地址。程序执行时,不能正确输出数组a中的字符,只是一些乱码,那是因为在第14行的函数调用之后,数组a就已经被释放掉了。
注意:指针变量p可以获得函数func返回的数组a的首地址,但数组a中的值在调用之后就已经不存在了。
(2)、在程序员未明确赋初始值的情况下,编译器自动把静态变量(即具有静态存储期的变量,包括全局变量和用static修饰的局部变量)初始化为0或NULL(这个值是针对指针变量而言的),而动态变量(即具有动态存储期的变量)的初始值是不确定的。
2、传值调用
由于函数中的数据只为本函数所有,所以在C语言中,函数之间交往数据的通路只有两种:全局变量和传值调用。
普通变量的传值调用,如清单2:
#include <stdio.h>
void func(int a, int b)
{
printf("in func, address a = %p, b = %p\n", &a, &b);
printf("in func, value a = %d, b = %d\n", a, b);
}
int main(void)
{
int a = 3, b = 4;
func(a, b);
printf("in main, address a = %p, b = %p\n", &a, &b);
printf("in main, value a = %d, b = %d\n", a, b);
return 0;
}
例子输出结果:
in func, address a = 0xbf8cc770, b = 0xbf8cc774
in func, value a = 3, b = 4
in main, address a = 0xbf8cc78c, b = 0xbf8cc788
in main, value a = 3, b = 4
在main函数中,通过函数传值调用(例子中的第14行)把变量a和b的值传给func函数中的a和b(整个传值过程就相当于赋值)。注意,这两个函数中的变量a和b虽然同名,但互不影响,属于不同的变量,从结果中它们的地址就可知。
与指针变量有关的传值调用,如清单3:
#include <stdio.h>
void func(int *p1, int *p2)
{
*p1 += 5;
*p2 += 5;
}
int main(void)
{
int a = 3, b = 4;
printf("before calling, a = %d, b = %d\n", a, b);
func(&a, &b);
printf("after calling, a = %d, b = %d\n", a, b);
return 0;
}
例子输出结果:
before calling, a = 3, b = 4
after calling, a = 8, b = 9
在main函数中,把变量a和b的地址(也是一个数值)传给func函数中的指针变量p1和p2,然后通过间接运算符来改变a和b的值。
相对复杂的例子,如清单4:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void func(char *p)
{
p = (char *)malloc(20);
}
int main(void)
{
char *str = NULL;
func(str);
strcpy(str, "hello world");
printf("string is %s\n", str);
return 0;
}
在例子中,通过函数调用,把指针变量str的值NULL传递给func函数的指针变量p,而后p的值又被malloc函数返回的地址所覆盖,其实例子中的函数调用对str值的改变没有任何影响,一直都是NULL,而往NULL中拷贝数据会产生段错误。
注意,用函数malloc分配的内存必须通过free函数来释放,否则在程序执行过程中,所分配的内存一直被占用。