预处理进阶
除了课上教的#define #ifdef等指令以外,编译器还支持许多其他的编译预处理命名。善用它们能够极大的减轻工作量,提高代码效率。
#
#能够将变量名等转换为字符串
#define STR(_arg) #_arg
printf("%s",STR(123))
输出结果应该为1230
#
##能够将两个字符串连接
#define CAT(_a,_b) _a##_b
printf("%s",CAT("123","456"))
输出结果应该为123456
#pragma once
放在头文件开始,保证头文件不被重复引用。 等效于:
#ifndef _THIS_FILE_H_
#define _THIS_FILE_H_
//头文件内容
#endif
#error “message”
在编译期报错”message” 示例:
#if size>=100
#error "size is too big!"
#endif
如果size小于100,编译器会报错
typeof()
获取变量或者函数的类型
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
min(x,y)先创建了与x类型相同的_min1变量和与y类型相同的min_2变量,然后比较了他们地址是否相等,再返回他们中较小的一个数。地址比较的结果是没有意义的,在地址比较的过程中,如果两个指针的类型不同,编译器会警告,由此来保证x和y的类型是相同的。
__FILE__ __LINE__
__FILE__是当前所在文件, __LINE__是当前行数。 给出代码:
void verify_failed(const char *file, uint32_t line) {
RM_UNUSED(file);
RM_UNUSED(line);
while (1) {
};
}
verify_failed(__FILE__, __LINE__);
程序出现问题时调用verify_failed,会跑死在这个函数里,并且会把调用verify_failed的文件和行数传入到参数里,以便定位BUG。
offsetof(type,member)
这个宏会返回一个结构体成员相对于结构体开头的字节偏移量 示例:
#define CONTAINER_OF(ptr, type, member) \
({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); \
})
根据结构体中某个成员的地址,得出整个结构体的地址