第7章 宏定义
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
22
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
③ 使用全局变量过多,会降低程序的清晰性,人们往往难以清 楚地判断出每个瞬时各个外部变量的值。在各个函数执行时都可能 改变外部变量的值,程序容易出错。因此要限制使用全局变量。 (3)如果外部变量在文件开头定义,则在整个文件范围内都可以使 用该外部变量;如果不在文件开头定义,按上面规定,作用范围只 限于定义点到文件末尾。如果在定义点之前的函数想引用该外部变 量,则应该在该函数中用关键字extern作“外部变量说明”,表示 该变量在函数外部定义,在函数内部可以使用。
静态存储变量在程序运行期间分配固定的存储空间。动态存储 变量在程序的运行过程中根据需要动态分配存储空间。 图5-1是内存中供用户使用的存储空间的分配情况。
12
第七章 编译预处理与变量的存储类型
7.2 变量的存储类型
存储空间可以分为三部分: l 程序区。 l 静态存储区。 l 动态存储区。 数据存放在静态存储区和动态存储区中。全局变量存放在静态存 储区中,在程序执行过程中它们占据固定的存储单元,程序执行 完毕才释放,而不是动态地分配和释放。 在C语言中每一个变量有两个属性:数据类型和存储类别。 存储类别指的是数据在内存中存储的方法。 存储方法分为两大类:静态存储类和动态存储类。具体包含四种: 自动的(auto)、静态的(static)、寄存器的(register)、外 部的(extern)。
7.1 宏定义
7.1.2 带参的宏 带参的宏的一般形式为:
#define 宏名(参数表) 字符串 其中的字符串包含有定义时的参数。如:
#define area(r) (3.1415926(r)(r)) 定义了带参的宏,就可以在程序中使用了。使用时与不带参的 宏基本相同,所不同的是,在宏替换时要用实参置换宏体中的 形参,其他字符不变。
3
第七章 编译预处理与变量的存储类型
7.1 宏定义
【例7.1】求圆周长。 #define PI 3.1415926 double yuan_zchang(double radius) {
return(2 PI radius); } 经宏替换后为 double yuan_zchang(double radius) {
10
第七章 编译预处理与变量的存储类型
7.1 宏定义
④ 调用函数只能得到一个返回值;而用宏可以设法得到几个结 果。 ⑤ 使用宏次数多时,宏展开后源程序长,因为每展开一次都是 程序增长;而函数调用不使源程序变长。 ⑥ 宏替换不占运行时间;而函数调用则占运行时间(分配单元、 保留现场、值传递、执行函数、返回函数值等)。
18
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
【例7.6】全局变量举例。
int p = 1,q = 5; /*外部变量*/
float f1(int a)
/*定义函数f1*/
{
int b,c;
}
char c1,c2 ;
/*外部变量*/
char f2(int x,int y) /*定义函数f2*/
16
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
【例7.5】下列程序中,具有相同变量名的变量出现在三个层次中。
void m来自百度文库in()
{
int x = 1; /*最外层变量初始化并赋值1*/
{
int x = 2; /*第二层变量初始化并赋值2*/
{
int x = 3; /*第三层变量初始化并赋值3*/
#define PI 3.14L5926 (2)如果在双引号中出现与宏名相同的字符,则这个字符不被 替换。
【例7.2】
#define WELCOME "hello" void main() {
printf("WELCOME\n"); printf(WELCOME); }
6
第七章 编译预处理与变量的存储类型
} 15
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
这里,对局部变量作如下说明: (1)主函数main( )中定义的变量(m和n)只在主函数中有效, 主函数也不能使用其他函数中定义的变量。 (2)不同函数中可以使用相同名字的变量,它们代表不同的对象, 互不干扰。例如,在f1( )函数中定义了变量b和c,倘若在f2( )函数 中也定义变量b和c,它们在内存中占不同的单元,互不混淆。 (3)形式参数也是局部变量。 (4)在一个函数内部,可以在语句块(复合语句中)定义变量, 这些变量只在该语句块(复合语句)中有效。
部变量c,它在函数main( )和 mul( )中都是可见的全局变 量。在mul( )中把x*y的结 果赋给变量c;在main( )中 直接引用c的值,也就得到 了x*y的结果。因此,函数
} void mul( int x,int y) {
c=x*y;
mul( )中也不需要用return 语句来返回函数值,mul( ) 可以定义为一个void函数。
2
第七章 编译预处理与变量的存储类型
7.1 宏定义
以#define作为标志的编译预处理命令称为宏定义命令。 7.1.1 不带参数的宏定义 不带参数的宏就是用指定的名字(标识符)来代表一个字符串, 其格式为: #define 标识符 字符串 如:#define PI 3.1415926 其中的标识符又叫宏名,宏名一般由大写字母组成。字符串又叫 宏体。#define、标识符、字符串三者之间用空格分隔。每条预处 理命名占一行,末尾不加分号。#define命令一般出现在文件的开 头,其有效作用域从它出现起到本源文件结束为止。可以用 #define命令终止宏定义的作用域。 实际中,系统对宏的处理是这样的:当遇到宏名时,就用宏体替 换,即所谓的宏替换。这一过程是由预编译程序完成的(不必用 户自己操作),而后才将宏替换后的程序交编译程序进行编译。
13
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
在一个函数内或复合语句中定义的变量称为内部变量。它 只在本函数范围或复合语句内有效,也就是说只有在本函数或 复合语句内才能使用它们,否则是不能使用这些变量的。内部 变量又称为“局部变量”。
14
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
第七章 编译预处理与变量的存储类型
第七章 编译预处理与变量的存储类型
7.1 宏定义 7.2 变量的存储类型
1
第七章 编译预处理与变量的存储类型
第七章 编译预处理与变量的存储类型
如前所述,一个高级语言源程序要在计算机上运行,必须先 用编译程序进行编译(词法分析、语法分析、代码生成)生成目 标文件,而后才可运行。但有时需要在编译之前进行某些“预处 理”工作,如去掉注释、变换格式等。为此,C语言允许在源程 序中使用以“#”号开头且末尾不加分号的语句,进行预处理工作。 从语法上讲,它不同于C语言的一般语句,因而被称为预处理命 令。C语言中的预处理命令有以下几种: (1)宏定义 (2)文件包含 (3)条件编译
20
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
【例7.7】全局变量举例。
由于本例中定义了一个外
int c; void main()
{
int a,b; printf("input a and b: "); scanf("%d,%d",&a,&b); mul(a,b); printf("\na*b=%d\n",c);
7
第七章 编译预处理与变量的存储类型
7.1 宏定义
【例7.3】 #define area(r) (3.1415926(r)(r)) void main() {
printf("%f ",area(3)); } 被替换成: void main() {
printf("%f ",3.141592633); }
【例7.4】局部变量例程
float fl(int a)
/*函数fl( )*/
{
int b,c; 形参a和局部变量b、c有效范围
}
char f2(int x, int y) /*函数f2( )*/
{
int i,j; 形参x,y和局部变量i、j有效范围
}
void main()
/*主函数*/
{
int m,n; 局部变量m和n有效范围
}
21
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
由此可以看出,可以利用全局变量减少函数实参与形参的个数, 从而减少内存空间的占用以及传递数据时的时间消耗。 (2)建议不在必要时不要使用全局变量,因为:
① 全局变量在程序的全部执行过程中都占用存储单元,而 不是仅在需要时才开辟单元。
② 它使函数在执行时依赖于其所用的外部变量。如果将一 个函数转移到另一个文件中,还要将有关的外部变量及其值一起 移过去。但若该外部变量与其他文件的变量同名时,就会出现问 题,从而降低了程序的可靠性和通用性。
#define square(x) xx 遇到下面语句
c = square(3+2); 将被替换成
c = 3+23+2;
9
第七章 编译预处理与变量的存储类型
7.1 宏定义
l 带参数宏定义与函数相似但不同。 ① 函数调用时先求参数表达式的值,然后代入形参。而使用 带参数的宏只是进行简单的字符替换。 ② 函数调用是在程序运行时处理的,并分配临时的内存单元。 而宏展开则是在预处理时进行的,在展开时并不分配内存单元, 不进行值的传递处理,也没有返回值的概念。 ③ 对函数中的实参和形参都要定义类型,并且二者要求一致。 而宏不存在类型问题,宏名无类型,它的参数也无类型,只是 一个符号代表,展开时代入指定的字符即可。宏定义时,字符 串可以是任意类型的数据。
printf("%d\n",x);
}
输出结果:
printf("%d\n",x);
3
}
2
printf("%d\n",x);
1
}
17
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
1. 全局变量及其定义和作用范围 在函数内定义的变量是局部变量,而在函数外定义的变量称
为外部变量,外部变量是全局变量。全局变量可以为本文件中其 他函数所共用。它的有效范围:从定义变量的位置开始到本源文 件结束。如:
19
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
2. 全局变量的有关说明 (1)设立全局变量增加了函数间数据联系的渠道。如果全局变量 定义在源文件的前面位置,则由于一个文件中的所有函数都能引 用全局变量的值,因此如果在一个函数中改变了全局变量的值, 就能影响到其他函数,相当于各个函数都有直接的传递通道,从 函数得到一个以上的返回值。
{
int i,j;
}
void main( )
/*主函数*/
{
int m,n;
}
p、q、c1、c2都是全局 变量,但它们的作用范 围不同,在main( )函数 和函数f2( )中可以使用 全局变量p、q、c1、c2, 但在函数f1( )中只能使 用全局变量p、q,而不 能使用c1和c2。 在一个函数中既可以使 用本函数中的局部变量, 又可以使用有效的全局 变量。
8
第七章 编译预处理与变量的存储类型
7.1 宏定义
使用带参的宏应注意以下几点: l 带参的宏定义中,宏名与带参数的圆括号之间不能有空格。如: #define area (r) (3.1415926(r)(r))
系统会认为这是一个不带参的宏,宏名为area,宏体为 (r) (3.1415926(r)(r))。 l 整个宏体及其中所含形参要用括号括起(也就是说,括号不 是宏体的一部分),否则就会出错。如:
return(2 3.1415926 radius); }
4
第七章 编译预处理与变量的存储类型
7.1 宏定义
使用宏的目的主要有两个: (1)提高程序的可读性
在程序中使用到的常数往往都有一定的物理含义,但我们很 难从数字本身看出它的含义。有了宏问题就解决了。 如: #define TRUE 1
#define FALSE 0 这样在源程序中就可以使用标识符TRUE和FALSE来表示逻 辑真与假。 (2)易于程序的修改 在程序中使用到的某一常数往往在多处出现,如要对它修改, 需修改多处且需排除形式相同含义不同的量。
5
第七章 编译预处理与变量的存储类型
7.1 宏定义
使用宏时还应注意以下问题: (1)在预编译时的,宏替换只是简单地用宏体(字符串)去替 代宏名而不进行语法和语义的检查。如下边的宏定义,在预编 译时也是正确的,只是在以后的编译中才报告出错。
11
第七章 编译预处理与变量的存储类型
7.2 变量的存储类型
从变量的作用域(即从空间)角度来分,可以将变量分为全局 变量和局部变量。从变量存在的时间(即生存期)角度来分,可以 分为静态存储变量和动态存储变量。动态数据存储在内存的堆栈区, 静态数据存储在内存的固定存储区,即静态存储区。全局变量存储 在内存的固定存储区,局部变量存储在内存的堆栈区。
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
③ 使用全局变量过多,会降低程序的清晰性,人们往往难以清 楚地判断出每个瞬时各个外部变量的值。在各个函数执行时都可能 改变外部变量的值,程序容易出错。因此要限制使用全局变量。 (3)如果外部变量在文件开头定义,则在整个文件范围内都可以使 用该外部变量;如果不在文件开头定义,按上面规定,作用范围只 限于定义点到文件末尾。如果在定义点之前的函数想引用该外部变 量,则应该在该函数中用关键字extern作“外部变量说明”,表示 该变量在函数外部定义,在函数内部可以使用。
静态存储变量在程序运行期间分配固定的存储空间。动态存储 变量在程序的运行过程中根据需要动态分配存储空间。 图5-1是内存中供用户使用的存储空间的分配情况。
12
第七章 编译预处理与变量的存储类型
7.2 变量的存储类型
存储空间可以分为三部分: l 程序区。 l 静态存储区。 l 动态存储区。 数据存放在静态存储区和动态存储区中。全局变量存放在静态存 储区中,在程序执行过程中它们占据固定的存储单元,程序执行 完毕才释放,而不是动态地分配和释放。 在C语言中每一个变量有两个属性:数据类型和存储类别。 存储类别指的是数据在内存中存储的方法。 存储方法分为两大类:静态存储类和动态存储类。具体包含四种: 自动的(auto)、静态的(static)、寄存器的(register)、外 部的(extern)。
7.1 宏定义
7.1.2 带参的宏 带参的宏的一般形式为:
#define 宏名(参数表) 字符串 其中的字符串包含有定义时的参数。如:
#define area(r) (3.1415926(r)(r)) 定义了带参的宏,就可以在程序中使用了。使用时与不带参的 宏基本相同,所不同的是,在宏替换时要用实参置换宏体中的 形参,其他字符不变。
3
第七章 编译预处理与变量的存储类型
7.1 宏定义
【例7.1】求圆周长。 #define PI 3.1415926 double yuan_zchang(double radius) {
return(2 PI radius); } 经宏替换后为 double yuan_zchang(double radius) {
10
第七章 编译预处理与变量的存储类型
7.1 宏定义
④ 调用函数只能得到一个返回值;而用宏可以设法得到几个结 果。 ⑤ 使用宏次数多时,宏展开后源程序长,因为每展开一次都是 程序增长;而函数调用不使源程序变长。 ⑥ 宏替换不占运行时间;而函数调用则占运行时间(分配单元、 保留现场、值传递、执行函数、返回函数值等)。
18
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
【例7.6】全局变量举例。
int p = 1,q = 5; /*外部变量*/
float f1(int a)
/*定义函数f1*/
{
int b,c;
}
char c1,c2 ;
/*外部变量*/
char f2(int x,int y) /*定义函数f2*/
16
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
【例7.5】下列程序中,具有相同变量名的变量出现在三个层次中。
void m来自百度文库in()
{
int x = 1; /*最外层变量初始化并赋值1*/
{
int x = 2; /*第二层变量初始化并赋值2*/
{
int x = 3; /*第三层变量初始化并赋值3*/
#define PI 3.14L5926 (2)如果在双引号中出现与宏名相同的字符,则这个字符不被 替换。
【例7.2】
#define WELCOME "hello" void main() {
printf("WELCOME\n"); printf(WELCOME); }
6
第七章 编译预处理与变量的存储类型
} 15
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
这里,对局部变量作如下说明: (1)主函数main( )中定义的变量(m和n)只在主函数中有效, 主函数也不能使用其他函数中定义的变量。 (2)不同函数中可以使用相同名字的变量,它们代表不同的对象, 互不干扰。例如,在f1( )函数中定义了变量b和c,倘若在f2( )函数 中也定义变量b和c,它们在内存中占不同的单元,互不混淆。 (3)形式参数也是局部变量。 (4)在一个函数内部,可以在语句块(复合语句中)定义变量, 这些变量只在该语句块(复合语句)中有效。
部变量c,它在函数main( )和 mul( )中都是可见的全局变 量。在mul( )中把x*y的结 果赋给变量c;在main( )中 直接引用c的值,也就得到 了x*y的结果。因此,函数
} void mul( int x,int y) {
c=x*y;
mul( )中也不需要用return 语句来返回函数值,mul( ) 可以定义为一个void函数。
2
第七章 编译预处理与变量的存储类型
7.1 宏定义
以#define作为标志的编译预处理命令称为宏定义命令。 7.1.1 不带参数的宏定义 不带参数的宏就是用指定的名字(标识符)来代表一个字符串, 其格式为: #define 标识符 字符串 如:#define PI 3.1415926 其中的标识符又叫宏名,宏名一般由大写字母组成。字符串又叫 宏体。#define、标识符、字符串三者之间用空格分隔。每条预处 理命名占一行,末尾不加分号。#define命令一般出现在文件的开 头,其有效作用域从它出现起到本源文件结束为止。可以用 #define命令终止宏定义的作用域。 实际中,系统对宏的处理是这样的:当遇到宏名时,就用宏体替 换,即所谓的宏替换。这一过程是由预编译程序完成的(不必用 户自己操作),而后才将宏替换后的程序交编译程序进行编译。
13
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
在一个函数内或复合语句中定义的变量称为内部变量。它 只在本函数范围或复合语句内有效,也就是说只有在本函数或 复合语句内才能使用它们,否则是不能使用这些变量的。内部 变量又称为“局部变量”。
14
第七章 编译预处理与变量的存储类型
7.2.1 局部变量
第七章 编译预处理与变量的存储类型
第七章 编译预处理与变量的存储类型
7.1 宏定义 7.2 变量的存储类型
1
第七章 编译预处理与变量的存储类型
第七章 编译预处理与变量的存储类型
如前所述,一个高级语言源程序要在计算机上运行,必须先 用编译程序进行编译(词法分析、语法分析、代码生成)生成目 标文件,而后才可运行。但有时需要在编译之前进行某些“预处 理”工作,如去掉注释、变换格式等。为此,C语言允许在源程 序中使用以“#”号开头且末尾不加分号的语句,进行预处理工作。 从语法上讲,它不同于C语言的一般语句,因而被称为预处理命 令。C语言中的预处理命令有以下几种: (1)宏定义 (2)文件包含 (3)条件编译
20
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
【例7.7】全局变量举例。
由于本例中定义了一个外
int c; void main()
{
int a,b; printf("input a and b: "); scanf("%d,%d",&a,&b); mul(a,b); printf("\na*b=%d\n",c);
7
第七章 编译预处理与变量的存储类型
7.1 宏定义
【例7.3】 #define area(r) (3.1415926(r)(r)) void main() {
printf("%f ",area(3)); } 被替换成: void main() {
printf("%f ",3.141592633); }
【例7.4】局部变量例程
float fl(int a)
/*函数fl( )*/
{
int b,c; 形参a和局部变量b、c有效范围
}
char f2(int x, int y) /*函数f2( )*/
{
int i,j; 形参x,y和局部变量i、j有效范围
}
void main()
/*主函数*/
{
int m,n; 局部变量m和n有效范围
}
21
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
由此可以看出,可以利用全局变量减少函数实参与形参的个数, 从而减少内存空间的占用以及传递数据时的时间消耗。 (2)建议不在必要时不要使用全局变量,因为:
① 全局变量在程序的全部执行过程中都占用存储单元,而 不是仅在需要时才开辟单元。
② 它使函数在执行时依赖于其所用的外部变量。如果将一 个函数转移到另一个文件中,还要将有关的外部变量及其值一起 移过去。但若该外部变量与其他文件的变量同名时,就会出现问 题,从而降低了程序的可靠性和通用性。
#define square(x) xx 遇到下面语句
c = square(3+2); 将被替换成
c = 3+23+2;
9
第七章 编译预处理与变量的存储类型
7.1 宏定义
l 带参数宏定义与函数相似但不同。 ① 函数调用时先求参数表达式的值,然后代入形参。而使用 带参数的宏只是进行简单的字符替换。 ② 函数调用是在程序运行时处理的,并分配临时的内存单元。 而宏展开则是在预处理时进行的,在展开时并不分配内存单元, 不进行值的传递处理,也没有返回值的概念。 ③ 对函数中的实参和形参都要定义类型,并且二者要求一致。 而宏不存在类型问题,宏名无类型,它的参数也无类型,只是 一个符号代表,展开时代入指定的字符即可。宏定义时,字符 串可以是任意类型的数据。
printf("%d\n",x);
}
输出结果:
printf("%d\n",x);
3
}
2
printf("%d\n",x);
1
}
17
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
1. 全局变量及其定义和作用范围 在函数内定义的变量是局部变量,而在函数外定义的变量称
为外部变量,外部变量是全局变量。全局变量可以为本文件中其 他函数所共用。它的有效范围:从定义变量的位置开始到本源文 件结束。如:
19
第七章 编译预处理与变量的存储类型
7.2.2 全局变量
2. 全局变量的有关说明 (1)设立全局变量增加了函数间数据联系的渠道。如果全局变量 定义在源文件的前面位置,则由于一个文件中的所有函数都能引 用全局变量的值,因此如果在一个函数中改变了全局变量的值, 就能影响到其他函数,相当于各个函数都有直接的传递通道,从 函数得到一个以上的返回值。
{
int i,j;
}
void main( )
/*主函数*/
{
int m,n;
}
p、q、c1、c2都是全局 变量,但它们的作用范 围不同,在main( )函数 和函数f2( )中可以使用 全局变量p、q、c1、c2, 但在函数f1( )中只能使 用全局变量p、q,而不 能使用c1和c2。 在一个函数中既可以使 用本函数中的局部变量, 又可以使用有效的全局 变量。
8
第七章 编译预处理与变量的存储类型
7.1 宏定义
使用带参的宏应注意以下几点: l 带参的宏定义中,宏名与带参数的圆括号之间不能有空格。如: #define area (r) (3.1415926(r)(r))
系统会认为这是一个不带参的宏,宏名为area,宏体为 (r) (3.1415926(r)(r))。 l 整个宏体及其中所含形参要用括号括起(也就是说,括号不 是宏体的一部分),否则就会出错。如:
return(2 3.1415926 radius); }
4
第七章 编译预处理与变量的存储类型
7.1 宏定义
使用宏的目的主要有两个: (1)提高程序的可读性
在程序中使用到的常数往往都有一定的物理含义,但我们很 难从数字本身看出它的含义。有了宏问题就解决了。 如: #define TRUE 1
#define FALSE 0 这样在源程序中就可以使用标识符TRUE和FALSE来表示逻 辑真与假。 (2)易于程序的修改 在程序中使用到的某一常数往往在多处出现,如要对它修改, 需修改多处且需排除形式相同含义不同的量。
5
第七章 编译预处理与变量的存储类型
7.1 宏定义
使用宏时还应注意以下问题: (1)在预编译时的,宏替换只是简单地用宏体(字符串)去替 代宏名而不进行语法和语义的检查。如下边的宏定义,在预编 译时也是正确的,只是在以后的编译中才报告出错。
11
第七章 编译预处理与变量的存储类型
7.2 变量的存储类型
从变量的作用域(即从空间)角度来分,可以将变量分为全局 变量和局部变量。从变量存在的时间(即生存期)角度来分,可以 分为静态存储变量和动态存储变量。动态数据存储在内存的堆栈区, 静态数据存储在内存的固定存储区,即静态存储区。全局变量存储 在内存的固定存储区,局部变量存储在内存的堆栈区。