预处理进阶


除了课上教的#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)); \
  })

根据结构体中某个成员的地址,得出整个结构体的地址