第8章 编译预处理及重新定义数据类型.docx

上传人:b****5 文档编号:8239197 上传时间:2023-01-30 格式:DOCX 页数:9 大小:19.43KB
下载 相关 举报
第8章 编译预处理及重新定义数据类型.docx_第1页
第1页 / 共9页
第8章 编译预处理及重新定义数据类型.docx_第2页
第2页 / 共9页
第8章 编译预处理及重新定义数据类型.docx_第3页
第3页 / 共9页
第8章 编译预处理及重新定义数据类型.docx_第4页
第4页 / 共9页
第8章 编译预处理及重新定义数据类型.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

第8章 编译预处理及重新定义数据类型.docx

《第8章 编译预处理及重新定义数据类型.docx》由会员分享,可在线阅读,更多相关《第8章 编译预处理及重新定义数据类型.docx(9页珍藏版)》请在冰豆网上搜索。

第8章 编译预处理及重新定义数据类型.docx

第8章编译预处理及重新定义数据类型

第8章编译预处理及重新定义数据类型

所谓编译预处理,是编译器在对C语言源程序进行正常编译之前,先对一些特殊的预处理命令作解释,产生一个新的源程序。

编译预处理主要为程序调试、移植等提供便利,是一个非常实用的功能。

8.1宏定义

在源程序中,为了区分预处理命令和一般的C语句的不同,所有预处理命令行都以符号“#”开头,并且结尾不用分号。

预处理命令可以出现在程序任何位置,但习惯上尽可能地写在源程序的开头,其作用范围从其出现的位置到文件尾。

C语言提供的预处理命令主要有:

宏定义、文件包含和条件编译。

其中宏定义分为带参数的宏定义和不带参数的宏定义。

8.1.1不带参数的宏定义

不带参数的宏定义的一般形式为:

#define标识符字符串

它的作用是在编译预处理时,将源程序中所有标识符替换成字符串。

例如:

#definePI3.14

#defineuintunsignedint

当需要修改元素时,只要直接修改宏定义即可,无需修改程序中所有出现元素个数的地方。

所以宏定义,不仅提高了程序的可读性、便于调试,而且也方便了程序的移植。

无参数的宏定义使用时,要注意以下几个问题:

1.宏名一般用大写字母,以便于与变量名的区别。

当然,用小写字母也不为错。

2.在编译预处理中宏名与字符串进行替换时,不作语法检查,只是简单的字符替换,只有在编译时才对已经展开宏名的源程序进行语法检查。

3.宏名的有效范围是从定义位置到文件结束。

如果需要终止宏定义的作用域,可以用#undef命令。

例如:

#undefPI

则该语句之后的PI不再代表3.14,这样可以灵活控制宏定义的范围。

4.宏定义时可以引用己经定义的宏名。

例如:

#defineR2.0

#definePI3.14

#defineALLPI*R

5.对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。

8.1.2带参数的宏定义

为了进一步扩大宏的应用范围,在定义宏时,还可以带参数。

带参数的宏定义的一般形式为:

#define标识符(参数表)字符串

它的作用是在编译预处理时,将源程序中所有标识符替换成字符串,并且将字符串中的参数用实际使用的参数替换。

例如:

#defineS(a,b)(a+b)/2

则源程序中如果使用了S(3,4),在编译预处理时将替换为(3+4)/2。

8.2在51MCUDEMO试验板上实现两数相加并输出结果,变量的数据类型用宏定义的缩写形式。

8.2.1实现方法

将无符号字符型数据类型“unsignedchar”宏定义为“uchar”,将无符号整型数据类型“unsignedint”宏定义为“uint”,便于程序中使用。

在主函数中定义3个“uchar”型的变量a、b、sum,a和b分别赋给初值,然后求和并赋予sum。

最后将sum的值输出到数码管上显示。

8.2.2源程序文件

在D盘建立一个文件目录(CS8-1),然后建立CS8-1.uv2的工程项目,最后建立源程序文件(CS8-1.c)。

输入下面的程序:

#include//1

#defineucharunsignedchar//2

#defineuintunsignedint//3

ucharcodeSEG7[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//4

//=====================5===========

voiddelay(uintk)//6

{//7

uinti,j;//8

for(i=0;i

for(j=0;j<121;j++)//10

{;}}//11

}//12

//=====================13=======

voidmain(void)//14

{//15

uchara,b,sum;//16

a=55;//17

b=200;//18

sum=a+b;//19

while

(1)//20

{//21

P0=SEG7[sum/100];//22

P2=0xfb;//23

delay

(1);//24

P0=SEG7[(sum%100)/10];//25

P2=0xfd;//26

delay

(1);//27

P0=SEG7[sum%10];//28

P2=0xfe;//29

delay

(1);//30

}//31

}//32

编译通过后,51MCUDEMO试验板接通5V稳压电源,将生成的CS8-1.hex文件下载到试验板上的单片机89S51中,注意,标示“LEDMOD_DATA”及“LEDMOD_COM”的双排针应插上短路块。

右边3个LED数码管显示“255”。

通过宏定义,我们发现原来长长的“unsignedchar”、“unsignedint”现变成了“uchar”、“uint”,是不是更方便使用了。

8.2.3程序分析解释

序号1:

包含头文件REG51.H。

序号2:

数据类型“unsignedchar”用宏定义为简写形式“uchar”。

序号3:

数据类型“unsignedint”用宏定义为简写形式“uint”。

序号4:

数码管0~9的字形码。

序号5:

程序分隔。

序号6~12:

延时子函数。

序号13:

程序分隔。

序号14:

定义函数名为main的主函数。

序号15:

main的主函数开始。

序号16:

定义无符号字符型变量a、b、sum。

序号17:

a赋值55。

序号18:

b赋值200。

序号19:

a、b作加法运算,其和放sum。

序号20:

while循环语句,这里进行无限循环。

序号21:

while循环语句开始。

序号22:

取出sum的百位数送P0口显示。

序号23:

点亮百位数码管。

序号24:

延时1mS以便观察清楚。

序号25:

取出sum的十位数送P0口显示。

序号26:

点亮十位数码管。

序号27:

延时1mS以便观察清楚。

序号28:

取出sum的个位数送P0口显示。

序号29:

点亮个位数码管。

序号30:

延时1mS以便观察清楚。

序号31:

while循环语句结束。

序号32:

main的主函数结束。

 

8.3使用带参数的宏定义进行运算,结果送51MCUDEMO试验板显示。

8.3.1实现方法

将无符号字符型数据类型“unsignedchar”宏定义为“uchar”,将无符号整型数据类型“unsignedint”宏定义为“uint”,便于程序中使用。

另外,将(a-b)*3宏定义为S(a,b),即a、b作为参数使用。

我们使用带参数的宏进行数学计算,并将计算结果输出到数码管上显示。

8.3.2源程序文件

在D盘建立一个文件目录(CS8-2),然后建立CS8-2.uv2的工程项目,最后建立源程序文件(CS8-2.c)。

输入下面的程序:

#include//1

#defineucharunsignedchar//2

#defineuintunsignedint&nbs,p;//3

#defineS(a,b)(a-b)*3//4

ucharcodeSEG7[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//5

//=====================6===========

voiddelay(uintk)//7

{//8

uinti,j;//9

for(i=0;i

for(j=0;j<121;j++)//11

{;}}//12

}//13

//=====================14=========

voidmain(void)//15

{//16

ucharout;//17

out=S(100,50);//18

while

(1)//19

{//20

P0=SEG7[out/100];//21

P2=0xfb;//22

delay

(1);//23

P0=SEG7[(out%100)/10];//24

P2=0xfd;//25

delay

(1);//26

P0=SEG7[out%10];//27

P2=0xfe;//28

delay

(1);//29

}//30

}//31

编译通过后,51MCUDEMO试验板接通5V稳压电源,将生成的CS8-2.hex文件下载到试验板上的单片机89S51中,注意,标示“LEDMOD_DATA”及“LEDMOD_COM”的双排针应插上短路块。

右边3个LED数码管显示“150”。

8.3.3程序分析解释

序号1:

包含头文件REG51.H。

序号2~3:

数据类型的宏定义。

序号4:

带参数的宏定义

序号5:

数码管0~9的字形码。

序号6:

程序分隔。

序号7~13:

延时子函数。

序号14:

程序分隔。

序号15:

定义函数名为main的主函数。

序号16:

main的主函数开始。

序号17:

定义无符号字符型变量out。

序号18:

使用参数宏进行计算,其结果送out。

序号19:

while循环语句,这里进行无限循环。

序号20:

while循环语句开始。

序号21:

取出out的百位数送P0口显示。

序号22:

点亮百位数码管。

序号23:

延时1mS以便观察清楚。

序号24:

取出out的十位数送P0口显示。

序号25:

点亮十位数码管。

序号26:

延时1mS以便观察清楚。

序号27:

取出sum的个位数送P0口显示。

序号28:

点亮个位数码管。

序号29:

延时1mS以便观察清楚。

序号30:

while循环语句结束。

序号31:

main的主函数结束。

8.4文件包含

“文件包含”实际上就是我们前面已经多次用到的#include命令实现的功能,即一个源程序文件可以包含另外一个源程序文件的全部内容。

“文件包含”不仅可以包含头文件,例如:

#include,还可以包含用户自己编写的源程序文件,例如:

#include

8.4.1文件包含预处理命令的一般形式

文件包含预处理命令的一般形式为:

#include<文件名>或#include“文件名”

上述两种方式的区别是:

前一种形式的文件名用尖括弧括起来,系统将到包含C语言库函数的头文件所在的目录(通常是KEIL目录中的include子目录)中寻找文件。

后一种形式的文件名用双引号括起来,系统先在当前目录下寻找,若找不到,再到其它路径中查找。

8.4.2文件包含使用注意

1.一个#include命令只能指定一个被包含的文件。

2.“文件包含”可以嵌套。

在文件包含的嵌套时,如果文件1包含了文件2,而文件2包含了文件3,则在文件1也要包含文件3,并且文件3的包含要写在文件2的包含之前,即文件l中的“文件包含”说明如下:

#include<文件名1>

#include<文件名2>

“文件包含”命令为多个源程序文件的组装提供了一种方法。

在编写程序时,习惯上将公共的符号常量定义、数据类型定义和extern类型的全局变量说明构成一个源文件,并以“.H”为文件名的后缀。

如果其他文件用到这些说明时,只要包含该文件即可,无需再重新说明,减少了工作量。

而且这样编程使得各源程序文件中的数据结构、符号常量以及全局变量形式统一,便于程序的修改和调试。

8.5条件编译

“条件编译”命令允许对程序中的内容选择性地编译,即可以根据一定的条件选择是否编译。

条件编译的命令主要有以下几种形式:

形式1.

#ifdef标识符

程序段l

#else

程序段2

#endif

它的作用是当“标识符”已经由#define定义过了,则编译“程序段1”,否则编译“程序段2”。

其中如果不需要编译“程序段2”,则上述形式可以变换为:

#ifdef标识符

程序段1

#endif

形式2.

#ifndef标识符

程序段1

#else

程序段2

#endif

它的作用是当“标识符”没有由#define定义过,则编译“程序段1”,否则编译“程序段2”。

同样当无“程序段2”时,则上述形式变换为:

#ifndef标识符

程序段1

#endif

形式3.

#if表达式

程序段1

#elsee

程序段2

#endif

它的作用是当“表达式”值为真时,编译程序段1,否则编译程序段2。

同样当无程序段2时,则上述形式变换为:

#if表达式

程序段1

#endif

以上三种形式的条件编译预处理结构都可以嵌套使用。

当#else后嵌套#if时,可以使用预处理命令#elif,它相当于#else#if。

在程序中使用条件编译主要是为了方便程序的调试和移植。

例如,我们在调试时需输出某个变量x的值进行分析,则可采用如下方法:

#defineDEBUG

……

#ifdefDEBUG

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

#endif

调试完毕,系统正常运行后,只需将#defineDEBUG行删除或注释掉即可。

8.6重新定义数据类型

在C语言程序中,用户可以根据自己的需要对数据类型重新定义。

使用关键字typedef的定义方法如下:

typedef已有的数据类型新的数据类型名;

其中“已有的数据类型”是指上面所介绍的C语言中所有的数据类型,包括结构、指针和数组等,“新的数据类型名”可按用户自己的习惯或根据任务需要决定。

关键字typedef的作用只是将C语言中已有的数据类型作了置换,因此可用置换后的新数据类型名来进行变量的定义。

例如:

typedefintWORD;//定义word为新的整型数据类型名

一般而言,对typedef定义的新数据类型用大写字母表示,以便与C语言中原有的数据类型相区别。

另外还要注意,用typedef可以定义各种新的数据类型名,但不能直接用来定义变量。

typedef只是对已有的数据类型作了一个名字上的置换,并没有创造出一个新的数据类型,例如前面例子中的WORD,它只是int类型的一个新名字而已。

采用typedef来重新定义数据类型有利于程序的移植,同时还可以简化较长的数据类型定义(如结构数据类型等)。

在采用多模块程序设计时,如果不同的模块程序源文件中用到同一类型的数据时(尤其是像数组、指针、结构、联合等复杂数据类型),经常用typedef将这些数据重新定义并放到一个单独的文件中,需要时再用预处理命令#include将它们包含进来。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1