宏定义中#等的用法

合集下载

C语言宏定义#define

C语言宏定义#define

C语言宏定义#define一、数值宏常量#define 宏定义是个演技非常高超的替身演员,但也会经常耍大牌的,所以我们用它要慎之又慎。

它可以出现在代码的任何地方,从本行宏定义开始,以后的代码就就都认识这个宏了;也可以把任何东西定义成宏。

因为编译器会在预编译的时候用真身替换替身,而在我们的代码里面却又用常常用替身来帮忙。

看例子:#define PI 3.141592654在此后的代码中你尽可以使用PI 来代替3.141592654,而且你最好就这么做。

不然的话,如果我要把PI 的精度再提高一些,你是否愿意一个一个的去修改这串数呢?你能保证不漏不出错?而使用PI 的话,我们却只需要修改一次。

这种情况还不是最要命的,我们再看一个例子:#define ERROR_POWEROFF -1如果你在代码里不用ERROR_POWEROFF 这个宏而用-1,尤其在函数返回错误代码的时候(往往一个开发一个系统需要定义很多错误代码)。

肯怕上帝都无法知道-1 表示的是什么意思吧。

这个-1,我们一般称为“魔鬼数”,上帝遇到它也会发狂的。

所以,我奉劝你代码里一定不要出现“魔鬼数”。

第一章我们详细讨论了const 这个关键字,我们知道const 修饰的数据是有类型的,而define 宏定义的数据没有类型。

为了安全,我建议你以后在定义一些宏常数的时候用const代替,编译器会给const 修饰的只读变量做类型校验,减少错误的可能。

但一定要注意const修饰的不是常量而是readonly 的变量,const 修饰的只读变量不能用来作为定义数组的维数,也不能放在case 关键字后面。

二、字符串宏常量除了定义宏常数之外,经常还用来定义字符串,尤其是路径:A),#define ENG_PA TH_1 E:\English\listen_to_this\listen_to_this_3B),#define ENG_PATH_2 “E:\English\listen_to_this\listen_to_this_3”噢,到底哪一个正确呢?如果路径太长,一行写下来比较别扭怎么办?用反斜杠接续符啊:C), #define ENG_PA TH_3 E:\English\listen_to_this\listen\_to_this_3还没发现问题?这里用了4 个反斜杠,到底哪个是接续符?回去看看接续符反斜杠。

C语言中的三种预处理功能

C语言中的三种预处理功能

C语言中的三种预处理功能C语言中的三种预处理功能导语:预处理指令是以#号开头的代码行。

#号必须是该行除了任何空白字符外的第一个字符。

#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。

整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。

下面是C语言三种预处理功能,欢迎阅读:指令用途# 空指令,无任何效果#include 包含一个源代码文件#define 定义宏#undef 取消已定义的宏#if 如果给定条件为真,则编译下面代码#ifdef 如果宏已经定义,则编译下面代码#ifndef 如果宏没有定义,则编译下面代码#elif 如果前#if条件不为真,当前条件为真,则编译下面代码,其实就是else if的简写#endif 结束一个#if……#else条件编译块#error 停止编译并显示错误信息特殊符号预编译程序可以识别一些特殊的符号。

预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

注意,是双下划线,而不是单下划线。

FILE 包含当前程序文件名的字符串LINE 表示当前行号的整数DATE 包含当前日期的字符串STDC 如果编译器遵循ANSI C标准,它就是个非零值TIME 包含当前时间的字符串//例#includeint main(){printf("Hello World! ");printf("%s ",__FILE__);printf("%d ",__LINE__);return 0;}1. 宏定义不带参数宏定义又称为宏代换、宏替换,简称“宏”。

预处理(预编译)工作也叫做宏展开:将宏名替换为字符串,即在对相关命令或语句的含义和功能作具体分析之前就要换。

格式:#define 标识符字符串其中标识符就是所谓的符号常量,也称为“宏名”。

例:#define Pi 3.1415926//把程序中出现的Pi全部换成3.1415926 说明:(1)宏名一般用大写;(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。

C语言宏定义

C语言宏定义

C语言宏定义C语言宏定义C语言既具有高级语言的功能,又具有低级语言的许多功能。

那么大家知道C语言宏定义是怎样的呢?下面一起来看看!宏定义是预处理命令的一种,它允许用一个标识符来表示一个字符串。

先看一个例子:#include#define N 100int main(){ int sum = 20 + N; printf("%d ", sum); return 0;}运行结果:120该示例中的语句int sum = 20 + N;,N被100代替了。

#define N 100就是宏定义,N为宏名,100是宏的内容。

在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。

宏定义是由源程序中的宏定义命令#define完成的,宏代换是由预处理程序完成的。

宏定义的一般形式为:#define 宏名字符串#表示这是一条预处理命令,所有的预处理命令都以#开头。

define是预处理命令。

宏名是标识符的一种,命名规则和标识符相同。

字符串可以是常数、表达式等。

这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符串等同,它不需要双引号。

程序中反复使用的表达式就可以使用宏定义,例如:#define M (n*n+3*n)它的作用是指定标识符M来代替表达式(y*y+3*y)。

在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去替换所有的宏名M,然后再进行编译。

将上面的例子补充完整:#include#define M (n*n+3*n)int main(){ int sum, n; printf("Input a number: "); scanf("%d", &n); sum = 3*M+4*M+5*M; printf("sum=%d ", n); return 0;}运行结果:Input a number: 10↙sum=1560上面的程序中首先进行宏定义,定义M来替代表达式(n*n+3*n),在sum=3*M+4*M+5*M中作了宏调用。

define宏定义中的#,##,@#及符号

define宏定义中的#,##,@#及符号

d efine宏定义中的#,##,@#及\符号(ZT)C++ STL学习2011-04-24 18:04:03 阅读19 评论0 字号:大中小订阅1、# (stringizing)字符串化操作符。

其作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。

其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。

如:#define example(instr) printf("the input string is:\t%s\n",#instr)#define example1(instr) #instr当使用该宏定义时:example(abc);在编译时将会展开成:printf("the input string is:\t%s\n","abc");string str=example1(abc);将会展成:string str="abc";注意:对空格的处理a。

忽略传入参数名前面和后面的空格。

如:str=example1( abc );将会被扩展成str="abc";b.当传入参数名间存在空格时,编译器将会自动连接各个子字符串,用每个子字符串中只以一个空格连接,忽略其中多余一个的空格。

如:str=exapme( abc def); 将会被扩展成str="abc def";2、## (token-pasting)符号连接操作符宏定义中:参数名,即为形参,如#define sum(a,b) (a+b);中a和b 均为某一参数的代表符号,即形式参数。

而##的作用则是将宏定义的多个形参成一个实际参数名。

如:#define exampleNum(n) num##nint num9=9;使用:int num=exampleNum(9); 将会扩展成int num=num9;注意:1.当用##连接形参时,##前后的空格可有可无。

C语言中的宏定义

C语言中的宏定义
1. n = MAX(i, MAX(j,k));
下面是预处理后的这条语句:
1. n=((i)>(((j)>(k)?(j):(k)))?(i):(((j)>(k)?(j):(k))));
2) 、宏参数没有类型检查。当一个函数被调用时,编译器会检查每一个参数来确认它们是 否是正确的类型。如果不是,或者将参数转换成正确的类型,或者由编译器产生一个出错信 息。预处理器不会检查宏参数的类型,也不会进行类型转换。 3) 、无法用一个指针来指向一个宏。如在 17.7 节中将看到的,C 语言允许指针指向函数。 这一概念在特定的编程条件下非常有用。 宏会在预处理过程中被删除, 所以不存在类似的 “指 向宏的指针”。因此,宏不能用于处理这些情况。 4) 、宏可能会不止一次地计算它的参数。函数对它的参数只会计算一次,而宏可能会计算 两次甚至更多次。如果参数有副作用,多次计算参数的值可能会产生意外的结果。考虑下面 的例子,其中 MAX 的一个参数有副作用:
1. #define getchar() getc(stdin)
空的参数列表不是一定确实需要, 但可以使 getchar 更像一个函数。 (没错, 这就是<stdio.h> 中的 getchar,getchar 的确就是个宏,不是函数 ——虽然它的功能像个函数。) 使用带参数的宏替代实际的函数的优点: 1) 、 程序可能会稍微快些。一个函数调用在执行时通常会有些额外开销—— 存储上下文 信息、复制参数的值等。而一个宏的调用则没有这些运行开销。 2) 、 宏会更“通用”。与函数的参数不同,宏的参数没有类型。因此,只要预处理后的程序 依然是合法的,宏可以接受任何类型的参数。例如,我们可以使用 MAX 宏从两个数中选出 较大的一个,数的类型可以是 int,long int,float,double 等等。 但是带参数的宏也有一些缺点。

c语言中宏定义中if else语法格式

c语言中宏定义中if else语法格式

C语言中宏定义中if else语法格式1. 宏定义概述在C语言中,宏定义是一种预处理指令,用于将一个标识符替换为一个指定的字符串或代码段。

宏定义可以简化代码,提高代码的可读性和可维护性。

在宏定义中使用if else语法格式,可以根据条件来选择不同的代码段进行替换,从而实现代码的灵活性和通用性。

2. 宏定义中的if else语法格式在C语言中,宏定义中的if else语法格式为:```#define 宏名源代码``````#ifdef 宏名源代码1#else源代码2#endif```3. 宏名的说明宏名是一个标识符,用于在代码中表示一个特定的宏定义。

在定义宏名时,通常使用大写字母和下划线来命名,以区分于普通变量和函数名。

4. ifdef指令#ifdef是一个预处理指令,用于判断指定的宏名是否已经定义。

如果宏名已经定义,则执行源代码1,否则执行源代码2。

5. else指令#ifdef指令的作用是在宏名已经定义的情况下执行源代码1,而else 指令则是在宏名未定义的情况下执行源代码2。

6. endif指令#ifdef和#else之间的源代码1和源代码2是通过#endif指令来结束的。

该指令用于标记#ifdef的结束位置,以便让编译器知道代码的分界。

7. 实例演示下面通过一个实例演示宏定义中的if else语法格式:```#define DEBUG#ifdef DEBUGprintf("Debugging information\n");#elseprintf("No debugging information\n");#endif```在上面的例子中,首先定义了一个名为DEBUG的宏名,然后使用#ifdef指令来判断DEBUG是否已经定义,如果已定义则输出"Debugging information",否则输出"No debugging information"。

define宏定义函数

define宏定义函数

define宏定义函数宏定义是C/C++语言中一种预处理指令,可以用来对代码中的固定值或者复杂表达式进行替换,减少代码编写时的重复性问题,提高代码重用性和可读性。

宏定义的语法格式为:#define 宏名称宏替换文本其中,宏名称是自定义的标识符,宏替换文本可以是单个字符、数值、表达式、函数调用等。

宏定义的有效范围是从定义处到文件末尾或者遇到#undef指令为止。

宏定义的优点在于,它可以让程序员在代码中使用一个短小的名称来代替一个复杂的表达式或者语句,从而提高代码可读性和可维护性。

同时,在编译时,编译器会将所有使用到宏定义的代码中的宏名称展开成对应的宏替换文本,从而使得程序的运行效率得到提高。

宏定义的应用场景非常广泛,例如:1.定义常量:可以使用#define宏定义,将一个固定的值定义为一个常量,方便在代码中多次使用。

例如:#define PI 3.14这样在后续的代码中就可以使用PI来代替3.14,提高可读性和可维护性。

2.定义函数:宏定义可以定义一些简单的函数。

例如:#define max(a,b) ((a)>(b)?(a):(b))这个宏定义表示求取两个数中的最大值,在后续的代码中可以用max(a,b)来代替((a)>(b)?(a):(b)),达到简洁、明了的效果。

3.定义缩写:在代码中,有时需要使用一些比较长的名称来表示一些事物,为了方便使用,可以用宏定义来缩写这些名称,在代码中使用时可以提高可读性和代码规范性。

例如:#define HTTP_STATUS_OK 200#define HTTP_STATUS_BAD_REQUEST 4004.条件编译:宏定义可以用于条件编译,在程序中加入一些特别的代码,根据不同的编译选项选择编译或者不编译这些代码。

例如:#ifdef DEBUGprintf("error message: %s\n", error_msg);#endif如果在编译程序的时候加上了-DDEBUG选项,则会将上述代码编译到程序中,否则会被忽略。

宏定义中##和#的作用

宏定义中##和#的作用
例如:
> #define STRCPY(dst, src) strcpy(dst, #src)

> STRCPY(buff, abc)
相当于strcpy(buff, "abc")
另外,如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开。
#define STRCPY(a, b) s源自rcpy(a ## _p, #b)
__attribute__ ((unused, alias(__stringify(name))))
得到
MODULE_DEVICE_TABLE(usb, products)
/*notes: struct usb_device_id products; */
<==> MODULE_GENERIC_TABLE(usb_device,products)
另外一些分隔标志是,包括操作符,比如+, -, *, /, [,], ...,所以尽管下面的
宏定义没有空格,但是依然表达有意义的定义:define add(a, b) a+b
而其强制连接的作用是,去掉和前面的字符串之间的空格,而把两者连接起来。
2.举列--试比较下述几个宏定义的区别
#define A1(name, type) type name_##type##_type或
<==> extern const struct usb_device_id __mod_usb_device_table
__attribute__ ((unused, alias("products")))
注意到alias attribute需要一个双引号,所以在这里使用了__stringify(name)来
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C语言宏定义中"#","#@"和"##"的用法一、引言#define macro(a)#a#define macro2(a,b)a##b#define macro3(a,b,c)a##b##c#a,表示a不再是一个变量,而变成了字符串"a"##表示连接,a##b,表示输入的参数名为ab,a##b##c同理,代表变量名为:abc测试例子:int x=3;int y=4;int xy=10;int xyz=20;CString str;OutputDebugString(macro(x));str.Format("%d",macro2(x,y));OutputDebugString(str);str.Format("%d",macro3(x,y,z));OutputDebugString(str);输出结果为:x1020第一个为x,marco(x),x变成了"x"字符串第二个为10,macro(x,y),就是变量xy第三个为20,macro(x,y,z),就是变量xyz二、一般用法#把宏参数变为一个字符串,#@把宏参数变为一个字符,##把两个宏参数贴合在一起。

#include<stdio.h>#include<limits.h>#define STR(s)#s//#与参数之间可以有空格#define TOCHAR(c)#@c#define CONS(a,b)int(a##e##b)//##与参数之间可以有空格int main(void){printf(STR(pele));//输出字符串"pele"printf("%c\n",TOCHAR(z));//输出字符zprintf("%d\n",CONS(2,3));//2e3输出:2000return0;}三、当宏参数是另一个宏的时候需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开的。

#define A(2)#define STR(s)#s#define CONS(a,b)int(a##e##b)printf("int max:%s\n",STR(INT_MAX));这行会被展开为:printf("int max:%s\n","INT_MAX");printf("%s\n",CONS(A,A));这一行被展开为:printf("%s\n",int(AeA));INT_MAX和A都不会再被展开,然而解决这个问题的方法很简单,多加一层中间转换宏。

加这层宏的用意是把所有宏的参数在这层里全部展开,那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数。

#define A(2)#define_STR(s)#s#define STR(s)_STR(s)//转换宏#define_CONS(a,b)int(a##e##b)#define CONS(a,b)_CONS(a,b)//转换宏printf("int max:%s\n",STR(INT_MAX));输出为:int max:0x7fffffffSTR(INT_MAX)-->_STR(0x7fffffff)-->"0x7fffffff"printf("%d\n",CONS(A,A));输出为:200CONS(A,A)-->_CONS((2),(2))-->int((2)e(2))以下为Minix3操作系统相关的源代码:#ifdef_ANSI#define__str(x)#x#define__xstr(x)__str(x)//转换宏_PROTOTYPE(void__bad_assertion,(const char*_mess));#define assert(expr)((expr)?(void)0:\__bad_assertion("Assertion\""#expr\"\"failed,file"__xstr(__FILE__)\",line"__xstr(__LINE__)"\n"))四、"#"和"##"的一些应用特例1、合并匿名变量名#define___ANONYMOUS1(type,var,line)type var##line#define__ANONYMOUS0(type,line)___ANONYMOUS1(type,_anonymous,line) #define ANONYMOUS(type)__ANONYMOUS0(type,__LINE__)例:ANONYMOUS(static int);即:static int_anonymous70;70表示该行行号;第一层:ANONYMOUS(static int);-->__ANONYMOUS0(static int,__LINE__);第二层:-->___ANONYMOUS1(static int,_anonymous,70);第三层:-->static int_anonymous70;即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;2、填充结构#define FILL(a){a,#a}enum IDD{OPEN,CLOSE};typedef struct MSG{IDD id;const char*msg;}MSG;MSG_msg[]={FILL(OPEN),FILL(CLOSE)};相当于:MSG_msg[]={{OPEN,"OPEN"},{CLOSE,"CLOSE"}};3、记录文件名#define_GET_FILE_NAME(f)#f#define GET_FILE_NAME(f)_GET_FILE_NAME(f)//转换宏static char FILE_NAME[]=GET_FILE_NAME(__FILE__);4、得到一个数值类型所对应的字符串缓冲大小#define_TYPE_BUF_SIZE(type)sizeof#type#define TYPE_BUF_SIZE(type)_TYPE_BUF_SIZE(type)char buf[TYPE_BUF_SIZE(INT_MAX)];-->char buf[_TYPE_BUF_SIZE(0x7fffffff)];-->char buf[sizeof"0x7fffffff"];这里相当于:char buf[11];五、C/C++宏定义的可变参数编写代码的过程中,经常会输出一些调试信息到屏幕上,一般会调用printf这类的函数。

但是当调试解决之后,我们需要手工将这些地方删除或者注释掉。

再这次的项目中就用到类似问题,为了调试程序,再一些地方输出了很多的信息,随着项目的调试,输出的信息越来越多。

于是就面临着,如何处理这些输出信息的语句。

简单删掉,不仅有一定的工作量,而且也不能保证之后就不出现问题,出现问题后这些信息还是有用的。

不去掉,带着调试信息就上线,这是明显不允许的。

于是就想到了一个可行的办法。

如下:void myprintf(char*fmt,...){}#ifdef DEBUG#define printf(fmt,args...)myprintf(fmt,##args)#endif调试阶段带着DEBUG调试,正式上线就可以把printf变成一个空函数了。

这样做的一个潜在风险是可能会导致默写glib函数需要调用printf输出错误log也给取消掉了。

令人欣慰的是,大部分glib调用的应该是fprintf。

虽然问题解决了,但是我对args...以及##args还是不太了解。

上网找了些gcc手册的资料如下:带有可变参数的宏(Macros with a Variable Number of Arguments)在1999年版本的ISO C标准中,宏可以象函数一样,定义时可以带有可变参数。

宏的语法和函数的语法类似。

下面有个例子:#define debug(format,...)fprintf(stderr,format,__VA_ARGS__)这里,‘…’指可变参数。

这类宏在被调用时,它(这里指‘…’)被表示成零个或多个符号,包括里面的逗号,一直到到右括弧结束为止。

当被调用时,在宏体(macro body)中,那些符号序列集合将代替里面的__VA_ARGS__标识符。

更多的信息可以参考CPP手册。

GCC始终支持复杂的宏,它使用一种不同的语法从而可以使你可以给可变参数一个名字,如同其它参数一样。

例如下面的例子:#define debug(format,args...)fprintf(stderr,format,args)这和上面举的那个ISO C定义的宏例子是完全一样的,但是这么写可读性更强并且更容易进行描述。

GNU CPP还有两种更复杂的宏扩展,支持上面两种格式的定义格式。

在标准C里,你不能省略可变参数,但是你却可以给它传递一个空的参数。

例如,下面的宏调用在ISO C里是非法的,因为字符串后面没有逗号:debug("A message")GNU CPP在这种情况下可以让你完全的忽略可变参数。

在上面的例子中,编译器仍然会有问题(complain),因为宏展开后,里面的字符串后面会有个多余的逗号。

为了解决这个问题,CPP使用一个特殊的‘##’操作。

书写格式为:#define debug(format,...)fprintf(stderr,format,##__VA_ARGS__)这里,如果可变参数被忽略或为空,‘##’操作将使预处理器(preprocessor)去除掉它前面的那个逗号。

如果你在宏调用时,确实提供了一些可变参数,GNU CPP也会工作正常,它会把这些可变参数放到逗号的后面。

象其它的pasted macro参数一样,这些参数不是宏的扩展。

参考资料:[1]/s/blog_550405a10100bqar.html[2]Minix操作系统内核代码。

相关文档
最新文档