前言
“goto error”模式因引入不易察觉的资源泄漏而臭名昭著。在已经存在多个展开条件的代码路径中添加新的资源获取约束既繁琐又容易出错。“cleanup”辅助函数可以帮助编译器减轻这种繁琐的工作,并有助于维护后进先出(LIFO)的展开顺序,从而避免意外的资源泄漏。
前备知识
1. 语句表达式:
在 C 语言中,({ ... })并不是标准 C 语言的原生语法,而是GNU C 编译器(GCC)提供的一种扩展语法,通常被称为语句表达式(Statement Expression)。
这种语法允许你将一系列语句(如变量声明、控制流语句if-else,for,switch等)包裹在括号中,并将其作为一个整体表达式来使用。
- 返回值:该语法块中最后一条语句的结果,即为整个
({ ... })表达式的返回值。 - 作为右值:由于它支持返回值,因此可以作为表达式的右值使用,这在普通的
do { ... } while(0)宏中是无法实现的。
>与do { ... } while(0)的对比
在 C 语言宏封装中,除了({ ... }),更常见的是do { ... } while(0)。两者的主要区别在于:
do { ... } while(0):无法返回值,不能作为表达式的右值;但它是标准 C 语法,跨平台兼容性极好。({ ... }):支持返回值,可以参与复杂的表达式计算;但它是 GNU C 扩展,在非 GCC 兼容的编译器(如某些环境下的 MSVC)中可能会报错
>与逗号表达式对比:
内部能承载的代码复杂度不同
- 逗号表达式:只能包含表达式(Expressions)。你不能在里面声明变量,也不能写
if-else、for等控制流语句。 - 语句表达式:可以包含任意 C 语句(Statements)。你可以在里面声明局部变量、写循环、条件判断,甚至调用返回
void的函数。
作用域隔离能力不同
- 逗号表达式:没有自己的作用域。如果在里面使用了变量,它直接操作的是外部的变量。
- 语句表达式:有独立的作用域。在
({ ... })内部声明的变量,出了这个括号就失效了,不会污染外部命名空间。这对于编写安全的宏至关重要。
标准支持度不同
- 逗号表达式:是ISO C 标准语法,任何 C 语言编译器(GCC, MSVC, Clang)都完美支持。
- 语句表达式:是GNU C 扩展语法。如果代码要在不支持 GCC 扩展的编译器(如 Windows 下的 MSVC)上编译,会直接报错。
核心应用场景的差异
- 逗号表达式:主要用于
for循环中同时更新多个变量,或者在宏中执行简单的副作用操作(如打印日志)并返回一个计算值。 - 语句表达式:主要用于底层开发(如 Linux 内核)中的高级宏定义。当宏需要执行多步复杂操作、需要临时变量来避免参数多次求值的副作用,且必须返回一个值时,语句表达式是唯一的标准替代方案(在没有它之前,只能借助外部指针参数或复杂的嵌套宏来实现)。
你可以把逗号表达式理解为“一行代码能写完的简单流水线”,而语句表达式则是“一个可以打包成单个零件的小型车间”。
2. 标记连接运算符##, 字符串化运算符#
3. 可变参数宏:
声明语法类似于可变参数函数:逗号后面三个句点"...",表示一个或多个参数。但常见编译器也允许传递0个参数。[2][3]宏扩展时使用特殊标识符__VA_ARGS__表示所传递的参数的替换。
变量宏扩展是指与具有可变自变量数的宏相关的 C99 和标准 C++ 的两个扩展。 一个扩展是用于将变量参数标识从__VA_ARGS__重命名为用户定义的标识的机制。 当未指定变量自变量时,另一个扩展提供了一种方法来除去变量宏中的悬空逗号。
>使用__VA_ARGS__宏表示可变参数...
#define debug(...) printf(__VA_ARGS__)
>给可以参数取了一个名称args(GNU C扩展语法)
#define debug(format, args...) printf(format, args)
>如果可变参数被忽略或为空,“##”操作将使预处理器(preprocessor)去除掉它前面的那个逗号
#define debug(format, args...) printf(format, ##args)
eg: debug("123") => printf("123"),而不是printf("123",)[这样会引起编译报错]
第一部分 DEFINE_FREE
DEFINE_FREE(...) - 定义free(或者说cleanup)函数:
1. _name: 名称去标识
2. _type: free的数据类型
3. _free: 用户自定义的清理语句
__free(...) - 使用free(或者说cleanup)函数:
1. _name: 上面定义提到的标识的名称
#define DEFINE_FREE(_name, _type, _free) \ static __always_inline void __free_##_name(void *p) { _type _T = *(_type *)p; _free; } #define __free(_name) __cleanup(__free_##_name)return_ptr(p) - 返回p的同时抑制_free()
原理: 通过一个临时变量保存p变量的指针,然后把p设为NULL, 返回临时变量保存的p变量的指针, p为NULL, 就不会执行_free(). 一般地,_free => if(_T) kfree(_T);
#define __get_and_null(p, nullvalue) \ ({ \ auto __ptr = &(p); \ auto __val = *__ptr; \ *__ptr = nullvalue; \ __val; \ }) static __always_inline __must_check const volatile void * __must_check_fn(const volatile void *val) { return val; } #define no_free_ptr(p) \ ((typeof(p)) __must_check_fn((__force const volatile void *)__get_and_null(p, NULL))) #define return_ptr(p) return no_free_ptr(p)第二部分 DEFINE_CLASS
DEFINE_CLASS(name, type, exit, init, init_args...): 用于定义 @type 析构函数destructor和构造函数constructor的辅助函数helper。 @exit 是一个使用 '_T' 的表达式 -- 类似于上面的 FREE。 @init 是 @init_args 中的一个表达式,其结果为 @type EXTEND_CLASS(name, ext, init, init_args...): 将类 @name 拓展为 @name@ext,并使用新的构造函数new constructor CLASS(name, var)(args...): 将变量 @var 声明为命名类(name)的实例 CLASS_INIT(name, var, init_expr): 将变量 @var 声明为命名类(name)的实例,并使用自定义初始化表达式init_expr。代码 DEFINE_CLASS
#define DEFINE_CLASS(_name, _type, _exit, _init, _init_args...) \ typedef _type class_##_name##_t; \ typedef _type lock_##_name##_t; \ static __always_inline void class_##_name##_destructor(_type *p) \ __no_context_analysis \ { _type _T = *p; _exit; } \ static __always_inline _type class_##_name##_constructor(_init_args) \ __no_context_analysis \ { _type t = _init; return t; } #define CLASS(_name, var) \ class_##_name##_t var __cleanup(class_##_name##_destructor) = \ class_##_name##_constructor #define CLASS_INIT(_name, _var, _init_expr) \ class_##_name##_t _var __cleanup(class_##_name##_destructor) = (_init_expr)扩展定义类 - EXTEND_CLASS
#define EXTEND_CLASS_COND(_name, ext, _cond, _init, _init_args...) \ typedef lock_##_name##_t lock_##_name##ext##_t; \ typedef class_##_name##_t class_##_name##ext##_t; \ static __always_inline void class_##_name##ext##_destructor(class_##_name##_t *_T) \ { if (_cond) return; class_##_name##_destructor(_T); } \ static __always_inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ __no_context_analysis \ { class_##_name##_t t = _init; return t; } #define EXTEND_CLASS(_name, ext, _init, _init_args...) \ EXTEND_CLASS_COND(_name, ext, 0, _init, _init_args)include/linux/compiler.h: /* Format: __UNIQUE_ID_<name>_<__COUNTER__> */ //__COUNTER__是一个从0开始的计数器,每次使用这个宏时,它的值会自动加1 #define __UNIQUE_ID(name) \ __PASTE(__UNIQUE_ID_, \ __PASTE(name, \ __PASTE(_, __COUNTER__))) include/linux/compiler_types.h: #define ___PASTE(a, b) a##b #define __PASTE(a, b) ___PASTE(a, b) include/linux/compiler_attributes.h: #define __always_inline inline __attribute__((__always_inline__)) #define __cleanup(func) __attribute__((__cleanup__(func)))