2020/1/171本章重点难点分析[目的与要求]掌握宏定义与文件包含[重点]带参数的宏定义[难点]带参数的宏定义与函数的区别使用第九章.编译预处理2020/1/172一、概念:c提供编译预处理功能:c对源程序进行编译(语法分析、代码生成和优化等)之前需要完成的工作。即完成对预处理的命令处理,将预处理的结果再和源程序一起进行编译,最后得到可执行的目标代码。编译预处理的命令有三种:宏定义文件包含条件编译均以#号开始,末尾无“;”号(这是c的特殊命令不是语句)C语言源程序C语言目标程序可执行的目标程序运行结果运行编译与库函数连接2020/1/173二、宏定义1、不带参数宏定义格式:#define标识符字符串含义:定义时将一个字符串定义成一个标识符;编译预处理时,将程序中所有宏名用该符串代替,也称宏展开(宏替换)。2020/1/174#includestdio.h#definePI3.1415926voidmain(){floatl,s,r,v;printf(inputradius:);scanf(%f,&r);l=2.0*PI*r;s=PI*r*r;v=4.0/3*PI*r*r*r;printf(l=%10.4f\ns=%10.4f\nv=%10.4f\n,l,s,v);}2020/1/175注意:宏名常用大写,以示与变量区别。宏展开时不做语法检查,只是简单的替换。宏不是语句,末尾不要加分号。宏名有效范围:从宏定义开始到本源文件结束,但也可用#undef终止。可以引用已定义的宏名,层层置换。双引号中的内容即使与宏名相同也不替换。#definePI3.14159printf(“PI=%5.2f\n”,PI);//PI=3.142020/1/1762、带参数宏定义格式:#define宏名(参数表)字符串含义:基本同不带参宏定义,区别在编译预处理时,将程序里出现宏名处用字符串代替,而字符串里的参数应用宏名后括号里的实参替换。2020/1/177例:#definePI3.14159#defineS(r)PI*r*rmain(){floatarea,a;scanf(“%f”,&a);area=S(a);/*area=PI*a*a*//*area=3.14159*a*a*/printf(“area=%f\n”,area);}area=S(a+2);/*area=PI*a+2*a+2不符原意*/可改为area=S((a+2));/*area=PI*(a+2)*(a+2)相符*/或改宏定义:#defineS(r)PI*(r)*(r):area=S(a+2);/*宏展开:area=PI*(a+2)*(a+2)*/2020/1/178例#includestdio.h#defineFU(k)k+3.14#definePR(a)printf(“a=%d\t”,(int)(a))#definePRINT(a)PR(a);putchar(‘\n’)main(){intx=2;PRINT(x*FU(2));}a预处理后:printf(“a=%d\t”,(int)(x*2+3.14));putchar(‘\n’);运行结果:a=7常用于:.*定义符号常量,简化编程,修改程序方便,见名知意。*定义复杂的打印格式,简化编程。2020/1/179三、文件包含:文件包含处理是一个源程序文件可以将另一个源程序文件全部包含。file1.cfile2.cfile1.c图一图二图三图一用“#include“file2.c””将file2.c中的内容代替。最后file1.c文件如图三所示。#include“file2.c”先在file1.c所在目录查找,若无,再按系统标准方式查找file2.c#includefile2.c按系统标准方式查找file2.c标准方式::在系统存放库函数的目录中查找要包含的文件。#include“file2.c”ABBA2020/1/1710*所有c提供的库函数在调用时,都将库函数的相应头文件用#include包含,才能对相应的库函数进行调用;*多人编写的大系统,为共享所定义的符号常量,类型说明,外部说明等,均可组织成头文件;*头文件可带路经,如:#include\\user\\prog.h表示头文件路径是\user\prog.h*也可将多人编写程序,合并成一个文件。2020/1/1711四、条件编译一般情况下,所有的源程序都要编译,但是有时,只希望程序中的一部分内容在满足一定条件时才编译,也就是对这部分内容指定编译的条件,称为“条件编译”。2020/1/1712A:#ifdef标识符程序段1#Else程序段2#endifB:#if表达式程序段1#else程序段2#endifC:#ifndef标识符程序段1#Else程序段2#endif条件编译的格式:2020/1/1713#includestdio.h#defineLETTERvoidmain(){charstr[20]=CLanguage,c;inti;i=0;while((c=str[i])!='\0'){i++;#ifLETTERif(c='a'&&c='z')c=c-32;#elseif(c='A'&&c='Z')c=c+32;#endifprintf(%c,c);}}2020/1/1714作业:9.1、9.3、9.42020/1/1715[目的与要求]掌握指针的概念.数组(字符串,函数)的指针指向数组(字符串,函数)的指针变量返回指针值的函数,指针数组与指针的指针[重点]数组(字符串,函数)的指针指向数组(字符串,函数)的指针变量[难点]数组(字符串,函数)的指针指向数组(字符串,函数)的指针变量返回指针值的函数,指针数组与指针的指针第十章.指针2020/1/1716一、概念变量的值变量的地址2020/1/1717通过变量名访问:给变量赋值;直接访问变量将变量值输出;通过指向变量在内存地址来访问:间接访问变量,即由变量存放的地址来得到变量的值。main(){inta,b;a=10;b=20;printf(“%d,%d\n”,a,b);main(){inta=1;b=2;int*pa,*pb;pa=&a;pb=&b;*pa=100;*pb=200;printf(“%d,%d\n”,*pa,*pb);}2020/1/17182000i_pointer3i3i33指针:变量的地址指针变量:专门用来存放另一个变量的地址的变量。200020002020/1/1719二、指针变量指针——就是变量在内存的首地址。指针变量——存放另一个变量首地址的变量。1.指针变量定义:基类型*指针变量名;int*pa,*pb;/*定义pa,pb分别指向整型类型*/float*pf1,*pf2;/*定义pf1,pf2分别指向实型变量*/char*pc1,*pc2;/*定义pc1,pc2分别指向字符型变量*/::pa和pb只可指向整型变量在内存首地址,不可指向其他类型变量;pf1,pf2只可指向实型;pc1,pc2只可指向字符型;pa++——pa是指向整型指针变量,增1表示增加2bytes.pf1++——pf是指向实型指针变量,增1表示增加4bytes.pc1++——pc是指向字符型指针变量,增1表示增加1byte.2020/1/17202.指针变量引用&:&a指a变量的地址;*:*p:指针变量p所指向的内存单元的内容。#includestdio.hvoidmain(){inta,b;paapaint*pa,*pb;&a10100010aa=10;b=20;pbbpbpa=&a;pb=&b;&b20100220bprintf(“%d,%d\n”,a,b);printf(“%d,%d\n”,*pa,*pb);}&、*、++、——均是单目运算:结合性左右*&a等价a或*pa&*pa等价pa或&a(*pa)++等价a++*(++pa)等价先pa+1,再*pa2020/1/1721例1:输入a,b两个整数,按从大到小顺序输出#includestdio.hvoidmain(){inta,b,*pa,*pb,*p;paascanf(“%d,%d”,&a,&b);pa=&a;pb=&b;if(ab){p=pa;pa=pb;pb=p;}pbbprintf(“a=%d,b=%d\n”,a,b);printf(“max=%d,min=%d\n”,*pa,*pb);}paa在程序中若ab,则将两个指针变量的值交换,原paa,现在pab(大数)pbb&a330&b&b330&a2020/1/1722voidswap(int*pa,int*pb){inttemp;/*变量分配空间,有确定的地址*/temp=*pa;*pa=*pb;*pb=temp;}#includestdio.hvoidmain(){inta,b;int*pa1,*pb1;scanf(“%d,%d\n”,&a,&b);pa1=&a;pb1=&b;if(ab)swap(pa1,pb1);printf(“%d,%d\n”,a,b);}&a&a&a&b&b&b&b&a33030330&b30&a参数传递函数中实现abpa1apb1bpa1paabpb1pbpa1papb1pbab3.指针变量作函数参数(被调函数改变变量的值,能在主调函数中使用其值:例2用函数实现两数交换2020/1/1723被调函数中改变了的变量的值能够被主调函数应用,被调函数的形参定义为指针变量。通过函数调用得到n个要改变的值,可以:在主调函数中:设n个变量,用n个指针变量指向它们;将指针变量做实参,将此n个指针变量的值传递给形参;在被调函数中改变n个变量的值;主调函数中可以使用这些改变了的值了。2020/1/1724例3:在函数中交换指针值,是不行的#includestdio.hvoidswap(int*pa,int*pb)voidmain(){inta,b;{int*p;/*交换指针的值int*pa,*pb;p=pa;返回时,pa,pbscanf(“%d,%d”,&a,&b);pa=pb;均释放,达不pa1=&a;pb1=&b;pb=p;到交换a,b的目printf(“%d,%d\n”,a,b);}的*/if(ab)swap(pa1,pa2);printf(“%d,%d\n”,a,b);}voidswap(int*pa,int*pb){int*p;/*指针变量p未赋值,是随机值,可能会使系统不*p=*p1;正常*/*p1=*p2;*p2=*p;}2020/1/1725三、数组与指针1、概念:数组名就是指针,是数组元素在内存存放的首地址即第一个元素的地址。一维数组:inta[10]二维数组:intb[3][4]a[0]a[2]a[9]::100010021004a[1]1010b[0]+1b[0]+2b[0]+3*(b+0)/b[0]*(b+1)/b[1]*(b+2)/b[2]ba+1:指向数组中下一个元素*a+1:a所指向的单元中的内容+12020/1/1726第i个元素:a[i]第i行第j列元素:b[i][j]第i个元素地址:&a[i]第i行第j列元素地址:&b[i][j]main(){inta[10],i;int*p;p=a;for(i=0;i10;i++){scanf(“%u”,p+i);/*每个元素首地址,与printf(“%u”,&a[i]);等价*/printf(“%d“,*(p+i));/*每个元素值,与}printf(“%d”,a[i]);等价*/指针:main(){staticintb[3][4]={1,2,3,……12};int*p,i;p=b[0];/*或p=*(b+0)*/for(;pb[0]+12;p++){printf(“%u”,p);printf(“%d“,*p);}}指针:2020/1/1727例4main(){staticinta[2][5]={{1,2,3,4,5},{6,7,8,9,10}};inti,(*