c语言高级教程第四章.ppt
李堂秋 厦门大学计算机科学编写C与C+程序设计 第四章 函数和程序结构前言 使用函数,可以把大的计算任务分解,换句使用函数,可以把大的计算任务分解,换句话说,可以使用许多函数的组合来完成复杂的任话说,可以使用许多函数的组合来完成复杂的任务。而且在务。而且在C C中,并非所有的函数都要自己定义,中,并非所有的函数都要自己定义,可以使用别人已做好的程序模块。在设计主函数可以使用别人已做好的程序模块。在设计主函数的时候,可以将不必要的细节蕴藏起来,以便更的时候,可以将不必要的细节蕴藏起来,以便更容易把握主线和全局。如果程序太大,还可以将容易把握主线和全局。如果程序太大,还可以将C C语言写的程序分放多个文件,分别编译,最后语言写的程序分放多个文件,分别编译,最后再联接起来成为整体。再联接起来成为整体。4.1 函数的基础 看看我们如何编一个程序,看看我们如何编一个程序,首先要明确我们要做什首先要明确我们要做什么事请,比如要读一个文件,找出所有包含某一字么事请,比如要读一个文件,找出所有包含某一字符串的行并把它打印出来。符串的行并把它打印出来。思路:思路:while(while(读入一个新行读入一个新行)if(if(如果如果这个新行包含指定的字符串这个新行包含指定的字符串)打印这个新行打印这个新行读入一个新行打印这一行判断这一行是否指定的字符串函数的基础(续)()除了除了 和()、和()、其他都可以省略,如:其他都可以省略,如:dummy();dummy();数据传入数据通过return;传回给调用函数#include#include#define MAXLINE 1000#define MAXLINE 1000int getline(char line,int max);int getline(char line,int max);int strindex(char source,char searchfor);int strindex(char source,char searchfor);char pattern =“ould”;/*pattern to search for*/char pattern =“ould”;/*pattern to search for*/*find all lines matching pattern*/*find all lines matching pattern*/main()main()char lineMAXLINE;char lineMAXLINE;int found=0;int found=0;while(getline(line,MAXLINE)0)while(getline(line,MAXLINE)0)if(strindex(line,pattern)=0)if(strindex(line,pattern)=0)printf(“%s”,line);printf(“%s”,line);found+;found+;return found;return found;主函数/*getline:get line into s,return length*/*getline:get line into s,return length*/int getlineint getline(char s,int lim)(char s,int lim)int c,i;int c,i;i=0;i=0;while(-lim 0&(c=getchar()!=EOF&c!=n)while(-lim 0&(c=getchar()!=EOF&c!=n)si+=c;si+=c;if(c=n)if(c=n)si+=c;si+=c;si=0;si=0;return i;return i;/*strindex:return index of t in s,-1 if none*/*strindex:return index of t in s,-1 if none*/int strindex(char s,char t)int strindex(char s,char t)int i,j,k;int i,j,k;for(i=0;si!=0;i+)for(i=0;si!=0;i+)for(j=i,k=0;tk!=0&sj=tk;j+,k+);for(j=i,k=0;tk!=0&sj=tk;j+,k+);if(k 0&tk=0)if(k 0&tk=0)return i;return i;return-1;return-1;子函数4.2 返回非整数的函数 在使用返回值不是整数的函数时,正确地进行函数的定义和在使用返回值不是整数的函数时,正确地进行函数的定义和函数说明十分重要。函数说明十分重要。函数定义函数定义时一定要说明返回值类型,否则缺省为整数。时一定要说明返回值类型,否则缺省为整数。为了使调用函数知道被调用函数的类型,函数调用为了使调用函数知道被调用函数的类型,函数调用前一定要前一定要进行原形说明,否则,如果函数定义的类型与调用类型出现进行原形说明,否则,如果函数定义的类型与调用类型出现不一致:不一致:如果调用和被调用函数在同一个文件中定义,编译器会提出警如果调用和被调用函数在同一个文件中定义,编译器会提出警告,这是实行原型说明的好处。告,这是实行原型说明的好处。如果不在一个文件中定义,编译器不会发现问题,即:如果不在一个文件中定义,编译器不会发现问题,即:在调用前在调用前没有原型说明的函数没有原型说明的函数,被理解为返回整数,而对它被理解为返回整数,而对它的变元毫无假定的变元毫无假定。函数原型说明中函数原型说明中没有给出变量说明的没有给出变量说明的,编译器对变元不做任,编译器对变元不做任何检查。何检查。子函数把字符串转换成浮点数#include#include/*atof:convert string s to double*/*atof:convert string s to double*/doubledouble atof(char s)atof(char s)double val,power;double val,power;int i,sign;int i,sign;for(i=0;isspace(si);i+)/*skip leading spaces*/for(i=0;isspace(si);i+)/*skip leading spaces*/;sign=(si=-)?-1:1;sign=(si=-)?-1:1;if(si=+|si=-)if(si=+|si=-)i+;i+;for(val=0.0;isdigit(si);i+)for(val=0.0;isdigit(si);i+)val=10*val+(si-0);val=10*val+(si-0);if(si=.)if(si=.)i+;i+;for(power=1.0;isdigit(si);i+)for(power=1.0;isdigit(si);i+)val=10*val+(si-0);val=10*val+(si-0);power*=10.0;power*=10.0;returnreturn sign*val/power;sign*val/power;主函数读入数字并把它们累加起来#include#include#define MAXLINE 100#define MAXLINE 100/*a rudimentary calculator*/*a rudimentary calculator*/main()main()double sum,atof(char);double sum,atof(char);char lineMAXLINE;char lineMAXLINE;int getline(char line,int max);int getline(char line,int max);sum=0;sum=0;while(getline(line,MAXLINE)0)while(getline(line,MAXLINE)0)printf(“t%gn”,sum+=atof(line);printf(“t%gn”,sum+=atof(line);return 0;return 0;4.3 外部变量-全局变量 全局变量是在所有函数外部定义的变量,它有如下的特性:全局变量是在所有函数外部定义的变量,它有如下的特性:全程可见性:对程序中全局变量,通过适当说明,所有函授全程可见性:对程序中全局变量,通过适当说明,所有函授都可以通过它的唯一的都可以通过它的唯一的“名字名字”对它存取,即使这些函数是对它存取,即使这些函数是在不同文件并且是分别编译的。在不同文件并且是分别编译的。可见性可控:当然,也可以通过适当的说明,使某些全局变可见性可控:当然,也可以通过适当的说明,使某些全局变量只在一个文件中有效。量只在一个文件中有效。全程生命周期:全局变量从程序执行时就存在,并且一直存全程生命周期:全局变量从程序执行时就存在,并且一直存在直到程序结束。在直到程序结束。可增加效率:可以用来保留中间结果,如果许多函数使用大可增加效率:可以用来保留中间结果,如果许多函数使用大量的共同数据,适度的使用外部变量,可以避免大量数据的量的共同数据,适度的使用外部变量,可以避免大量数据的传递,对于简化程序是有益的。传递,对于简化程序是有益的。函数是全局的:函数是全局的:C C程序是由一系列外部变量和函数组成的,程序是由一系列外部变量和函数组成的,C C中所有的函数都是全局的,不能在函数中定义函数。中所有的函数都是全局的,不能在函数中定义函数。局部变量或内部变量(2)全局变量又叫外部变量,它与内部变量相对应,函数的参数全局变量又叫外部变量,它与内部变量相对应,函数的参数变量和函数内部定义的变量叫内部变量。变量和函数内部定义的变量叫内部变量。内部变量的可见性是局部的:内部变量又称局部变量,只在内部变量的可见性是局部的:内部变量又称局部变量,只在函数内可见,当局部变量与某全局变量同名时,全局变量受函数内可见,当局部变量与某全局变量同名时,全局变量受到屏蔽。其好处是,不同的函数的局部变量可以同名,他们到屏蔽。其好处是,不同的函数的局部变量可以同名,他们之间不会产生混淆。之间不会产生混淆。局部变量的生命周期短:从函数被调用的时刻起存在,到函局部变量的生命周期短:从函数被调用的时刻起存在,到函数调用结束时消亡。数调用结束时消亡。函数的局部动态变量在两次调用之间没有关系,更不会相互函数的局部动态变量在两次调用之间没有关系,更不会相互影响。影响。使用局部变量,可以保证程序的模块化使用局部变量,可以保证程序的模块化 但在特定的情况下使用全局变量会简化参数的传递。但在特定的情况下使用全局变量会简化参数的传递。权衡权衡例子:说明外部变量的正确使用 设计一个可以做加减乘除的计算器,输入采用反序波兰表达式:设计一个可以做加减乘除的计算器,输入采用反序波兰表达式:1 2 -4 5 +*1 2 -4 5 +*表示:(表示:(1-21-2)*(4+54+5)处理的算法如下处理的算法如下:while(while(下一个字符是操作数或操作符并且不是文件结束下一个字符是操作数或操作符并且不是文件结束)if(if(是是数字数字)压入栈中压入栈中else if(else if(是是操作符操作符)弹弹出操作数出操作数 实行操作实行操作 压入栈中压入栈中else if(else if(是是新行新行)弹弹出操作数并打印结果出操作数并打印结果else else 出出错错这里的主要问题是决策:公共存取的变量放在何处?这里的主要问题是决策:公共存取的变量放在何处?-外部还是局外部还是局部?部?主程序#include include#include#include#define MAXOP 100#define MAXOP 100#define NUMBER 0#define NUMBER 0int getop(char );/*int getop(char );/*原型说明原型说明*/*/void push(double);void push(double);double pop(void):double pop(void):/*reverse polish calculator*/*reverse polish calculator*/main()main()int type;int type;double op2;double op2;char sMAXOP;char sMAXOP;while(type=getop(s)!=EOF)while(type=getop(s)!=EOF)switch(type)switch(type)case NUMBER:push(atof(s);break;case NUMBER:push(atof(s);break;case+:push(pop()+pop();break;case+:push(pop()+pop();break;case*:push(pop()*pop();break;case*:push(pop()*pop();break;case-:op2=pop();push(pop()-op2);break;case-:op2=pop();push(pop()-op2);break;case/:op2=pop();case/:op2=pop();if(op2!=0.0)push(pop()/op2);if(op2!=0.0)push(pop()/op2);else printf(“error:zero divisorn”);else printf(“error:zero divisorn”);break;break;case n:printf(“t%.8gn”,pop();break;case n:printf(“t%.8gn”,pop();break;default:printf(“error:unknown command%sn”,s);break;default:printf(“error:unknown command%sn”,s);break;return 0;return 0;子程序1 /*/*解决堆栈和出栈的数据存储和操作解决堆栈和出栈的数据存储和操作*/#define MAXVAL 100 /#define MAXVAL 100int sp=0;int sp=0;double valMAXVAL;/*value stack*/double valMAXVAL;/*value stack*/*push:push f onto value stack*/*push:push f onto value stack*/void push(double f)void push(double f)if(sp MAXVAL)if(sp 0)if(sp 0)return val-sp;return val-sp;else else printf(“error:stack emptyn”):printf(“error:stack emptyn”):return 0.0;return 0.0;子程序2#include#include int getch(void);int getch(void);void ungetch(int);void ungetch(int);/*getch:get next operator or numeric operand/*getch:get next operator or numeric operand 解决数据和操作符的输入解决数据和操作符的输入*/*/int getop(char s)int getop(char s)int i,c;int i,c;while(s0=c=getch()=|c=t)while(s0=c=getch()=|c=t);s1=0;s1=0;if(!isdigit(c)&c!=.)if(!isdigit(c)&c!=.)return c;/*not a number*/return c;/*not a number*/i=0;i=0;if(isdigit(c)if(isdigit(c)while(isdigit(s+i=c=getch()while(isdigit(s+i=c=getch();/*collect interger part*/;/*collect interger part*/if(c=.)if(c=.)while(isdigit(s+i=c=getch()while(isdigit(s+i=c=getch();/*collect fraction part*/;/*collect fraction part*/si=0;si=0;if(c!=EOF)if(c!=EOF)ungetch(c);ungetch(c);return NUMBER;return NUMBER;valsp+=f;valsp+=f;else else printf(“error:stack full,cant push%gn”,f);printf(“error:stack full,cant push%gn”,f);/*pop:pop and return top value from stack*/*pop:pop and return top value from stack*/double pop(void)double pop(void)if(sp 0)if(sp 0)return val-sp;return val-sp;else else printf(“error:stack emptyn”):printf(“error:stack emptyn”):return 0.0;return 0.0;子程序3 /*/*缓冲式的字符输入缓冲式的字符输入*/*/#define BUFSIZE 100#define BUFSIZE 100char bufBUFSIZE;/*buffer for ungetch*/char bufBUFSIZE;/*buffer for ungetch*/int bufp=0;/*next buffer position*/int bufp=0;/*next buffer position*/*get a character(possibly pushed back)*/*get a character(possibly pushed back)*/int getch(void)int getch(void)return(bufp 0)?buf-bufp:getchar();return(bufp 0)?buf-bufp:getchar();/*ungetch:push character back on input*/*ungetch:push character back on input*/void ungetch(int c)void ungetch(int c)if(bufp=BUFSIZE)if(bufp=BUFSIZE)printf(“ungetch:too many charactersn”);printf(“ungetch:too many charactersn”);else else bufbufp+=c;bufbufp+=c;4.4 变量辖域 某变量的辖域是指可以通过该变量的名字对之存取的程序部分。某变量的辖域是指可以通过该变量的名字对之存取的程序部分。函数的形式参数和在函数前部定义或说明的局部变量的定义域是整函数的形式参数和在函数前部定义或说明的局部变量的定义域是整个函数。不同函数中同名的变量间没有任何关系。个函数。不同函数中同名的变量间没有任何关系。在程序开头定义的全局变量的辖域是所在文件的这个变量定义以后在程序开头定义的全局变量的辖域是所在文件的这个变量定义以后的其余部分。的其余部分。但是一个程序可以分放在几个文件中,预先编译好的程序也可以从但是一个程序可以分放在几个文件中,预先编译好的程序也可以从程序库中导入,连接成更大的程序。由于存在一个文件使用另一个程序库中导入,连接成更大的程序。由于存在一个文件使用另一个文件中定义的全程变量,此时存在如下问题:文件中定义的全程变量,此时存在如下问题:如何写变量的说明使得能正确编译?如何写变量的说明使得能正确编译?如何安排变量说明,使得程序装入内存时能正确地连接?如何安排变量说明,使得程序装入内存时能正确地连接?如何保证是外部变量只有一份拷贝?如何保证是外部变量只有一份拷贝?如何对全局变量初始化?如何对全局变量初始化?下面回答这个问题:下面回答这个问题:变量辖域(续)外部变量的辖域是从定义或说明它们的地方起直到文件的结束外部变量的辖域是从定义或说明它们的地方起直到文件的结束:main().main().int sp=0;int sp=0;double valMAXVAL;double valMAXVAL;void push(double f).void push(double f).double pop(viod).double pop(viod).如果要在定义函数时需要访问尚未定义的外部变量或需要访问另一如果要在定义函数时需要访问尚未定义的外部变量或需要访问另一文件中定义的外部变量,则在使用它的文件(或函数)的开头必须文件中定义的外部变量,则在使用它的文件(或函数)的开头必须加外部变量说明。说明以后它在文件(或函数)的其余部分是可见加外部变量说明。说明以后它在文件(或函数)的其余部分是可见的了。的了。要注意外部变量的定义和说明之间的区别,说明只指出变量的类型,要注意外部变量的定义和说明之间的区别,说明只指出变量的类型,定义还涉及内存的分配。在整个程序中一个外部变量只能定义一次,定义还涉及内存的分配。在整个程序中一个外部变量只能定义一次,其它文件用其它文件用externextern说明加以关联。说明加以关联。外部变量只能在定义的地方初始化一次,不能在外部变量只能在定义的地方初始化一次,不能在externextern说明时进行说明时进行初始化。初始化。正常的情况是:main无法存取它后面定义的变量和函数。如果main要存取sp,val,push,和pop,则必须在这里或main的开头加:extern int sp;extern double val;void push(double);double pop(void):4.5 头文件 当程序分成好几个文件时,为了共享外部变量和函数,一般的当程序分成好几个文件时,为了共享外部变量和函数,一般的做法是把说明集中写在一个头文件中,然后在必要的文件中加做法是把说明集中写在一个头文件中,然后在必要的文件中加以包含:以包含:/*calc.h*/#define NUMBER 0void push(double);double pop(void);int getop(char);int getch(void);void ungetch(int);/*getop.c*/#include#include#include“calc.h”getop()./*getch.c*/#include#define BUFSIZE 100char bufBUFSIZE;int bufp=0;int getch(void).void ungetch(int c)./*main.c*/#include#include#include“calc.h”#define MAXOP 100main()./*stack.c*/#include#include“calc.h”#define MAXVAL 100int sp=0;double valMAXVAL;void push(double f).double pop().针对性问题:只有在特别大的程序中才考虑使用多个头文件4.6 静态变量 为了使一个外部变量的辖域仅限于一个文件,不让其它文件存取这为了使一个外部变量的辖域仅限于一个文件,不让其它文件存取这个变量,或者让其它文件可以使用同样的外部变量名而不与这个变个变量,或者让其它文件可以使用同样的外部变量名而不与这个变量相混淆,可以在该外部变量之前加量相混淆,可以在该外部变量之前加staticstatic前缀:前缀:函数也可以说明成为函数也可以说明成为static,static,说明成说明成staticstatic的函数只在本文件可见。的函数只在本文件可见。局部变量也可以说明成局部变量也可以说明成static,static,静态局部变量仍然是局部的,但它有静态局部变量仍然是局部的,但它有永久的、私有的内存空间,因此函数调用的结果可以保留下来,直永久的、私有的内存空间,因此函数调用的结果可以保留下来,直到此函数以后的调用中将之改变。这一特性十分重要!到此函数以后的调用中将之改变。这一特性十分重要!/*getch.c*/#include#define BUFSIZE 100static char bufBUFSIZE;static int bufp=0;int getch(void).void ungetch(int c)./*stack.c*/#include#include“calc.h”#define MAXVAL 100static int sp=0;static double valMAXVAL;void push(double f).double pop().4.7 寄存器变量 对于常用的变量(特别是循环变量)可以说明成寄存器变量,要求对于常用的变量(特别是循环变量)可以说明成寄存器变量,要求编译器将其安排在机器的寄存器中,其格式是在说明之间加编译器将其安排在机器的寄存器中,其格式是在说明之间加registerregister前缀:前缀:register int x;register int x;register char c;register char c;寄存器变量存取的速度快,但只能把局部变量和函数的形式参数说寄存器变量存取的速度快,但只能把局部变量和函数的形式参数说明成寄存器变量明成寄存器变量,而且也只有一定类型(整型)的变量能被指定成寄而且也只有一定类型(整型)的变量能被指定成寄存器变量:存器变量:fun(register unsigned n,register long n)fun(register unsigned n,register long n)register int i;.register int i;.根据机器的不同,可同时指定为寄存器变量的数目不同,编译器可根据机器的不同,可同时指定为寄存器变量的数目不同,编译器可对过多的寄存器变量说明不予理会。一般说来,只把使用频繁的循对过多的寄存器变量说明不予理会。一般说来,只把使用频繁的循环变量指定为寄存器变量。环变量指定为寄存器变量。寄存器变量没有地址,不管它实际存放在寄存器上与否。寄存器变量没有地址,不管它实际存放在寄存器上与否。4.8 块结构 C C语言不是严格的块结构语言,不能在函数内定义函数,但可以在函语言不是严格的块结构语言,不能在函数内定义函数,但可以在函数内甚至程序块内定义局部变量:数内甚至程序块内定义局部变量:if(n 0)if(n 0)int i;int i;for(i=0;i n;i+)for(i=0;i n;i+)./*/*这变量只在程序执行这个块时建立,块结束时消亡这变量只在程序执行这个块时建立,块结束时消亡*/*/局部变量对块外的同名变量和函数会起屏蔽作用,如:局部变量对块外的同名变量和函数会起屏蔽作用,如:int x;int x;int y;int y;fun(double x)fun(double x)double y;double y;./*/*在在函数内,这两个变量与外部的变量无关,这也是一个十分函数内,这两个变量与外部的变量无关,这也是一个十分重要的特点,编程时最好避免这种情况重要的特点,编程时最好避免这种情况*/*/这个 i 是局部于这个程序块 4.9 变量初始化 在没有明确指定初始化公式情况下:全程变量和静态变量初始化为在没有明确指定初始化公式情况下:全程变量和静态变量初始化为0 0。自动变量(局部和参数)和寄存器变量的初值是不确定的。自动变量(局部和参数)和寄存器变量的初值是不确定的。数值性的变量可以初始化:数值性的变量可以初始化:char squote=;char squote=;long day=1000L*60L*60L*24L;long day=1000L*60L*60L*24L;全局变量和静态变量的初始化公式只能是常数表达式,并且只初始全局变量和静态变量的初始化公式只能是常数表达式,并且只初始化一次(程序执行前);局部的非静态变量初始化公式可以是任意化一次(程序执行前);局部的非静态变量初始化公式可以是任意的,而且在每一次进入函数时进行一次初始化。的,而且在每一次进入函数时进行一次初始化。int bisearch(int x,int v,int n)int bisearch(int x,int v,int n)int low=0,high=n-1,mid;.int low=0,high=n-1,mid;.数组的初始化:数组的初始化:int days =31,28,31,30,31,30,31,31,30,31,30,31;int days =31,28,31,30,31,30,31,31,30,31,30,31;对于对于任何类型的变量,在指明元素个数时,初始化不足的元素为任何类型的变量,在指明元素个数时,初始化不足的元素为0 0;初始化过多时出错;没有办法表示元素值重复;无法省略前导元素初始化过多时出错;没有办法表示元素值重复;无法省略前导元素 字符数组的初始化可以使用如下特殊的形式:字符数组的初始化可以使用如下特殊的形式:char s =“hello”;/*char s =“hello”;/*它是下式的简写它是下式的简写*/*/char s =h,e,l,l,o,0;char s =h,e,l,l,o,0;4.10 递归 C C语言允许递归调用,即一个函数直接或间接地调用它自身。语言允许递归调用,即一个函数直接或间接地调用它自身。有许多需要递归调用的场合,例一,打印一个整数,由于最先有许多需要递归调用的场合,例一,打印一个整数,由于最先得到的是最低位,但最先要打印的是最高位,递归可以解决此得到的是最低位,但最先要打印的是最高位,递归可以解决此类问题:类问题:#include#include/*printd:print n in decimal*/*printd:print n in decimal*/void printd(int n)void printd(int n)if(n 0)if(n=right)if(left=right)return;return;swap(v,left,(left+right)/2);/*move partition element to elem*/swap(v,left,(left+right)/2);/*move partition element to elem*/last=left;last=left;for(i=left+1;i=right;i+)for(i=left+1;i=right;i+)if(vi vleft)if(vi vleft)swap(v,+last,i);swap(v,+last,i);swap(v,left,last);/*restore partition elem*/swap(v,left,last);/*restore partition elem*/qsort(v,left,last-1);qsort(v,left,last-1);qsort(v,last+1,right);qsort(v,last+1,right);/*swap:interchange vi,and vj*/*swap:interchange vi,and vj*/void swap(int v,int i,int j)void swap(int v,int i,int j)int temp;int temp;temp=vi;temp=vi;vi=vj;vi=vj;vj=temp;vj=temp;许多问题既可以用递归方法,也可以用非递归方法:在许多情况下,用递归写的程序会更紧凑、更容易理解,特别对于处理递归设计的数据特别方便,但是递归在内存和时间上都不会节省。4.11 C预处理器C C在文件编译之前对被编译的文件提供预了许多预处理在文件编译之前对被编译的文件提供预了许多预处理手段,其中包括:文件包含;宏定义;条件编译。手段,其中包括:文件包含;宏定义;条件编译。1.1.文件包含:文件包含:一般格式:一般格式:#include /*include /*在缺省的文件目录寻找在缺省的文件目录寻找filename*/filename*/#include “filename”/*#include “filename”/*在原程序所在的目录寻找,若找不到在原程序所在的目录寻找,若找不到 在缺省的文件目录寻找在缺省的文件目录寻找filename*/filename*/#includeinclude将由将由filenamefilename的文件内容所取代。的文件内容所取代。应用文件包含的目的是把个文件共同需要的宏定义,外部变量说明,库函数的原型说明等(stdio.h)插入到文件的开头。这样可更好地保证每一个文件需要的常数定义和子函数的定义都是一样的。C预处理器(续)2 2#define(#define(宏代换宏代换)一般格式:一般格式:#define#define#define forever for(;);/*#define forever for(;);/*在编译前将文本中的前者(不在编译前将文本中的前者(不 包括在字符串中的)用后者替换包括在字符串中的)用后者替换*/*/可带变量可带变量#define max(A,B)(A)(B)?(A):(B)#define max(A,B)(A)(B)?(A):(B)x=max(p+q,r+s)x=max(p+q,r+s)替换成替换成x=(p+q)(r+s)?(p+q):(r+s)x=(p+q)(r+s)?(p+q):(r+s)尽管宏定义是十分有用的,但要注意如下的调用或定义的问题尽管宏定义是十分有用的,但要注意如下的调用或定义的问题-特别要注意特别要注意重复执行问题重复执行问题和和运算的优先级问题运算的优先级问题:max(i+,j+);max(i+,j+);#define square(x)x*x#define square(x)x*x#把实参变成字符串把实参变成字符串#define dprint(expr)printf(#expr“=%gn”,expr)#define dprint(expr)printf(#expr“=%gn”,expr)dprint(x/y);-printf(“x/y”“=%gn”,x/y);dprint(x/y);-printf(“x/y”“=%gn”,x/y);#把实参拼接起来把实参拼接起来#define paste(front,back)front#back#define paste(front,back)front#backpaste(name,1)-name1paste(name,1)-name1宏的作用域在它的定义点到文件结束,如果要使其失效,则要用:#undef C预处理器(续)3条件编译条件编译可根据编译时的情况,对程序进行条件编译:可根据编译时的情况,对程序进行条件编译:#if#if 1.#elif elif 2.#elseelse.#endif#endifdefined(defined()=1)=1,如果名字已定义,否则为,如果名字已定义,否则为0 0。例如,下面。例如,下面的措施是为了