变量的作用域
变量的存储类别
宏定义
文件包含
条件编译
变量的作用域
变量都有自己的作用域,按照作用域的范围可分为两种:局部变量和全局变量。
局部变量
在
关于局部变量的说明如下
main 函数内申明的变量也只在 main 函数内有效,main 函数不能使用其他函数中声明的局部变量。
不同函数内可以声明同名的局部变量,他们代表不同的对象,互不干扰。
形参也是局部变量。
全局变量
在
关于全局变量的说明如下
全局变量增加了函数间数据联系的渠道。
如果全局变量与局部变量同名,则在局部变量的作用范围内,全局变量被屏蔽,不起作用。
编程练习1
编写并调用函数 exchange ,用于交换 int 类型全局变量 x 、 y 的值。
1 |
|
变量的存储类别
变量的存储类别分为六种:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)、typedef、_Thread_local。
静态存储变量:是指在程序运行期间分配固定的存储空间的方式。 动态存储变量:是指在程序运行期间根据需要进行动态的分配空间的方式。内存中供用户使用的存储空间的情况分为三部分:程序区;静态存储区;动态存储区。
全局变量存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地分配和释放的。
局部变量的存储类别
- 函数中的局部变量,如不做专门的说明,都是动态的分配存储空间的,存储在动态存储区中。对它们分配和释放存储空间的工作是由编译系统自动处理的,因此这类局部变量称为自动变量。
- 自动变量用关键字auto作存储类型的说明。
- 如果声明局部变量时没有指定存储类别,默认为auto,通常称为自动变量。例如:int n; // 等价于 auto int n;
- 有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量的值是上一次函数调用结束时的值。这时就可以指定该局部变量为“静态局部变量”,用static指定存储类别。
- 静态局部变量的说明:
- 静态局部变量占用的存储单元在程序整个运行期间都不释放;而自动变量在函数调用时分配存储单元,函数调用结束,存储单元被释放。
- 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新赋一次初值。
- 声明静态局部变量时没有初始化,静态局部变量的值是0(对数值型变量)或空字符(对char类型变量);声明自动变量时没有初始化,自动变量的值是不确定的。
例题1
静态局部变量实例。
1 |
|
运行结果
1 | 7 8 9 |
先后三次调用函数 f 时,变量 b 、 c 的值如图所示:
例题2
输出1 ~ 5 的阶乘值。
1 |
|
运行结果
1 | 1! = 1 |
如果有一些变量使用频繁,为提高执行效率,C 语言允许将局部变量的值存储在寄存器中,以提高执行效率。这种变量叫寄存器变量,用register指定存储类别。
全局变量的存储类别
- 全局变量有 extern 和 static 两种存储类别。如果声明全局变量时没有指定存储类别,默认为 extern 。
- 如果全局变量不在文件的开头声明,其作用域只限于声明点到源文件末尾。如果在声明点之前的函数想使用该全局变量,则应该在使用之前用关键字extern对该变量声明,表示该变量是一个已经声明的全局变量,以扩展全局变量的作用域。
例题1
用 extern 声明全局变量,扩展全局变量的作用域。
1 |
|
该程序的运行结果:
1 | max = 15 |
原本 main 函数不在全局变量 a 、 b 的作用范围内,用 extern 对使用全局变量 a 、 b 进行声明,就可以在 main 函数内使用全局变量 a 、 b 。
宏定义
编译预处理是在编译前对源程序进行的一些预加工。它是 C 语言与其他高级语言的一个重要区别,有助于编写易移植、易调试的程序。
不带参数的宏定义
一般形式:
1 |
例题1
计算圆的面积。
1 |
|
点击查看说明
宏名的替换过程其实是一种简单的复制工作,它不做任何计算,也不做任何错误检查,错误的检查工作要等到编译过程中才会进行。
宏名通常用大写字母表示。
例题2
计算圆的周长。
1 |
|
运行结果
1 | L = 18.85 |
带参数的宏定义
一般形式:
1 |
点击查看注意
宏名与参数表的括号之间不可有空格符;否则,系统将把程序中的宏名替换成空格后的字符串,变成不带参数的宏定义。
例题1
计算长方形的面积。
1 |
|
运行结果
1 | Input the length and the width: 3 4 |
带参数的宏定义与带形参的函数,他们从形式上与特征上都很相似,但他们在本质上是不同的:
- 函数调用时是在程序的执行过程中进行的,而宏调用是在预处理中进行的,宏名被替换后,宏体将是程序的一部分。
- 在函数调用时,要先求实参的值,然后将这个值赋给形参,而宏调用时,只是简单地用实参替换宏体中的形参。
例题2
宏调用是简单的替换。
1 |
|
运行结果
1 | SUM = 11 |
编程练习
定义并调用带参数的宏,交换两个参数的值。
1 |
|
运行结果
1 | x = 5 , y = 3 |
文件包含
文件包含也叫宏包含,是编译预处理的一个重要功能,通过预处理命令#include把另一个文件的整个内容嵌入进来。文件包含有以下两种方式:
第一种形式
1
首先在源文件所在目录中检索指定的文件;如果没有找到,则按系统规定的标准方式检索文件目录,直至找到文件为止。
第二种形式
1 |
只按系统规定的标准方式检索文件目录,直至找到文件为止。
条件编译
编译预处理提供了条件编译的功能,可以根据条件编译程序的不同部分,从而产生不同的目标代码文件。
第一种形式
1 |
|
功能:如果标识符已被 #define命令定义过,则对程序段1进行编译;否则,对程序段2进行编译。
第二种形式
1 |
|
功能:如果标识符没有被 #define命令定义过,则对程序段1进行编译;否则,对程序段2进行编译。这与第一种形式的功能正好相反。
第三种形式
1 |
|
功能:如果常量表达式的值不等于0,则对程序段1进行编译;否则,对程序段2进行编译。
例题1
条件编译示例。
1 |
|
运行结果
1 | Input the radius: 5 |