GCC 和 Clang 中提供的延后(Defer)功能
Defer available in gcc and clang

原始链接: https://gustedt.wordpress.com/2026/02/15/defer-available-in-gcc-and-clang/

C语言的`defer`特性,旨在简化资源管理和错误处理,现已可用。这得益于完成的技术规范(TS 25755)以及在Clang(版本22)和GCC(版本9+,需使用变通方法)中的实现。`defer`确保清理操作(如释放资源或解锁互斥锁)*总是*被执行,即使在提前返回或出现异常时,从而消除潜在的泄漏并简化复杂代码。 作者强烈建议C语言开发者采用`defer`。一个预处理器变通方法允许与旧版编译器兼容,但需要将代码包含在花括号中以用于GCC的嵌套函数回退。重要的是,即使没有优化,此实现也能避免堆栈利用等安全风险。 虽然由于不兼容的“块”扩展,较旧的Clang版本不受支持,但`defer`在编译后独立于外部库工作,使其易于广泛部署。提供的示例展示了它在安全内存分配和互斥锁处理中的用法。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 GCC 和 Clang 中提供的 defer (gustedt.wordpress.com) 11 分,由 r4um 1 小时前发布 | 隐藏 | 过去 | 收藏 | 1 条评论 帮助 kjgkjhfkjf 0 分钟前 [–] 文章有点难懂,但它宣布的内容实际上类似于 golang 的 `defer`(带有额外的花括号)或 C++ 的 RAII 的有限形式(减少了大量的样板代码)。RAII 和 `defer` 都已被证明在实际代码中非常有用。这似乎是 C 语言的一个很好的补充,我希望它能进入标准。 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

About a year ago I posted about defer and that it would be available for everyone using gcc and/or clang soon. So it is probably time for an update.

Two things have happened in the mean time:

  • A technical specification (TS 25755) edited by JeanHeyd Meneide is now complete and moves through ISO’s complicated publication procedures.
  • Both gcc and clang communities have worked on integrating this feature into their C implementations.

I have not yet got my hands on the gcc implementation (but this is also less urgent, see below), but I have been able to use clang’s which is available starting with clang-22.

I think that with this in mind everybody developing in C could and should now seriously consider switching to defer for their cleanup handling:

  • no more resource leakage or blocked mutexes on rarely used code paths,
  • no more spaghetti code just to cover all possibilities for preliminary exits from functions.

I am not sure if the compiler people are also planning back ports of these features, but with some simple work around and slightly reduced grammar for the defer feature this works for me from gcc-9 onward and for clang-22 onward:

#if __has_include(<stddefer.h>)
# include <stddefer.h>
# if defined(__clang__)
#  if __is_identifier(_Defer)
#   error "clang may need the option -fdefer-ts for the _Defer feature"
#  endif
# endif
#elif __GNUC__ > 8
# define defer _Defer
# define _Defer      _Defer_A(__COUNTER__)
# define _Defer_A(N) _Defer_B(N)
# define _Defer_B(N) _Defer_C(_Defer_func_ ## N, _Defer_var_ ## N)
# define _Defer_C(F, V)                                                 \
  auto void F(int*);                                                    \
  __attribute__((__cleanup__(F), __deprecated__, __unused__))           \
     int V;                                                             \
  __attribute__((__always_inline__, __deprecated__, __unused__))        \
    inline auto void F(__attribute__((__unused__)) int*V)
#else
# error "The _Defer feature seems not available"
#endif

So this is already a large panel of compilers. Obviously it depends on your admissible compile platforms whether or not these are sufficient for you. In any case, with these you may compile for a very wide set of installs since defer does not need any specific software infrastructure or library once the code is compiled.

As already discussed many times, the gcc fallback uses the so-called “nested function” feature which is always subject of intense debate and even flame wars. Don’t worry, the implementation as presented here, even when compiled with no optimization at all, does not produce any hidden function in the executable, and in particular there is no “trampoline” or whatever that would put your execution at risk of a stack exploit.

You may also notice that there is no fallback for older clang version. This is because their so-called “blocks” extension cannot easily be used as a drop-in to replace nested function: their semantics to access variables from the surrounding scope are different and not compatible with the defer feature as defined by TS 25755.

So for example if you are scared of using big VLA on the stack, you may use the above code in headers and something like

double* BigArray
  = malloc(sizeof(double[aLot]));
if (!BigArray {
  exit(EXIT_FALURE);
}
defer { 
  free(BigArray); 
}

to have an implementation of a big array with a failure mode for the allocation.

Or if you want to be sure that all your mutexes are unlocked when you leave a critical section, use and idiom as here

{
  if (mtx_lock(&mtx) != thrd_success) {
    exit(EXIT_FAILURE);
  }
  defer {
    mtx_unlock(&mtx);
  }

  ... do something complicated ...

  if (rareCondition) {
    return 42;
  }

  ... do something even more complicated ...
}

Just notice, that you’d always have to use the defer feature with curly braces to ensure that the gcc fallback works smoothly.

联系我们 contact @ memedata.com