关于C变量作用域和生存期的常见问题
2016-07-21 19:12
323 查看
作用域
作用域描述了程序中可以访问一个标识符的一个或多个区域,一个C变量的作用域可以是代码块作用域、函数原型作用域,或者文件作用域。代码块是包含在一对花括号内的一段代码,在代码块中定义的变量具有代码块作用域,从该变量被定义的地方到包含该定义的代码块的末尾该变量均可见,但只局限于代码块。因此,函数作用域也属于代码块作用域。
一个在所有函数之外定义的变量具有文件作用域,从定义开始到包含该文件结尾都可见,文件作用域变量也称为全局变量。
生存期
一个C变量有两种生存期:静态生存期和自动生存期。一个静态变量在程序执行期间将一直存在,一个自动变量在作用域结束后内存将释放。
一般使用 static 声明变量的静态生存期,而对于文件作用域变量,都具有静态生存期。
1 具有代码块作用域的静态变量
未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非被显示初始化)。
在调用包含具有代码块作用域静态变量的函数时,由于其一直存在,因此在每次调用时print()函数时,其它自动变量都被初始化,而temp只在编译print()时初始化一次。
返回下列结果
data = 1 temp = 2
data = 1 temp = 3
data = 1 temp = 4
2 返回字符串的函数
在调用返回字符串的函数时,有时候返回的是垃圾信息。例如下面的程序
因为局部数组在函数调用完成之后,堆栈的空间将释放,因而返回指向数组的指针无效。
一种解决方案是把缓冲区声明成静态生存期,这样在程序运行期间局部数组将一直存在
static char retbuf[20];
另一种解决方案是使用 malloc申请动态内存空间
char * retbuf = malloc(20);
虽然指针retbuf 是局部变量,离开调用函数后将被释放,但是申请的内存空间在没有使用 free之前将一直存在。因此也可能产生了一个问题:申请的内存空间没有被释放,可行的方法是用完返回的字符指针之后,使用free将该地址处的内存释放,前提是这个指针没有移动。
3 static 与全局变量
由于全局变量自动具有静态生存期,在全局变量之前加上 static,表示全局变量的链接方式。
具有外部链接的全局变量,其他文件可以使用;具有部链接的全局变量,属于文件私有,但是可以被该文件中的任一函数使用。为了使程序更加清晰,可以在使用全局变量的函数中通过使用关键字extern 来再次声明它。
如果变量是在别的文件中定义的,使用 extern来声明该变量是必须的。其它文件中引用声明
两个具有相同名称的外部对象实际上代表的是同一个对象,当在两个不同源文件中都包括了一个相同的定义,那么这将表示程序错误(如果连接器禁止外部变量重复定义的话),或者两个源文件共享同一个实例。为了避免这类命名冲突的问题,static是一个有用的工具。
注意:当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。
4 静态函数
在函数的返回类型前加上关键字static,函数就被定义成为静态函数。
函数的定义和声明默认情况下是extern的,与具有内部链接的全局变量一样,静态函数只是在声明它的文件当中可见,不能被其他文件所用。例如
文件分别编译通过,但是连接的时候找不到函数static_dis()的定义,产生错误。
定义静态函数的好处:其他文件中可以定义相同名字的函数,不会发生冲突
作用域描述了程序中可以访问一个标识符的一个或多个区域,一个C变量的作用域可以是代码块作用域、函数原型作用域,或者文件作用域。代码块是包含在一对花括号内的一段代码,在代码块中定义的变量具有代码块作用域,从该变量被定义的地方到包含该定义的代码块的末尾该变量均可见,但只局限于代码块。因此,函数作用域也属于代码块作用域。
一个在所有函数之外定义的变量具有文件作用域,从定义开始到包含该文件结尾都可见,文件作用域变量也称为全局变量。
生存期
一个C变量有两种生存期:静态生存期和自动生存期。一个静态变量在程序执行期间将一直存在,一个自动变量在作用域结束后内存将释放。
一般使用 static 声明变量的静态生存期,而对于文件作用域变量,都具有静态生存期。
#include <stdio.h> int lint = 1; // lint 具有文件作用域、静态生存期 void print(int data) { int i, *p; // 作用域开始,局部变量 for(i=0; i<lint; i++) { static int temp = 1; // 具有代码块作用域、静态生存期 int pd = data * i; // pd作用域开始 . . . temp++; p = &temp; // p指向temp所在地址 printf(“data = %d\t”, pd); } // pd作用域结束 . . . // printf(“temp = %s\n”, temp); // 错误,temp 已离开作用域 printf(“temp = %s\n”, *p); // 正确,temp 不可见,但是一直存在,其地址也可用 } // i 作用域结束
1 具有代码块作用域的静态变量
未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非被显示初始化)。
在调用包含具有代码块作用域静态变量的函数时,由于其一直存在,因此在每次调用时print()函数时,其它自动变量都被初始化,而temp只在编译print()时初始化一次。
int main(void) { int c; for(c=0;c<=3;c++) { print(1); } return 0; }
返回下列结果
data = 1 temp = 2
data = 1 temp = 3
data = 1 temp = 4
2 返回字符串的函数
在调用返回字符串的函数时,有时候返回的是垃圾信息。例如下面的程序
char *itoa(int n) { char retbuf[20]; // 错! sprintf(retbuf, "%d", n); return retbuf; // 错! }
因为局部数组在函数调用完成之后,堆栈的空间将释放,因而返回指向数组的指针无效。
一种解决方案是把缓冲区声明成静态生存期,这样在程序运行期间局部数组将一直存在
static char retbuf[20];
另一种解决方案是使用 malloc申请动态内存空间
char * retbuf = malloc(20);
虽然指针retbuf 是局部变量,离开调用函数后将被释放,但是申请的内存空间在没有使用 free之前将一直存在。因此也可能产生了一个问题:申请的内存空间没有被释放,可行的方法是用完返回的字符指针之后,使用free将该地址处的内存释放,前提是这个指针没有移动。
3 static 与全局变量
由于全局变量自动具有静态生存期,在全局变量之前加上 static,表示全局变量的链接方式。
具有外部链接的全局变量,其他文件可以使用;具有部链接的全局变量,属于文件私有,但是可以被该文件中的任一函数使用。为了使程序更加清晰,可以在使用全局变量的函数中通过使用关键字extern 来再次声明它。
// file1.c int giant = 5; // 全局变量,外部链接 static int dodg = 3; // 全局变量,内部链接 int main(void) { extern int giant; // 可选的声明 extern int dodg; // 可选的声明 . . . }
如果变量是在别的文件中定义的,使用 extern来声明该变量是必须的。其它文件中引用声明
// file2.c static int dodg = 3; // 可以 int giant = 5; // 命名冲突,在file1.c中已经定义了 int main(void) { extern int giant; // 必须的声明 // extern int dodg; // 错误的声明,因为dodg为内部链接 . . . }
两个具有相同名称的外部对象实际上代表的是同一个对象,当在两个不同源文件中都包括了一个相同的定义,那么这将表示程序错误(如果连接器禁止外部变量重复定义的话),或者两个源文件共享同一个实例。为了避免这类命名冲突的问题,static是一个有用的工具。
注意:当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。
4 静态函数
在函数的返回类型前加上关键字static,函数就被定义成为静态函数。
函数的定义和声明默认情况下是extern的,与具有内部链接的全局变量一样,静态函数只是在声明它的文件当中可见,不能被其他文件所用。例如
// file1.c void dis(); static void static_dis(); int main() { dis(); static_dis(); renturn 0; }
// file2.c void dis() { static_dis(); } static void static_dis() { printf("static_dis() of file2.c has been called/n"); }
文件分别编译通过,但是连接的时候找不到函数static_dis()的定义,产生错误。
定义静态函数的好处:其他文件中可以定义相同名字的函数,不会发生冲突
相关文章推荐
- 如何组织构建多文件 C 语言程序(二)
- 如何写好 C main 函数
- 理解C#编程中的静态类和静态成员以及密封类
- Lua和C语言的交互详解
- Lua中的全局变量、非全局变量总结
- js DOM 元素ID就是全局变量
- 关于C语言中参数的传值问题
- 简要对比C语言中三个用于退出进程的函数
- 深入C++中API的问题详解
- 基于C语言string函数的详解
- C语言中fchdir()函数和rewinddir()函数的使用详解
- C语言内存对齐实例详解
- C语言编程中统计输入的行数以及单词个数的方法
- C 语言简单加减乘除运算
- C语言自动生成enum值和名字映射代码
- C语言练习题:自由落体的小球简单实例
- 使用C语言判断英文字符大小写的方法
- c语言实现的带通配符匹配算法
- C语言实现顺序表基本操作汇总
- C语言中进制知识汇总