《C语言程序设计》第8章 编译预处理和动态存储分配 写字字帖_第1页
《C语言程序设计》第8章 编译预处理和动态存储分配 写字字帖_第2页
《C语言程序设计》第8章 编译预处理和动态存储分配 写字字帖_第3页
《C语言程序设计》第8章 编译预处理和动态存储分配 写字字帖_第4页
《C语言程序设计》第8章 编译预处理和动态存储分配 写字字帖_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8章编译预处理和动态存储分配8.1编译预处理8.2宏定义8.3文件包含处理8.4动态存储分配8.1编译预处理C语言允许在源程序中加入一些“预处理命令”(preprocessingdirective),以改进程序设计环境,提高编程效率。这些预处理指令是由C标准建议的,但是它不是C语言本身的组成部分,不能用C编译系统直接对它们进行编译(因为编译程序不能识别它们)。所谓“编译预处理”就是在C编译程序对C源程序进行编译前,由编译预处理程序对这些编译预处理命令行进行处理的过程。在预处理阶段,预处理器把程序中的注释全部删除;对预处理指令进行处理,如把#include指令指定的头文件(如stdio.h)的内容复制到#include指令处;对#define指令,进行指定的字符替换(如将程序中的符号常量用指定的字符串代替),同时删去预处理指令。8.1编译预处理经过预处理后的程序不再包括预处理指令了,最后再由编译程序对预处理后的源程序进行实际的编译处理,得到可供执行的目标代码。C语言与其他高级语言的一个重要区别是可以使用预处理指令和具有预处理的功能。C语言提供的预处理功能常用的主要有以下3种:1、宏定义;2、文件包含;3、条件编译。这些预处理命令组成的预处理命令行必须在一行的开头以”#”号开始,每行的末尾不得用“;”号结束,以区别于C语句、定义和说明语句。这些命令行的语法与C语言中其他部分的语法无关。根据需要,命令行可以出现在程序的任何一行的开始部位,其作用一直持续到源文件的末尾。8.2宏定义8.2.1不带参数的宏定义8.2.2带参数的宏定义8.2.3终止宏定义8.2.1不带参数的宏定义1不带参数的宏定义命令行形式格式:#define宏名替换文本或者:#define宏名在define、宏名和宏替换文本之间用空格隔开。例如:#define SIZE 100以上标识符SIZE称为“宏名”,是用户定义的标识符,因此,不得与程序中的其他名字相同。在编译时,在此命令行之后,预处理程序对源程序中的所有名为SIZE的标识符用100三个字符来替换,这个替换过程称为“宏替换”。但要注意:不能认为“SIZE等于整数100”。#define命令行可以不包含“替换文本”,这种情况下仅说明标识符“被定义”。8.2.1不带参数的宏定义2替换文本中可以包含已定义过的宏名例8.1计算圆面积。#include<stdio.h>#definePI3.1415926#defineR3.0#defineSPI*R*R /*S的宏定义使用了前面的PI和R宏定义*/intmain(){

printf(“圆的面积=%f”,S); return0;}运行结果:圆的面积=28.274333分析:该例中既有宏定义,又有宏定义的多重替换,这样求圆的面积,只需将宏名S进行展开后计算,输出即可。8.2.1不带参数的宏定义3当宏定义在一行中写不下,需要在下一行继续时,只需在最后一个字符后紧接着加一个反斜线“\”。例如:#define LEAP_YEAR year%4==0\&&year%100!=0||year%400==0第一列如果在“\”前或在下一行的开头留有许多空格,则在宏替换时也将加入这些空格。4同一个宏名不能重复定义,除非两个宏定义命令行完全一致。5替换文本不能替换双引号中与宏名相同的字符串。例8.2宏名相同的字符串不能替换。#define BOOK “TheRedandTheBlack”intmain(){

printf(“%s\n”,”BOOK”); return0;}运行结果:BOOK8.2.1不带参数的宏定义6替换文本并不替换用户标识符中的成分。例如,宏名YES,不会替换标识符YESORNO中的YES。7用作宏名的标识符通常用大写字母表示,这并不是语法规定,只是一种习惯,以便与程序中的其他标识符相区别。8在C程序中,宏定义的定义位置一般写在程序的开头。返回8.2.2带参数的宏定义1带参数的宏定义命令行形式如下:格式:#define 宏名(形参表) 替换文本如果定义带参数的宏,在对源程序进行预处理时,将程序中出现宏名的地方均用替换文本替换,并用实参代替替换文本中的形参。例8.3编写程序,使用带参数的宏定义。#include<stdio.h>#defineMAX(a,b) a>b?a:b /*定义带参数的宏MAX*/#defineSQR(c)c*c /*定义带参数的宏SQR*/intmain(){

intx=3,y=4; x=MAX(x,y);

y=SQR(x);

printf(“x=%d,y=%d\n”,x,y);

return0;}运行结果:x=4,y=16对于带参的宏定义有以下问题需要说明:2.带参宏定义中,宏名和形参表之间不能有空格出现。例如把:

#defineMAX(a,b)(a>b)?a:b写为:

#defineMAX(a,b)(a>b)?a:b将被认为是无参宏定义,宏名MAX代表字符串(a,b)(a>b)?a:b。宏展开时,宏调用语句:

max=MAX(x,y);将变为:

max=(a,b)(a>b)?a:b(x,y);这显然是错误的。8.2.2带参数的宏定义3.在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。4.在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。例8.4宏调用实参为表达式。#defineSQ(y)(y)*(y)main(){

int

a,sq;

printf("inputanumber:");

scanf("%d",&a);sq=SQ(a+1);printf("sq=%d\n",sq);}运行结果:inputanumber:3sq=16分析:上例中第一行为宏定义,形参为y。程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y)代换SQ,得到如下语句:

sq=(a+1)*(a+1);这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参。而宏代换中对实参表达式不作计算直接地照原样代换。8.2.2带参数的宏定义5.在宏定义中,字符串内的形参通常要用括号括起来以避免出错。在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。如果去掉括号,把程序改为以下形式:例8.5#defineSQ(y)y*ymain(){

int

a,sq;

printf("inputanumber:");

scanf("%d",&a);sq=SQ(a+1);printf("sq=%d\n",sq);}运行结果:inputanumber:3sq=7分析:同样输入3,但结果却是不一样的。问题在哪里呢?这是由于代换只作符号代换而不作其它处理而造成的。宏代换后将得到以下语句:

sq=a+1*a+1;由于a为3故sq的值为7。这显然与题意相违,因此参数两边的括号是不能少的。即使在参数两边加括号还是不够的,请看下面程序:8.2.2带参数的宏定义例8.6#defineSQ(y)(y)*(y)main(){

int

a,sq;

printf("inputanumber:");

scanf("%d",&a);sq=160/SQ(a+1);printf("sq=%d\n",sq);}运行结果:inputanumber:3sq=160分析:本程序与前例相比,只把宏调用语句改为:

sq=160/SQ(a+1);运行本程序如输入值仍为3时,希望结果为10。为什么会得这样的结果呢?分析宏调用语句,在宏代换之后变为:

sq=160/(a+1)*(a+1);a为3时,由于“/”和“*”运算符优先级和结合性相同,则先作160/(3+1)得40,再作40*(3+1)最后得160。为了得到正确答案应在宏定义中的整个字符串外加括号,程序修改如例8.78.2.2带参数的宏定义6带参的宏和带参函数很相似,但有本质上的不同,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理两者的结果有可能是不同的。例8.8main(){

inti=1;

while(i<=5)

printf("%d\n",SQ(i++));}SQ(inty){return((y)*(y));}例8.9#defineSQ(y)((y)*(y))main(){

inti=1;

while(i<=5)

printf("%d\n",SQ(i++));}例8.8运行结果:1491625例8.9运行结果:19258.2.2带参数的宏定义7宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。看下面的例子。例8.10#defineSSSV(s1,s2,s3,v)s1=l*w;s2=l*h;s3=w*h;v=w*l*h;main(){

intl=3,w=4,h=5,sa,sb,sc,vv;

SSSV(sa,sb,sc,vv);

printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n",sa,sb,sc,vv);}运行结果:sa=12sb=15sc=20vv=60分析:程序第一行为宏定义,用宏名SSSV表示4个赋值语句,4个形参分别为4个赋值符左部的变量。在宏调用时,把4个语句展开并用实参代替形参。使计算结果送入实参之中。返回8.2.3终止宏定义可以用#undef提前终止宏定义的作用域。例如:#definePI3.14main()#undefPI以上PI的作用域从#definePI3.14命令行开始,到#undefPI命令行结束。从#undef以后PI变成无定义,不再代表3.14了。8.3文件包含处理所谓文件包含,是指在一个文件中,去包含另一个文件的全部内容。C语言用#include命令行来实现文件包含的功能。格式:#include “文件名”或 #include <文件名>在预编译时,预编译程序将用指定文件中的内容来替换此命令行。如果文件名用双引号括起来,系统先在源程序所在的目录内查找指定的包含文件,如果找不到,再按照系统指定的标准方式到有关目录中去寻找;如果文件名用尖括号括起来,系统将直接按照系统指定的标准方式到有关目录中寻找。说明:1包含文件的#include命令行通常应书写在所用源程序文件的开头,故有时也把包含文件称作“头文件“。头文件名可以由用户指定,其后缀不一定用”.h”。2包含文件中,一般包含有一些公用的#define命令行、外部说明或对(库)函数的原型说明。例如stdio.h就是这样的头文件。3当包含文件修改后,对包含该文件的源程序必须重新进行编译连接。4在一个程序中,允许有任意多个#include命令行。5在包含文件中还可以包含其他文件。8.4动态存储分配8.4.1malloc函数和free函数8.4.2calloc函数8.4.1malloc函数和free函数1malloc函数格式:(类型说明符*)malloc(size)功能:在内存的动态存储区中分配一块长度为"size"字节的连续区域。函数的返回值为该区域的首地址。“类型说明符”表示把该区域用于何种数据类型。(类型说明符*)表示把返回值强制转换为该类型指针。“size”是一个无符号数。假设shortint型数据占2个字节,float型数据占4字节存储单元,则以下程序段将使pi指向一个shortint类型的存储单元,使pf指向一个float类型的存储单元:shortint*pi;float*pf;pi=(short*)malloc(2);pf=(float*)malloc(4);由于在ANSIC中malloc函数返回的指针为void*(无值型),故在调用函数时,必须利用强制类型转换将其转成所需的类型。上面的程序段中,调用malloc函数时括号中的*号不可少,否则就转换成普通变量类型而不是指针类型了。在动态申请存储空间时,若不能确定数据类型所占字节数,可以使用sizeof运算符来求得。例如:

pi=(int*)malloc(sizeof(int)); pf=(float*)malloc(sizeof(float));这是一种常用的形式。此时将由系统来计算指定类型的字节数,采用这种形式将有利于程序的移植。8.4.1malloc函数和free函数2free函数格式:free(void*ptr);功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc函数所分配的区域。例8.11分配一块区域,输入一个学生数据。main(){

struct

stu{

intnum;char*name;charsex;floatscore;}*ps;

ps=(struct

stu*)malloc(sizeof(struct

stu));

ps->num=102;

ps->name="Zhangping";

ps->sex='M';

ps->score=62.5;

printf("Number=%d\nName=%s\n",ps->num,ps->name);

printf("Sex=%c\nScore=%f\n",ps->sex,ps->score);

free(ps);}运行结

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论