您的位置 首页 > 德语词汇

define是什么意思,详解宏定义

大家好,关于define是什么意思很多朋友都还不太明白,不过没关系,因为今天小编就来为大家分享关于详解宏定义的知识点,相信应该可以解决大家的一些困惑和问题,如果碰巧可以解决您的问题,还望关注下本站哦,希望对各位有所帮助!

C语言中用到宏定义的地方很多,如在头文件中为了防止头文件被重复包含,则用到:

#ifndefcTest_Header_h\n#definecTest_Header_h\n//头文件内容\n#endif

在我们常用的stdio.h头文件中也可以见到很多宏定义,如:

define是什么意思,详解宏定义

#defineBUFSIZ1024//缓冲区大小\n#defineEOF(-1)//表文件末尾\n#ifndefSEEK_SET\n#defineSEEK_SET0//表示文件指针从文件的开头开始\n#endif\n#ifndefSEEK_CUR\n#defineSEEK_CUR1//表示文件指针从现在的位置开始\n#endif\n#ifndefSEEK_END\n#defineSEEK_END2//表示文件指针从文件的末尾开始\n#endif

从开始写C语言到生成执行程序的流程大致如下(姑且忽略预处理之前的编译器的翻译处理流程等),在进行编译的第一次扫描(词法扫描和语法分析)之前,会有由预处理程序负责完成的预处理工作。

预处理工作是系统引用预处理程序对源程序中的预处理部分做处理,而预处理部分是指以“#”开头的、放在函数之外的、一般放在源文件的前面的预处理命令,如:包括命令#include,宏命令#define等,合理地利用预处理功能可以使得程序更加方便地阅读、修改、移植、调试等,也有利于模块化程序设计。本文主要介绍宏定义的以下几个部分:

1、概念及无参宏

一种最简单的宏的形式如下:\n#define宏名替换文本

每个#define行(即逻辑行)由三部分组成:第一部分是指令#define自身,“#”表示这是一条预处理命令,“define”为宏命令。第二部分为宏(macro),一般为缩略语,其名称(宏名)一般大写,而且不能有空格,遵循C变量命令规则。“替换文本”可以是任意常数、表达式、字符串等。在预处理工作过程中,代码中所有出现的“宏名”,都会被“替换文本”替换。这个替换的过程被称为“宏代换”或“宏展开”(macroexpansion)。“宏代换”是由预处理程序自动完成的。在C语言中,“宏”分为两种:无参数和有参数。

无参宏是指宏名之后不带参数,上面最简单的宏就是无参宏。

#defineM5//宏定义\n#definePI3.14//宏定义\ninta[M];//会被替换为:inta[5];\nintb=M;//会被替换为:intb=5;\nprintf("PI=%.2f\\n",PI);//输出结果为:PI=3.14

注意宏不是语句,结尾不需要加“;”,否则会被替换进程序中,如:

#defineN10;//宏定义\nintc[N];//会被替换为:intc[10;];\n//error:…main.c:133:11:Expected']'

以上几个宏都是用来代表值,所以被成为类对象宏(object-likemacro,还有类函数宏,下面会介绍)。

如果要写宏不止一行,则在结尾加反斜线符号使得多行能连接上,如:

#defineHELLO"hello\\\ntheworld"

注意第二行要对齐,否则,如:

#defineHELLO"hellothewo\\\nrld"\nprintf("HELLOis%s\\n",HELLO);\n//输出结果为:HELLOishellotheworld

也就是行与行之间的空格也会被作为替换文本的一部分

而且由这个例子也可以看出:宏名如果出现在源程序中的“”内,则不会被当做宏来进行宏代换。

#defineM5//宏定义\n#defineMMM*M//宏的嵌套\nprintf("MM=%d\\n",MM);//MM被替换为:MM=M*M,然后又变成MM=5*5

宏代换的过程在上句已经结束,实际的5*5相乘过程则在编译阶段完成,而不是在预处理器工作阶段完成,所以宏不进行运算,它只是按照指令进行文字的替换操作。再强调下,宏进行简单的文本替换,无论替换文本中是常数、表达式或者字符串等,预处理程序都不做任何检查,如果出现错误,只能是被宏代换之后的程序在编译阶段发现。

宏定义必须写在函数之外,其作用域是#define开始,到源程序结束。如果要提前结束它的作用域则用#undef命令,如:

#defineM5//宏定义\nprintf("M=%d\\n",M);//输出结果为:M=5\n#defineM100//取消宏定义\nprintf("M=%d\\n",M);//error:…main.c:138:24:Useofundeclaredidentifier'M'

也可以用宏定义表示数据类型,可以使代码简便:

#defineSTUstructStudent//宏定义STU\nstructStudent{//定义结构体Student\nchar*name;\nintsNo;\n};\nSTUstu={"Jack",20};//被替换为:structStudentstu={"Jack",20};\nprintf("name:%s,sNo:%d\\n",stu.name,stu.sNo);

如果重复定义宏,则不同的编译器采用不同的重定义策略。有的编译器认为这是错误的,有的则只是提示警告。Xcode中采用第二种方式。如:

#defineM5//宏定义\n#defineM100//重定义,warning:…main.c:26:9:'M'macroredefined

这些简单的宏主要被用来定义那些显式常量(ManifestConstants)(StephenPrata,2004),而且会使得程序更加容易修改,特别是某一常量的值在程序中多次被用到的时候,只需要改动一个宏定义,则程序中所有出现该变量的值都可以被改变。而且宏定义还有更多其他优点,如使得程序更容易理解,可以控制条件编译等。

两者都可以用来表示数据类型,如:

#defineINT1int\ntypedefintINT2;

两者是等效的,调用也一样:

INT1a1=3;\nINT2a2=5;

但当如下使用时,问题就来了:

#defineINT1int*\ntypedefint*INT2;\nINT1a1,b1;\nINT2a2,b2;\nb1=&m;//...main.c:185:8:Incompatiblepointertointegerconversionassigningto'int'from'int*';remove&\nb2=&n;//OK

因为INT1a1,b1;被宏代换后为:int*a1,b1;即定义的是一个指向int型变量的指针a1和一个int型的变量b1.而INT2a2,b2;表示定义的是两个变量a2和b2,这两个变量的类型都是INT2的,也就是int*的,所以两个都是指向int型变量的指针。

所以两者区别在于,宏定义只是简单的字符串代换,在预处理阶段完成。而typede不是简单的字符串代换,而是可以用来做类型说明符的重命名的,类型的别名可以具有类型定义说明的功能,在编译阶段完成的。

C语言中宏是可以有参数的,这样的宏就成了外形与函数相似的类函数宏(function-likemacro),如:

printf(“MEAN=%d\\n”,MEAN(7,9));//输出结果:MEAN=8

和函数类似,在宏定义中的参数成为形式参数,在宏调用中的参数成为实际参数。

而且和无参宏不同的一点是,有参宏在调用中,不仅要进行宏展开,而且还要用实参去替换形参。如:

#defineM5//无参宏\n#defineCOUNT(M)M*M//有参宏\nprintf("COUNT=%d\\n",COUNT(10));//替换为:COUNT(10)=10*10\n//输出结果:COUNT=100

这看上去用法与函数调用类似,但实际上是有很大差别的。如:

#defineCOUNT(M)M*M//定义有参宏\nintx=6;\nprintf("COUNT=%d\\n",COUNT(x+1));//输出结果:COUNT=13\nprintf("COUNT=%d\\n",COUNT(++x));//输出结果:COUNT=56//warning:...main.c:161:34:Multipleunsequencedmodificationsto'x'

这两个结果和调用函数的方法的结果差别很大,因为如果是像函数那样的话,COUNT(x+1)应该相当于COUNT(7),结果应该是7*7=49,但输出结果却是21。原因在于,预处理器不进行技术,只是进行字符串替换,而且也不会自动加上括号(),所以COUNT(x+1)被替换为COUNT(x+1*x+1),代入x=6,即为6+1*6+1=13。而解决办法则是:尽量用括号把整个替换文本及其中的每个参数括起来:

#defineCOUNT(M)((M)*(M))

但即使用括号,也不能解决上面例子的最后一个情况,COUNT(++x)被替换为++x*++x,即为7*8=56,而不是想要7*7=49,解决办法最简单的是:不要在有参宏用使用到“++”、“–”等。

上面说到宏名中不能有空格,宏名与形参表之间也不能有空格,而形参表中形参之间可以出现空格:

#defineSUM(a,b)a+b//定义有参宏\nprintf("SUM=%d\\n",SUM(1,2));//调用有参宏。BuildFailed!\n因为SUM被替换为:(a,b)a+b

如果用函数求一个整数的平方,则是:

intcount(intx){\nreturnx*x;\n}

所以在宏定义中:#defineCOUNT(M)M*M中的形参不分配内存单元,所以不作类型定义。而函数intcount(intx)中形参是局部变量,会在栈区分配内存单元,所以要作类型定义,而且实参与形参之间是“值传递”。而宏只是符号代换,不存在值传递。

宏定义也可以用来定义表达式或者多个语句。如:

#defineJI(a,b)a=i+3;b=j+5;//宏定义多个语句\ninti=5,j=10;\nintm=0,n=0;\nJI(m,n);//宏代换后为:m=i+3,n=j+5;\nprintf("m=%d,n=%d\\n",m,n);//输出结果为:m=8,n=15

3、#运算符

#defineSUM(a,b)((a)+(b))

我们想要输出“1+2+3+4=10”,用以下方式显得比较麻烦,有重复代码,而且中间还有括号:

printf("(%d+%d)+(%d+%d)=%d\\n",1,2,3,4,SUM(1+2,3+4));

那么这时可以考虑用#运算符来在字符串中包含宏参数,#运算符的用处就是把语言符号转化为字符串。例如,如果a是一个宏的形参,则替换文本中的#a则被系统转化为“a”。而这个转化的过程成为“字符串化(stringizing)”。用这个方法实现上面的要求:

#defineSUM(a,b)printf(#a"+"#b"=%d\\n",((a)+(b)))//宏定义,运用#运算符\nSUM(1+2,3+4);//宏调用\n//输出结果:1+2+3+4=10

调用宏时,用1+2代替a,用3+4代替b,则替换文本为:printf(“1+2””+”“3+4””=%d\\n”,((1+2)+(3+4))),接着字符串连接功能将四个相邻的字符串转换为一个字符串:

"1+2+3+4=%d\\n"

4、##运算符

和#运算符一样,##运算符也可以用在替换文本中,而它的作用是起到粘合的作用,即将两个语言符号组合成一个语言符号,所以又称为“预处理器的粘合剂(PreprocessorGlue)”。用法:

#defineNAME(n)num##n//宏定义,使用##运算符\nintnum0=10;\nprintf("num0=%d\\n",NAME(0));//宏调用

NAME(0)被替换为num##0,被粘合为:num0。

5、可变宏:…和__VA_ARGS__

我们经常要输出结果时要多次使用prinf(“…”,…);如果用上面例子#defineSUM(a,b)printf(#a”+“#b”=%d\\n”,((a)+(b))),则格式比较固定,不能用于输出其他格式。

这时我们可以考虑用可变宏(VariadicMacros)。用法是:

#definePR(...)printf(__VA_ARGS__)//宏定义\nPR("hello\\n");//宏调用\n//输出结果:hello

在宏定义中,形参列表的最后一个参数为省略号“…”,而“__VA_ARGS__”就可以被用在替换文本中,来表示省略号“…”代表了什么。而上面例子宏代换之后为:printf(“hello\\n”);

#definePR2(X,...)printf("Message"#X":"__VA_ARGS__)//宏定义\ndoublemsg=10;\nPR2(1,"msg=%.2f\\n",msg);//宏调用\n//输出结果:Message1:msg=10.00

在宏调用中,X的值为10,所以#X被替换为”1”。宏代换后为:

printf("Message""1"":""msg=%.2f\\n",msg);

接着这4个字符串连接成一个:

printf("Message1:msg=%.2f\\n",msg);

要注意的是:省略号“…”只能用来替换宏的形参列表中最后一个!

关于define是什么意思和详解宏定义的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023