c语言宏
使用
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语言宏的更多相关文章
- C语言 宏/macor/#define/
C语言 宏/macor/#define 高级技巧 1.在进行调试的时候,需要进行打印/PRINT,可以通过define进行自定义.例如,自己最常用的DEBUG_PRINT() #define DEBU ...
- C语言宏的高级应用
原文:C语言宏的高级应用 关于#和##在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号.比如 ...
- C 语言宏定义
C 语言宏定义1.例子如下: #define PRINT_STR(s) printf("%s",s.c_str()) string str = "abcd"; ...
- 将C语言宏定义数值转换成字符串!
将C语言宏定义转换成字符串! 摘自:https://blog.csdn.net/happen23/article/details/50602667 2016年01月28日 19:15:47 六个九十度 ...
- C语言宏应用-------#define STR(X) #X
C语言宏应用-------#define STR(X) #X #:会把参数转换为字符串 #define STR(x) #x #define MAX 100 STR(MAX) 会被扩展成" ...
- C语言 宏定义之可变参数
可变参数宏定义 C99编译器标准允许你可以定义可变参数宏(variadic macros),这样你就可以使用拥有可以变化的参数表的宏.可变参数宏就像下面这个样子: #define dbgprint(. ...
- C语言宏的使用
使用条件宏进行条件编译 譬如,对于同一份代码,我想编译出两个不同的版本,在其中一个版本中去掉某一部分功能, 这时可以通过条件宏判断是否编译,例: 如果不使用条件宏进行控制,想编译两个不同版本的程序,就 ...
- C语言宏定义时#(井号)和##(双井号)的用法1
#在英语里面叫做 pound 在C语言的宏定义中,一个#表示字符串化:两个#代表concatenate 举例如下: #include <iostream> void quit_comman ...
- C语言宏定义时#(井号)和##(双井号)的用法
C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念).下面对常遇到的宏的使用问题做了简单总结. 关于#和## 在C语言的宏中,#的功能是将其后面 ...
- c语言宏定义
一. #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时 ...
随机推荐
- 对于当下国产CPU如火如荼有感
国家在国家战略层面去做国产CPU这个事情,从初衷来说是好的.国产CPU战略如果能够实现,则会大大加强我国在计算机产业领域从头到尾的话语权与技术竞争力.但是个人觉得,事情不是那么简单.我将从下面几个方面 ...
- [tools]excel转lua的python实现
time:2015/04/13 描述:需要将excel表格内容转成lua,并且作为工具使用,能够批量转换 步骤: (1)文章[1]已经做了大部分的内容,而且也已经能够使用了 (2)根据自己新的需求: ...
- Linux中脚本的使用方法
Linux中脚本的使用方法 一.前言 关于Linux中的脚本的用法,一直没有时间去好好地总结,正好今天下雨,就好好的整理一下思路吧,其实精通了一门语言,比如C语言,学习其他语言需要的成本是非常少的,同 ...
- Android Proguard使用技巧
1.混淆后解码 ProGuard运行结束后,输出以下文件: dump.txt :描述.apk文件中所有类文件间的内部结构 mapping.txt:列出了原始的类,方法和字段名与混淆后代码间的映射.这个 ...
- ZT android -- 蓝牙 bluetooth (四)OPP文件传输
android -- 蓝牙 bluetooth (四)OPP文件传输 分类: Android的原生应用分析 2013-06-22 21:51 2599人阅读 评论(19) 收藏 举报 4.2源码AND ...
- pip 升级 Appium-Python-Client
第一种方法: pip install --upgrade Appium-Python-Client 如果出现权限提醒: sudo -H pip install --upgrade Appium-Pyt ...
- msysgit 上传文件夹,规范化的日常
在我们第一次成功的上传到github之后,要上传文件夹的我们要在msysgit里输入些什么呢? 选择要上传的文件夹前一项右键点击git bash here 进入msysgit后 首先初始化,输入 gi ...
- tar 打包带软连接的文件
打包普通文件夹,压缩带参数z,创建tar.gz tar -cvf ./tmp/SK_Aug_camera.tar ./gap_40_5 但是文件夹里含有软连接,带参数 h tar -cvhf ./tm ...
- xss练习平台及writeup
今天玩了一天的xss. 分享几个xss game https://xss.haozi.me/#/0x00 http://47.94.13.75/test/ writeup:http://www.cn ...
- Python 多线程 线程安全、daemon简介 (四)
线程安全 只能在Winodws下的ipython中演示,Python命令行.Pycharm.Mac下的ipython都演示不出效果 import threading def worker(): for ...