友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
C语言程序设计-第10部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。
【例8。17】使用寄存器变量。
int fac(int n)
{register int i;f1;
for(i1;ib)?a:b
main(){
int x;y;max;
printf(〃input two numbers:〃);
scanf(〃%d%d〃;&x;&y);
maxMAX(x;y);
printf(〃max%dn〃;max);
}
上例程序的第一行进行带参宏定义,用宏名MAX表示条件表达式(a》b)?a:b,形参a;b均出现在条件表达式中。程序第七行maxMAX(x;y)为宏调用,实参x;y,将代换形参a;b。宏展开后该语句为:
max(x》y)?x:y;
用于计算x;y中的大数。
对于带参的宏定义有以下问题需要说明:
1。 带参宏定义中,宏名和形参表之间不能有空格出现。
例如把:
#define MAX(a;b) (a》b)?a:b
写为:
#define MAX (a;b) (a》b)?a:b
将被认为是无参宏定义,宏名MAX代表字符串 (a;b) (a》b)?a:b。宏展开时,宏调用语句:
maxMAX(x;y);
将变为:
max(a;b)(a》b)?a:b(x;y);
这显然是错误的。
2。 在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。
3。 在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
【例9。5】
#define SQ(y) (y)*(y)
main(){
int a;sq;
printf(〃input a number:〃);
scanf(〃%d〃;&a);
sqSQ(a+1);
printf(〃sq%dn〃;sq);
}
上例中第一行为宏定义,形参为y。程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y) 代换SQ,得到如下语句:
sq(a+1)*(a+1);
这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参。而宏代换中对实参表达式不作计算直接地照原样代换。
4。 在宏定义中,字符串内的形参通常要用括号括起来以避免出错。在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。如果去掉括号,把程序改为以下形式:
【例9。6】
#define SQ(y) y*y
main(){
int a;sq;
printf(〃input a number:〃);
scanf(〃%d〃;&a);
sqSQ(a+1);
printf(〃sq%dn〃;sq);
}
运行结果为:
input a number:3
sq7
同样输入3,但结果却是不一样的。问题在哪里呢? 这是由于代换只作符号代换而不作其它处理而造成的。宏代换后将得到以下语句:
sqa+1*a+1;
由于a为3故sq的值为7。这显然与题意相违,因此参数两边的括号是不能少的。即使在参数两边加括号还是不够的,请看下面程序:
【例9。7】
#define SQ(y) (y)*(y)
main(){
int a;sq;
printf(〃input a number:〃);
scanf(〃%d〃;&a);
sq160/SQ(a+1);
printf(〃sq%dn〃;sq);
}
本程序与前例相比,只把宏调用语句改为:
sq160/SQ(a+1);
运行本程序如输入值仍为3时,希望结果为10。但实际运行的结果如下:
input a number:3
sq160
为什么会得这样的结果呢?分析宏调用语句,在宏代换之后变为:
sq160/(a+1)*(a+1);
a为3时,由于“/”和“*”运算符优先级和结合性相同,则先作160/(3+1)得40,再作40*(3+1)最后得160。为了得到正确答案应在宏定义中的整个字符串外加括号,程序修改如下:
【例9。8】
#define SQ(y) ((y)*(y))
main(){
int a;sq;
printf(〃input a number:〃);
scanf(〃%d〃;&a);
sq160/SQ(a+1);
printf(〃sq%dn〃;sq);
}
以上讨论说明,对于宏定义不仅应在参数两侧加括号,也应在整个字符串外加括号。
5。 带参的宏和带参函数很相似,但有本质上的不同,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理两者的结果有可能是不同的。
【例9。9】
main(){
int i1;
while(i intl3;w4;h5;sa;sb;sc;vv;
SSSV(sa;sb;sc;vv);
printf(〃sa%dnsb%dnsc%dnvv%dn〃;sa;sb;sc;vv);
}
程序第一行为宏定义,用宏名SSSV表示4个赋值语句,4 个形参分别为4个赋值符左部的变量。在宏调用时,把4个语句展开并用实参代替形参。使计算结果送入实参之中。
9。3 文件包含
文件包含是C预处理程序的另一个重要功能。
文件包含命令行的一般形式为:
#include〃文件名〃
在前面我们已多次用此命令包含过库函数的头文件。例如:
#include〃stdio。h〃
#include〃math。h〃
文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。
在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。
对文件包含命令还要说明以下几点:
1。 包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来。例如以下写法都是允许的:
#include〃stdio。h〃
#include
但是这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;
使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。
2。 一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个include命令。
3。 文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。
9。4 条件编译
预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。
条件编译有三种形式,下面分别介绍:
1。 第一种形式:
#ifdef 标识符
程序段1
#else
程序段2
#endif
它的功能是,如果标识符已被 #define命令定义过则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),本格式中的#else可以没有,即可以写为:
#ifdef 标识符
程序段
#endif
【例9。12】
#define NUM ok
main(){
struct stu
{
int num;
char *name;
char sex;
float score;
} *ps;
ps(struct stu*)malloc(sizeof(struct stu));
ps》num102;
ps》name〃Zhang ping〃;
ps》sex'M';
ps》score62。5;
#ifdef NUM
printf(〃Number%dnScore%fn〃;ps》num;ps》score);
#else
printf(〃Name%snSex%cn〃;ps》name;ps》sex);
#endif
free(ps);
}
由于在程序的第16行插入了条件编译预处理命令,因此要根据NUM是否被定义过来决定编译那一个printf语句。而在程序的第一行已对NUM作过宏定义,因此应对第一个printf语句作编译故运行结果是输出了学号和成绩。
在程序的第一行宏定义中,定义NUM表示字符串OK,其实也可以为任何字符串,甚至不给出任何字符串,写为:
#define NUM
也具有同样的意义。只有取消程序的第一行才会去编译第二个printf语句。读者可上机试作。
2。 第二种形式:
#ifndef 标识符
程序段1
#else
程序段2
#endif
与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。
3。 第三种形式:
#if 常量表达式
程序段1
#else
程序段2
#endif
它的功能是,如常量表达式的值为真(非0),则对程序段1 进行编译,否则对程序段2进行编译。因此可以使程序在不同条件下,完成不同的功能。
【例9。13】
#define R 1
main(){
float c;r;s;
printf (〃input a number: 〃);
scanf(〃%f〃;&c);
#if R
r3。14159*c*c;
printf(〃area of round is: %fn〃;r);
#else
sc*c;
printf(〃area of square is: %fn〃;s);
#endif
}
本例中采用了第三种形式的条件编译。在程序第一行宏定义中,定义R为1,因此在条件编译时,常量表达式的值为真,故计算并输出圆面积。
上面介绍的条件编译当然也可以用条件语句来实现。 但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是十分必要的。
9。5 本章小结
1。 预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。程序员在程序中用预处理命令来调用这些功能。
2。 宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表达式。在宏调用中将用该字符串代换宏名。
3。 宏定义可以带有参数,宏调用时是以实参代换形参。而不是“值传送”。
4。 为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两边也应加括号。
5。 文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。
6。 条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。
7。 使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计。
?语言 10
10 指针
10。1 地址指针的基本概念
10。2 变量的指针和指向变量的指针变量
10。2。1 定义一个指针变量
10。2。2 指针变量的引用
10。2。3 指针变量作为函数参数
10。2。4 指针变量几个问题的进一步说明
10。3 数组指针和指向数组的指针变量
10。3。1 指向数组元素的指针
10。3。2 通过指针引用数组元素
10。3。3 数组名作函数参数
10。3。4 指向多维数组的指针和指针变量
10。4 字符串的指针指向字符串的针指变量
10。4。1 字符串的表示形式
10。4。2 使用字符串指针变量与字符数组的区别
10。5 函数指针变量
10。6 指针型函数
10。7 指针数组和指向指针的指针
10。7。1 指针数组的概念
10。7。2 指向指针的指针
10。7。3 main函数的参数
10。8 有关指针的数据类型和指针运算的小结
10。8。1 有关指针的数据类型的小结
10。8。2 指针运
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!