文章目录
第一章 C语言基础知识学习一(printf,scanf,数组) link.
第二章 C语言基础知识学习二 (数组、指针) 链接: link.
第三章 C语言基础知识学习三
前言
宏定义,预编译命令,可变参数宏的使用
第一题 利用log输出调试信息
1. 要求:
设计输出一种包含代码行号,函数名称等信息的log函数宏,要考虑可变参数的情况
2. 代码实现:
#include <stdio.h>
#define log1(frm, ...) {\
printf("[%s : %d] ",__func__,__LINE__);\
printf(frm, ##__VA_ARGS__);\
}
#define log2(...) {\
printf("[%s : %d] ",__func__,__LINE__);\
printf(__VA_ARGS__);\
}
#define log3(frm, arg...) {\
printf("[%s : %d] ",__func__,__LINE__);\
printf(frm, ##arg);\
}
void func(int a) {
log1("a = %d\n", a);
log2("a = %d\n", a);
log3("a = %d\n", a);
}
int main() {
int a = 123;
printf("a = %d\n", a);
log1("a = %d\n", a);
log2("a = %d\n", a);
log3("a = %d\n", a);
func(a);
log1("hello world\n");
log2("hello world\n");
log3("hello world\n");
return 0;
}
3. 总结归纳:
- 介绍几个预处理器符号
符号 | 含义 |
---|---|
FILE | 进行编译的源文件名 |
LINE | 文件当前行的行号 |
DATA | 文件被编译的日期 |
TIME | 文件被编译的时间 |
- 可变参数宏的几个重要参数的设置
C99中规定宏可以像函数一样带有可变参数,比如
#define LOG(frm, ...) printf(frm, __VA_ARGS__)
其中,...表示参数可变,__VA_ARGS__在预处理中为实际的参数集所替换
GCC是支持C99这种新的规定标准的,同时GCC同样也支持下面这种写法,
区别只是参数符号有变化。
#define LOG(frm, args...) printf(frm, args)
但是这种写法有一个小bug,就是当log函数中只有一个参数frm时,编译会出错,
宏在替换的过程中出现了第二个参数为空的情况。
针对这种情况,有如下处理办法
3. #define LOG(frm, ...) printf(frm, ##__VA_ARGS__)
4. #define LOG(frm, args...) printf(frm, ##args)
5. #define LOG(...) printf(__VA_ARGS__)
这几种方法里面第三种最为暴力,但是也比较好理解,
但是前两种方法,对于##的解释我是从网上找的:##预处理操作符除了具备连接两个符号的功能,
当一端没有符号时可以不做连接处理,同时去除“,”以保证程序可以编译执行。
但是我不太明白的是为什么会同时去除“,”呢?希望有看到的大佬讲解一下,感谢!
- 变参宏定义配合条件编译#ifdef #else #endif可以很方便的区别调试和发布代码,比如调试时输出调试信息,正式发布时则不输出,代码如下
#ifdef DEBUG
#define LOG(frm, ...) printf(frm, ##__VA_ARGS__)
#else
#define LOG(frm, ...)
#endif
在调试代码的时候,LOG宏是一个变参输出宏,以自定义的格式输出自己想要的息,
在正式环境下,LOG宏就是一个空宏,不做任何事情。
第二题 宏定义输出最大值MAX
1. 要求:
用宏实现一个求最大值的函数,输入两个参数,返回最大值。
2. 代码实现:
#include <stdio.h>
#define P(item) printf("%s = %d\n", #item, item);
#define MAX(a, b) ({ \
__typeof(a) __x = (a); \
__typeof(b) __y = (b); \
((__x) > (__y) ? (__x) : (__y)); \
})
int main() {
int a = 6;
P(MAX(3.5, 3));
printf("the mac value is %.2lf\n", MAX(3.5, 3));
P(5 + MAX(2, 3));
P(MAX(2, MAX(3, 4)));
P(MAX(2, 3 > 4 ? 3 : 4));
P(MAX(a++, 5));
P(a);
return 0;
}
3. 总结归纳:
- __typeof()的使用
因为在求最大值的问题中涉及到了“++”这样的运算,所以在利用上述宏计算的时候,相当于重复调用了,解决的办法就是将参数a++赋值给临时变量,这样在后面比较的时候,就不会有重复调用的情况了。然后说说为什么用__typeof(),而不是直接定义成int型了,因为将临时变量定义成int或者float,那么变量类型就固定了,假如成int,那么上面的P(MAX(3.5, 3))得到的结果就不正确了,我们需要根据max()函数的输入类型,灵活的确定临时变量的数据类型,然后__typeof()就派上用场了。__typeof():声明和已知类型一样类型的新变量。其参数可以是表达式或者类型,如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。 - 关于函数宏返回值的问题
刚开始的代码是这样的#define MAX(a, b) {…} 但是编译不通过,有错误后面通过修改成#define MAX(a, b) ({…}),然后正确运行。
通过使用小括号()把函数体括起来使用宏定义可以得到该函数体最后一条语句运算结果。
但是很想知道为什么?请路过大神指点一下
我的理解是:小括号()是一个运算符,括起来以后,先执行小括号里面的语句,然后将()里面的内容赋值给变量的时候,相当于将()里面的最后一条语句的计算值赋值给变量了。而{}的作用是组成复合语句,比如说if,for,while这些,本来只能后面跟随一条语句,利用复合语句可以达到多条语句变成一条语句的效果。同样{}也是一个代码块,有代码块作用域,变量在代码块声明时,也只能在这块区域内才能被访问。