_Noreturn 是什么?
_Noreturn 是一个 函数修饰符(function specifier),当它用于声明一个函数时,它向编译器和程序员传达了一个明确的信息:

“这个函数将不会返回到它的调用者。”
这意味着,一旦这个函数被调用,执行流程将永远不会回到函数调用点之后,函数会通过以下两种方式之一终止程序:
- 调用
exit(),abort()等函数来终止整个进程。 - 通过
longjmp()等非局部跳转机制转移控制权。
为什么需要 _Noreturn?
在 _Noreturn 出现之前,C 语言没有标准的方式来告诉编译器一个函数永不返回,这会带来两个主要问题:
a) 编译器警告的误报
考虑以下代码:

#include <stdio.h>
#include <stdlib.h>
void my_exit(void) {
printf("Exiting...\n");
exit(1); // 这个函数不会返回
}
int main(void) {
int x = 10;
my_exit();
printf("x is %d\n", x); // 这行代码理论上永远不会执行
return 0;
}
一个智能的编译器(如 GCC 或 Clang)在编译 main 函数时,可能会发出警告:
warning: 'noreturn' function does return [-Wreturn-type]
这是因为编译器无法确定 my_exit 是一个永不返回的函数,它只能根据 exit 这个函数名进行猜测,但这种猜测并非100%可靠。
使用 _Noreturn,我们可以明确地告诉编译器:
#include <stdio.h>
#include <stdlib.h>
_Noreturn void my_exit(void) {
printf("Exiting...\n");
exit(1);
}
int main(void) {
int x = 10;
my_exit();
printf("x is %d\n", x); // 编译器现在知道这行是死代码
return 0;
}
编译器 不会再警告 my_exit 函数“不返回”,因为它已经被告知了这一点,更重要的是,编译器知道 my_exit 之后的代码(即 printf 和 return)是 死代码,它可以进行更好的优化,并确保这些代码不会被执行。

b) 优化代码
当编译器知道一个函数是 _Noreturn 的,它可以进行更激进的优化,它可以移除 _Noreturn 函数调用之后的任何代码,因为它知道这些代码永远不会到达,这可以生成更高效的二进制文件。
c) 提高代码可读性和文档化
_Noreturn 也是一个强大的 文档工具,当其他开发者(或者未来的你)看到 _Noreturn void fatal_error(const char* msg); 这样的声明时,会立刻明白这个函数一旦调用,程序就会终止,从而避免编写依赖于它返回的无效代码。
如何使用 _Noreturn?
语法很简单,直接放在函数返回类型之前即可:
_Noreturn void my_function(void);
完整示例
下面是一个典型的 _Noreturn 函数的例子,它用于处理致命错误并退出程序。
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h> // C11标准推荐包含这个头文件
// 使用 stdnoreturn.h 中的 noreturn 宏,它通常被定义为 _Noreturn
// 这样写更具可移植性,并且IDE能更好地提供高亮
#include <stdnoreturn.h>
noreturn void fatal_error(const char* message) {
fprintf(stderr, "Fatal Error: %s\n", message);
exit(EXIT_FAILURE);
}
int calculate_something(int a, int b) {
if (b == 0) {
fatal_error("Division by zero!");
// 这行代码之后的所有代码都是死代码
// 编译器会确保它们不会被生成
}
return a / b;
}
int main(void) {
int result = calculate_something(10, 0);
printf("The result is: %d\n", result); // 这行永远不会执行
return 0;
}
编译和运行(使用 GCC):
# 编译,开启所有警告 gcc -Wall -Wextra -std=c11 -o test test.c # 运行 ./test
输出:
Fatal Error: Division by zero!
你会发现,main 函数中 fatal_error 之后的 printf 语句甚至没有被编译进最终的二进制文件中,因为编译器知道它是死代码。
_Noreturn vs __attribute__((noreturn))
在 _Noreturn 成为 C11 标准之前,GCC 和 Clang 等编译器通过一个 编译器特定的属性(attribute) 来实现相同的功能:
// GCC/Clang 扩展 __attribute__((noreturn)) void my_function(void);
关键区别:
| 特性 | _Noreturn (C11) |
__attribute__((noreturn)) (GCC/Clang 扩展) |
|---|---|---|
| 标准性 | C11 标准的一部分,可移植性好。 | 非标准,是 GCC/Clang 的编译器扩展。 |
| 兼容性 | 所有现代 C11 编译器都支持。 | 几乎所有主流 C 编译器都支持,但严格来说不是标准C。 |
| 语法 | 关键字,直接用在函数声明中。 | 属性,需要 __attribute__ 语法。 |
| 头文件 | 声明在 <stdnoreturn.h> 中。 |
无需包含特定头文件。 |
最佳实践:
为了代码的 可移植性和标准化,优先使用 C11 的 _Noreturn,你可以通过包含 <stdnoreturn.h> 并使用 noreturn 宏(它通常被定义为 _Noreturn)来获得最佳兼容性。
注意事项
- 不要滥用:
_Noreturn应该只用于那些确实永不返回的函数,如果错误地将其用于一个有时会返回的函数,会导致未定义行为。 - 函数体必须永不返回:如果你标记了一个函数为
_Noreturn,那么它的函数体必须保证通过某种方式(如exit(),abort(),longjmp())终止程序,如果函数体正常到达结尾(即执行到 ),行为是未定义的,大多数编译器会将其视为一个严重错误并发出警告。 - 不是
inline的替代品:_Noreturn和inline是两个完全不同的概念。inline建议编译器内联展开函数以减少调用开销,而_Noreturn是关于函数执行流程的语义声明。
_Noreturn 是 C11 引入的一个简单但功能强大的关键字,它通过向编译器和程序员明确声明函数永不返回,实现了以下目标:
- 消除误报:避免编译器对正确的
_Noreturn函数发出警告。 - 启用优化:允许编译器移除死代码,生成更高效的程序。
- 提高可读性:作为一种文档,清晰地传达函数的意图,使代码更易于理解和维护。
在编写现代、健壮的 C 代码时,应该积极使用 _Noreturn 来标记那些用于终止程序或进行非局部跳转的函数。
