使用

c语言的宏是在预处理时候对字符进行简单替换。

优点:1.如果要改一个变量,只需要改宏就可以了,也就是只改一次;2.宏函数展开和普通函数一样,但是它没有普通函数调用的过程,不需要压栈出栈等操作,所以效率高。

缺点:增大了编译后可执行文件的大小

比如常见的

#define MAX(a,b) ((a)>(b)?(a):(b))

变量使用括号包围,否则这样调用时

MAX(+,+)

结果并不是我们想要的

即使使用括号包围了,宏依然是有陷阱的,比如这样调用

a = ; MAX(a++, )

宏展开之后,成了这样:

((a++)>()?(a++):())

a++被执行了两次

再比如,在宏中调用了函数,那么,这个函数可能被执行了多次。

所以,我们可以这样定义:

#define MAX(x,y) ({ \
typeof(x) _##x=(x); typeof(y) _##y=(y); \
&_##x==&_##y; \
_##x>_##y?_##x:_##y; \
})

gdb调试

使用gdb调试宏的时候,加-g3选项,因为默认的-g选项,级别为2,看不到宏

(gdb) help macro
Prefix for commands dealing with C preprocessor macros. List of macro subcommands: macro define -- Define a new C/C++ preprocessor macro
macro expand -- Fully expand any C/C++ preprocessor macro invocations in EXPRESSION
macro expand-once -- Expand C/C++ preprocessor macro invocations appearing directly in EXPRESSION
macro list -- List all the macros defined using the `macro define' command
macro undef -- Remove the definition of the C/C++ preprocessor macro with the given name

#

When you put a # before an argument in a preprocessor macro, the preprocessor turns that argument into a character array
宏定义中的#是把跟在后面的参数转换成一个字符串

##分割连接符

在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。使用空格分段的话,替换之后有空格,如果我们不想要空格,可以用##分隔

.#define TYPE1(type,name) type name_##type##_type
.#define TYPE1(type,name) type name _##type##_type
.#define TYPE2(type,name) type name##_##type##_type

1中,name_是一个段,其中的name没有被宏替换

2和3中,name作为一个段,就会被宏替换。但是2中name和_之间有个空格

宏的相互调用

#define _STR(a) #a
#define STR(a) _STR(a)
#define CAT(a,b) a##b

STR(CAT(3,5))被处理为:"35"

_STR(CAT(3,5))被处理为:"CAT(3,5)"。因为宏替换只是进行很简单的字符替换。

可变参数宏

ISO C标准的可变参数宏定义,类似这个

#define debug(format, ...) fprintf(stderr,format, __VA_ARGS__)

对这个宏,"..."指可变参数。在展开时,"..."表示0个或多个符号,包括逗号,一直到括号结束为止。当调用时,那些符号序列将代替里面的__VA_ARGS__。
gcc 还支持这样的语法:

#define debug(format, args...) fprintf(stderr, format, args)

这和上面举的那个ISO C定义的宏例子是完全一样的,但是这么写可读性更强并且更容易进行描述。

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

但是,下面的宏调用在ISO C里是非法的,因为字符串后面没有逗号:

debug ("A message")

需要写成这样:

debug ("A message",)

又有了新的问题,上面宏展开后,成了这样:

fprintf(stderr, "A message", )

为了解决这个问题,使用"##"操作,宏改为:

#define debug(format, ...) fprintf (stderr,format, ## __VA_ARGS__)

如果可变参数被忽略或为空,‘##'操作将使预处理器去除掉它前面的那个逗号。如果你在宏调用时,确实提供了一些可变参数,GNU CPP也会工作正常,它会把这些可变参数放到逗号的后面。象其它的pasted macro参数一样,这些参数不是宏的扩展。

一个简单的日志

#define LOG(format, ...) \
save_log("[%d] " format "\n", __LINE__, ##__VA_ARGS__) void save_log(const char* format, ...)
{
va_list args; FILE* pf = fopen("log.txt", "a+"); va_start(args, format);
vfprintf(pf, format, args);
va_end(args); fclose(pf);
}

c语言宏的更多相关文章

  1. C语言 宏/macor/#define/

    C语言 宏/macor/#define 高级技巧 1.在进行调试的时候,需要进行打印/PRINT,可以通过define进行自定义.例如,自己最常用的DEBUG_PRINT() #define DEBU ...

  2. C语言宏的高级应用

    原文:C语言宏的高级应用 关于#和##在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号.比如 ...

  3. C 语言宏定义

    C 语言宏定义1.例子如下: #define PRINT_STR(s) printf("%s",s.c_str()) string str = "abcd"; ...

  4. 将C语言宏定义数值转换成字符串!

    将C语言宏定义转换成字符串! 摘自:https://blog.csdn.net/happen23/article/details/50602667 2016年01月28日 19:15:47 六个九十度 ...

  5. C语言宏应用-------#define STR(X) #X

    C语言宏应用-------#define STR(X) #X   #:会把参数转换为字符串 #define STR(x) #x #define MAX 100 STR(MAX) 会被扩展成" ...

  6. C语言 宏定义之可变参数

    可变参数宏定义 C99编译器标准允许你可以定义可变参数宏(variadic macros),这样你就可以使用拥有可以变化的参数表的宏.可变参数宏就像下面这个样子: #define dbgprint(. ...

  7. C语言宏的使用

    使用条件宏进行条件编译 譬如,对于同一份代码,我想编译出两个不同的版本,在其中一个版本中去掉某一部分功能, 这时可以通过条件宏判断是否编译,例: 如果不使用条件宏进行控制,想编译两个不同版本的程序,就 ...

  8. C语言宏定义时#(井号)和##(双井号)的用法1

    #在英语里面叫做 pound 在C语言的宏定义中,一个#表示字符串化:两个#代表concatenate 举例如下: #include <iostream> void quit_comman ...

  9. C语言宏定义时#(井号)和##(双井号)的用法

    C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念).下面对常遇到的宏的使用问题做了简单总结. 关于#和## 在C语言的宏中,#的功能是将其后面 ...

  10. c语言宏定义

    一. #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时 ...

随机推荐

  1. CAShapeLayer的使用[2]

    CAShapeLayer的使用[2] CAShapeLayer支持的动画类型有如下这些. ------------------------------------------------------- ...

  2. Git分布式工作流程

    Git官网给出了三种分布式工作流程: 集中式工作流程 集成管理者工作流 司令官与副官工作流 这里以私有gitserver服务器上的git-test项目为例,简单说明集中式工作流程. 基于分支的开发策略 ...

  3. .Net Intelligencia.UrlRewriter 重定向参数中文支持配置方法

    在使用.Net 官方 Url重定向组件时,发现若原地址包含中文,如:http://localhost/首页.html 重定向为:http://localhost/index.aspx?id=首页  时 ...

  4. vue项目出现的错误汇总

    报错一: expected "indent", got "!" 通过vue-cli创建的项目,不需要在webpack.base.conf.js中再手动配置关于c ...

  5. UVa 10900 - So you want to be a 2n-aire?(期望DP)

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  6. 「LGP4719【模板】动态dp」

    题目 尽管知道这个东西应该不会考了,但是还是学一学吧 哎要是去年noip之前学该多好 动态\(dp\)就是允许修改的一个\(dp\),比如这道题,我们都知道这是一个树上最大点权独立集 众所周知方程长这 ...

  7. [CQOI2006]凸多边形(半平面相交)

    嘟嘟嘟 本来我要写feng shui这道题的.然后网上都说什么半平面相交,于是我还得现学这个东西,就来刷这道模板题了. 所谓的半平面相交和高中数学的分数规划特别像.比如这道题,把每一条边看成一条有向直 ...

  8. 智慧监狱来了!SaCa EMM 助推现代监狱建设迈上新台阶

    近几年来,移动化已经成为警务信息化建设的必然方向,为紧急和突发事件的处理提供了信息依据.为监狱民警提供移动警务所需的信息管理系统,司法系统从很早就开始推动警务通项目.为了落实移动警务的工作需求,很多监 ...

  9. Owin+ASP.NET Identity浅析系列(一)用户登录注册

    在今天,读书有时是件“麻烦”事.它需要你付出时间,付出精力,还要付出一份心境.--仅以<Owin+ASP.NET Identity浅析系列>来祭奠那逝去的…… 使用VS2015创建MVC项 ...

  10. 新闻cms管理系统 (补)-----路由优化一

    修改后台的入口文件,将访问后台页面的入口文件设为admin.php,并且访问该文件的模块默认为admin模块. 添加后台入口文件具体操作步骤: (1)项目目录下新建admin.php (2)实现入口文 ...