cpp宏使用.docx
- 文档编号:18404127
- 上传时间:2023-08-16
- 格式:DOCX
- 页数:20
- 大小:22.88KB
cpp宏使用.docx
《cpp宏使用.docx》由会员分享,可在线阅读,更多相关《cpp宏使用.docx(20页珍藏版)》请在冰点文库上搜索。
cpp宏使用
1无参宏定义
无参宏的宏名后不带参数。
其定义的一般形式为:
#define标识符字符串
其中的“#”表示这是一条预处理命令。
凡是以“#”开头的均为预处理命令。
“define”为宏定义命令。
“标识符”为所定义的宏名。
“字符串”可以是常数、表达式、格式串等。
在前面介绍过的符号常量的定义就是一种无参宏定义。
此外,常对程序中反复使用的表达式进行宏定义。
例如:
#defineM(y*y+3*y)
它的作用是指定标识符M来代替表达式(y*y+3*y)。
在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序作编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去置换所有的宏名M,然后再进行编译。
【例】
#defineM(y*y+3*y)
main(){
ints,y;
printf("inputanumber:
");
scanf("%d",&y);
s=3*M+4*M+5*M;
printf("s=%d\n",s);
}
上例程序中首先进行宏定义,定义M来替代表达式(y*y+3*y),在s=3*M+4*M+5*M中作了宏调用。
在预处理时经宏展开后该语句变为:
s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);
但要注意的是,在宏定义中表达式(y*y+3*y)两边的括号不能少。
否则会发生错误。
如当作以下定义后:
#difineMy*y+3*y
在宏展开时将得到下述语句:
s=3*y*y+3*y+4*y*y+3*y+5*y*y+3*y;
这相当于:
3y2+3y+4y2+3y+5y2+3y;
显然与原题意要求不符。
计算结果当然是错误的。
因此在作宏定义时必须十分注意。
应保证在宏代换之后不发生错误。
对于宏定义还要说明以下几点:
1)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。
如有错误,只能在编译已被宏展开后的源程序时发现。
2)宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。
3)宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。
如要终止其作用域可使用#undef命令。
例如:
#definePI3.14159
main()
{
……
}
#undefPI
f1()
{
……
}
表示PI只在main函数中有效,在f1中无效。
4)宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。
【例】
#defineOK100
main()
{
printf("OK");
printf("\n");
}
上例中定义宏名OK表示100,但在printf语句中OK被引号括起来,因此不作宏代换。
程序的运行结果为:
OK这表示把“OK”当字符串处理。
5)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。
在宏展开时由预处理程序层层代换。
例如:
#definePI3.1415926
#defineSPI*y*y/*PI是已定义的宏名*/
对语句:
printf("%f",S);
在宏代换后变为:
printf("%f",3.1415926*y*y);
6)习惯上宏名用大写字母表示,以便于与变量区别。
但也允许用小写字母。
7)可用宏定义表示数据类型,使书写方便。
例如:
#defineSTUstructstu
在程序中可用STU作变量说明:
STUbody[5],*p;
#defineINTEGERint
在程序中即可用INTEGER作整型变量说明:
INTEGERa,b;
应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。
宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。
被命名的标识符具有类型定义说明的功能。
请看下面的例子:
#definePIN1int*
typedef(int*)PIN2;
从形式上看这两者相似,但在实际使用中却不相同。
下面用PIN1,PIN2说明变量时就可以看出它们的区别:
PIN1a,b;在宏代换后变成:
int*a,b;
表示a是指向整型的指针变量,而b是整型变量。
然而:
PIN2a,b;
表示a,b都是指向整型的指针变量。
因为PIN2是一个类型说明符。
由这个例子可见,宏定义虽然也可表示数据类型,但毕竟是作字符代换。
在使用时要分外小心,以避出错。
8)对“输出格式”作宏定义,可以减少书写麻烦。
【例】中就采用了这种方法。
#definePprintf
#defineD"%d\n"
#defineF"%f\n"
main(){
inta=5,c=8,e=11;
floatb=3.8,d=9.7,f=21.08;
P(DF,a,b);
P(DF,c,d);
P(DF,e,f);
}
2带参宏定义
C++语言允许宏带有参数。
在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:
#define宏名(形参表)字符串
在字符串中含有各个形参。
带参宏调用的一般形式为:
宏名(实参表);
例如:
#defineM(y)y*y+3*y/*宏定义*/
……
k=M(5);/*宏调用*/
……
在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:
k=5*5+3*5
【例】
#defineMAX(a,b)(a>b)?
a:
b
main(){
intx,y,max;
printf("inputtwonumbers:
");
scanf("%d%d",&x,&y);
max=MAX(x,y);
printf("max=%d\n",max);
}
上例程序的第一行进行带参宏定义,用宏名MAX表示条件表达式(a>b)?
a:
b,形参a,b均出现在条件表达式中。
程序第七行max=MAX(x,y)为宏调用,实参x,y,将代换形参a,b。
宏展开后该语句为:
max=(x>y)?
x:
y;
用于计算x,y中的大数。
对于带参的宏定义有以下问题需要说明:
1.带参宏定义中,宏名和形参表之间不能有空格出现。
例如把:
#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);
这显然是错误的。
2.在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。
而宏调用中的实参有具体的值。
要用它们去代换形参,因此必须作类型说明。
这是与函数中的情况不同的。
在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。
而在带参宏中,只是符号代换,不存在值传递的问题。
3.在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
【例】
#defineSQ(y)(y)*(y)
main(){
inta,sq;
printf("inputanumber:
");
scanf("%d",&a);
sq=SQ(a+1);
printf("sq=%d\n",sq);
}
上例中第一行为宏定义,形参为y。
程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y)代换SQ,得到如下语句:
sq=(a+1)*(a+1);
这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参。
而宏代换中对实参表达式不作计算直接地照原样代换。
4.在宏定义中,字符串内的形参通常要用括号括起来以避免出错。
在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。
如果去掉括号,把程序改为以下形式:
【例】
#defineSQ(y)y*y
main(){
inta,sq;
printf("inputanumber:
");
scanf("%d",&a);
sq=SQ(a+1);
printf("sq=%d\n",sq);
}
运行结果为:
inputanumber:
3
sq=7
在这里总结宏的使用方法欢迎补充
1条件include
如下
CODE
#ifndefMAIN_H_//如果没有定义MAIN_H宏,就执行下面的程序
#defineMAIN_H_//定义MAIN_H宏,以后这段程序就不会再被include了,因为一开始就不满足ifndef条件
其它内容
#endif
上面在看到头文件时会看到作用就是阻止这个头文件被多次include
多次include就会出现重复的定义情况所以需要在每个头文件中都使用这个定义
如果还不是很了解要怎样使用可以看看c的标准头文件如fcntl.h
2条件编译
如下
CODE
#ifdef_DEBUG
printf("thisdebuginfo\n");
#endif
如果没有定义_DEBUG宏的话那么上面那一行是不会编译进去的
但是定义了_DEBUG后上面那行就会编译进去可以写个简单的程序测试
CODE
#include
intmain()
{
#ifdef_DEBUG
printf("helloworld\n");
#else
printf("nodebug");
#endif
return0;
}
第一次使用gcc-D_DEBUGmain.c
第二次使用gccmain.c
运行两次的结果看
3定义为某个值以便后面修改这个值时不用修改其它地方代码只要修改这个宏的定义就可以了
如一个软件的多语言版本等
如下
CODE
#include
#definePRINT_STR"你好DD"
main(){
printf(PRINT_STR);
return0;
}
编译时会把PRINT_STR代替成"你好DD"
以后想修改时就方便了
另外也可以定义为函数
#include
#ifdef_DEBUG
#defineA(x)a(x)
#else
#defineA(x)b(x)
#endif
inta(intx)
{
returnx+1;
}
intb(intx){
returnx+100;
}
intmain(){
printf("A(10)valueis%d",A(10));
return0;
}
[/code]
其实也可以定义成
#defineAa
但是定义成A(x)后只有A后面带一个(x)类型的编译器才会执行替换比较安全可以保证只替换函数而不替换变量
第四个
可变参数宏
有些时候定义一个宏来代替某个函数但是这个函数是可变参数的话那就需要考虑办法了
定义方法如下
CODE
#definePRINT(...)printf(__VA_ARGS__)
#include
intmain(){
PRINT("%d%s%s",1,"吃饭了吗smileMM:
)","\n");
return0;
}
第五个宏组合
也就是##和#的用法
##是连接符号连接两个宏
#是把名字代替成字符串
如下
CODE
#defines5(a)supper_##a
#include
voidsupper_printf(constchar*p)
{
printf("thisissupperprintf:
\n%s\n",a);
}
intmain()
{
s5(printf)("helloowrld");
return0;
}
#用法如下
#include
#defines(p)#p
intmain(){
printf(s(p)"\n");
return0;
}
运行一下就知道了
最后附上网上找到的宏定义的概念
第一篇
第九章预处理命令
预处理的概念:
编译之前的处理
C的预处理主要有三个方面的内容:
宏定义、文件包含、条件编译
预处理命令以符号“#”开头。
9.1宏定义
9.1.1不带参数的宏定义
宏定义又称为宏代换、宏替换,简称“宏”
格式:
#define标识符字符串
其中的标识符就是所谓的符号常量,也称为“宏名”
预处理(预编译)工作也叫做宏展开:
将宏名替换为字符串。
掌握"宏"概念的关键是“换”。
一切以换为前提、做任何事情之前先要换,准确理解之前就要“换”。
即在对相关命令或语句的含义和功能作具体分析之前就要换,“不管三七二十一,先换了再说”。
那么剩下的问题就简单了:
1把谁换掉?
2换成什么?
#definePI3.1415926
把程序中出现的PI全部换成3.1415926
li9_1.c
说明:
(1)宏名一般用大写
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。
例如:
数组大小常用宏定义
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用#undef命令终止宏定义的作用域
(7)宏定义可以嵌套
li9_2.c
(8)字符串""中永远不包含宏
(9)宏定义不分配内存,变量定义分配内存。
9.1.2带参数的宏
除了一般的字符串替换,还要做参数代换
格式:
#define宏名(参数表)字符串
例如:
#defineS(a,B)a*b
area=S(3,2);第一步被换为area=a*b;,第二步被换为area=3*2;
类似于函数调用,有一个哑实结合的过程
li9_3.c
(1)实参如果是表达式容易出问题
#defineS®r*r
area=S(a+B);第一步换为area=r*r;,第二步被换为area=a+b*a+b;
正确的宏定义是#defineS®®*®
(2)宏名和参数的括号间不能有空格
(3)宏替换只作替换,不做计算,不做表达式求解
(4)函数调用在编译后程序运行时进行,并且分配内存。
宏替换在编译前进行,不分配内存
(5)宏的哑实结合不存在类型,也没有类型转换。
(6)函数只有一个返回值,利用宏则可以设法得到多个值
li9_4.c
(7)宏展开使源程序变长,函数调用不会
(8)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)
li9_5.c
分析该例中的"
9.2“文件包含”处理
一个文件包含另一个文件的内容
格式:
#include"文件名"
或
#include<文件名>
编译时以包含处理以后的文件为编译单位,被包含的文件是源文件的一部分。
li9_6a.cli9_6b.c
编译以后只得到一个目标文件.obj
被包含的文件又被称为“标题文件”或“头部文件”、“头文件”,并且常用.h作扩展名。
修改头文件后所有包含该文件的文件都要重新编译
头文件的内容除了函数原型和宏定义外,还可以有结构体定义,全局变量定义
(1)一个#include命令指定一个头文件
(2)文件1包含文件2,文件2用到文件3,则文件3的包含命令#include应放在文件1的头部第一行。
(3)包含可以嵌套
(4)<文件名>称为标准方式,系统到头文件目录查找文件
"文件名"则先在当前目录查找,而后到头文件目录查找
(5)被包含文件中的静态全局变量不用在包含文件中声明。
9.3条件编译
有些语句行希望在条件满足时才编译。
格式:
(1)
#ifdef标识符
程序段1
#else
程序段2
#endif
或
#ifdef
程序段1
#endif
当标识符已经定义时,程序段1才参加编译。
格式:
(2)
#ifndef标识符
格式:
(3)
#if表达式
li9_7.c
使用条件编译可以使目标程序变小,运行时间变短。
预编译使问题或算法的解决方案增多,有助于我们选择合适的解决方案。
1.简单的define定义
#define MAXTIME 1000
一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写
if(i 编译器在处理这个代码之前会对MAXTIME进行处理替换为1000。 这样的定义看起来类似于普通的常量定义CONST,但也有着不同,因为define的定义更像是简单的文本替换,而不是作为一个量来使用,这个问题在下面反映的尤为突出。 2.define的“函数定义” define可以像函数那样接受一些参数,如下 #define max(x,y) (x)>(y)? (x): (y); 这个定义就将返回两个数中较大的那个,看到了吗? 因为这个“函数”没有类型检查,就好像一个函数模板似的,当然,它绝对没有模板那么安全就是了。 可以作为一个简单的模板来使用而已。 但是这样做的话存在隐患,例子如下: #define Add(a,b) a+b; 在一般使用的时候是没有问题的,但是如果遇到如: c * Add(a,b) * d的时候就会出现问题,代数式的本意是a+b然后去和c,d相乘,但是因为使用了define(它只是一个简单的替换),所以式子实际上变成了 c*a + b*d 另外举一个例子: #define pin (int*); pin a,b; 本意是a和b都是int型指针,但是实际上变成int* a,b; a是int型指针,而b是int型变量。 这是应该使用typedef来代替define,这样a和b就都是int型指针了。 所以我们在定义的时候,养成一个良好的习惯,建议所有的层次都要加括号。 3.宏的单行定义 #define A(x) T_##x #define B(x) #@x #define C(x) #x 我们假设: x=1,则有: A (1)------〉T_1 B (1)------〉’1’ C (1)------〉\"1\" (这里参考了 hustli的文章) 4.define的多行定义 define可以替代多行的代码,例如MFC中的宏定义(非常的经典,虽然让人看了恶心) #define MACRO(arg1, arg2) do { \\ /* declarations */ \\ stmt1; \\ stmt2; \\ /* ... */ \\ } while(0) /* (no trailing ; ) */ 关键是要在每一个换行的时候加上一个\"\\\" 摘抄自 修补了几个bug 5.在大规模的开发过程中,特别是跨平台和系统的软件里,define最重要的功能是条件编译。 就是: #ifdef WINDOWS ...... ...... #endif #ifdef LINUX ...... ...... #endif 可以在编译的时候通过#define设置编译环境 6.如何定义宏、取消宏 //定义宏 #define [MacroName] [MacroValue] //取消宏 #undef [MacroName] 普通宏 #define PI (3.1415926) 带参数的宏 #define max(a,b) ((a)>(b)? (a),(b)) 关键是十分容易产生错误,包括机器和人理解上的差异等等。 7.条件编译 #ifdef XXX…(#else) …#endif 例如 #ifdef DV22_AUX_INPUT #define AUX_MODE 3 #else #define AUY_MODE 3 #endif #ifndef XXX … (#else) … #endif[Page] 8.头文件(.h)可以被头文件或C文件包含; 重复包含(重复定义) 由于头文件包含可以嵌套,那么C文件就有可能包含多次同一个头文件,就可能出现重复定义的问题的。 通过条件编译开关来避免重复包含(重复定义) 例如 #ifndef __headerfileXXX__ #define __headerfileXXX__ … 文件内容 … #endif
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- cpp 使用
![提示](https://static.bingdoc.com/images/bang_tan.gif)