什么是 __attribute__?
__attribute__ 是一个 GCC(GNU Compiler Collection)和 Clang 编译器特有的关键字,它允许开发者向编译器提供一些额外的信息,从而让编译器在编译过程中进行更高级的优化、生成更高质量的代码,或者进行更严格的错误检查。

它是一个“编译器指令”,用来告诉编译器一些标准 C 语言本身没有规定的事情。
重要提示:
__attribute__是 GCC/Clang 的扩展,不是 C 标准的一部分,使用它写的代码不具备可移植性。- 语法格式为
__attribute__((attribute-list)),通常放在函数声明、变量声明或类型定义的末尾。 - 对于需要兼容其他编译器(如 MSVC)的场景,通常会使用宏来包裹它,
#define __attribute__(x),这样在非 GCC/Clang 编译器下,这个指令就会被忽略。
__attribute__ 的主要用法分类
__attribute__ 的功能非常强大,我们将其分为几个大类来介绍:
A. 函数属性
函数属性用于修饰函数,影响函数的调用方式、栈帧管理、警告信息等。

always_inline
作用:强制编译器将函数内联展开,即使在没有开启优化(-O)的情况下也是如此,这比普通的 inline 关键字更有强制力。
示例:
// 强制内联,即使没有优化选项
static __attribute__((always_inline)) int add(int a, int b) {
return a + b;
}
int main() {
int result = add(1, 2); // 调用会被直接替换为 "int result = 1 + 2;"
return 0;
}
noinline
作用:禁止编译器内联该函数,在某些调试场景下,或者为了精确测量函数执行时间时非常有用。
示例:

// 即使开启了优化,此函数也不会被内联
__attribute__((noinline)) void debug_log() {
printf("Debug point reached.\n");
}
deprecated / deprecated(message)
作用:标记函数或变量为“已弃用”,当其他代码尝试使用或调用它时,编译器会生成一个警告信息。
示例:
// 带有默认警告信息
__attribute__((deprecated)) void old_function() {
printf("This is the old way.\n");
}
// 带有自定义警告信息
__attribute__((deprecated("Please use new_function() instead.")))
void legacy_function() {
printf("This is legacy code.\n");
}
int main() {
old_function(); // 编译警告: warning: 'old_function' is deprecated [-Wdeprecated-declarations]
legacy_function(); // 编译警告: warning: 'legacy_function' is deprecated: Please use new_function() instead. [-Wdeprecated-declarations]
return 0;
}
noreturn
作用:告诉编译器该函数不会返回,编译器可以利用这个信息进行优化,在调用该函数后,编译器可以假定该函数之后的代码是不可达的,从而简化控制流分析。
典型应用:exit(), abort(), assert() 失败时的处理函数。
示例:
#include <stdlib.h>
// 告诉编译器这个函数永远不会返回
__attribute__((noreturn)) void handle_fatal_error() {
fprintf(stderr, "A fatal error occurred!\n");
exit(1);
}
void do_something_risky() {
// ... 假设这里发生了致命错误
handle_fatal_error();
// 编译器知道这里的代码永远不会被执行到
int a = 10; // 可能会被编译器警告为“unreachable code”
}
int main() {
do_something_risky();
return 0;
}
format (arch, index)
作用:用于对 printf, scanf 等风格函数的格式字符串进行静态检查。arch 是格式字符串的类型(如 printf, scanf),index 是可变参数列表中格式化字符串的位置(从1开始)。
示例:
// 自定义一个安全的日志打印函数
// 第一个参数是格式字符串 (printf-like),位置是1
void my_log(const char *format, ...) __attribute__((format(printf, 1, 2)));
void my_log(const char *format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args); // 使用 vprintf 来处理可变参数
va_end(args);
}
int main() {
my_log("User %s logged in.\n", "Alice"); // 正确
my_log("User %s logged in at %d.\n", "Bob"); // 编译警告: format '%d' expects a matching 'int' argument
return 0;
}
const
作用:告诉编译器,该函数除了返回值外,不会修改任何全局或静态变量,也不会修改其传入的指针所指向的内容,这使得编译器可以安全地对函数的调用进行缓存优化。
示例:
// 这个函数是 "pure" 的,它不依赖于任何外部状态,输入相同则输出必然相同
// 注意:这里用 const 更合适,但 pure 也是类似概念
int square(int x) __attribute__((const));
int square(int x) {
return x * x;
}
int main() {
int y = 5;
// 在一个循环中,y 的值不变,编译器可能会只计算一次 square(y)
for (int i = 0; i < 100; i++) {
// ... some code
int val = square(y);
// ...
}
return 0;
}
B. 变量属性
变量属性用于修饰变量,影响其存储位置、对齐方式等。
aligned(n)
作用:确保变量或类型的对齐方式至少为 n 字节,这对于某些硬件架构(如 ARM)或 SIMD 指令集(如 SSE, AVX)是必需的。
示例:
// 确保 cache_line 变量从 64 字节的边界开始分配
// 这常用于避免伪共享
int cache_line __attribute__((aligned(64)));
// 定义一个对齐度为 16 字节的类型
struct vector4 {
float x, y, z, w;
} __attribute__((aligned(16)));
struct vector4 v;
printf("Alignment of v: %zu\n", __alignof__(v)); // 输出应为 16
packed
作用:取消结构体成员之间的字节填充,以节省空间,这通常用于处理硬件寄存器定义或网络协议数据包。
示例:
// 普通结构体,编译器可能会填充对齐
struct normal_struct {
char a;
int b; // 可能在 char a 后面填充了 3 个字节
};
// 使用 packed 属性,编译器不会填充
struct packed_struct {
char a;
int b; // 紧跟在 a 后面,总共只占用 5 字节
} __attribute__((packed));
printf("Size of normal_struct: %zu\n", sizeof(struct normal_struct)); // 通常是 8
printf("Size of packed_struct: %zu\n", sizeof(struct packed_struct)); // 一定是 5
C. 类型属性
类型属性用于修饰类型定义,如 struct 和 enum。
aligned(n) 和 packed
这两个属性也可以用于类型定义,效果与用于变量时类似。
// 定义一个默认对齐度为 32 的类型
typedef int my_int_array[10] __attribute__((aligned(32)));
// 定义一个 packed 的 enum
enum __attribute__((packed)) my_flags {
FLAG_A = 0x01,
FLAG_B = 0x02,
FLAG_C = 0x04
};
D. 构造器/析构器属性
constructor 和 destructor
作用:
constructor: 标记的函数会在main()函数执行之前自动被调用。destructor: 标记的函数会在程序正常退出(exit()或main()返回)时自动被调用。
优先级:可以指定一个优先级数值(0-100,数值越小优先级越高),决定它们的执行顺序。
典型应用:模块初始化、全局资源注册、单例模式实现等。
示例:
#include <stdio.h>
// 优先级为 101 的构造函数,会在 main 前执行
__attribute__((constructor(101)))
void init_module_a() {
printf("Module A initialized.\n");
}
// 优先级为 100 的构造函数,会在 init_module_a 之后执行
__attribute__((constructor(100)))
void init_module_b() {
printf("Module B initialized.\n");
}
// 优先级为 101 的析构函数,会在程序退出时执行
__attribute__((destructor(101)))
void cleanup_module_a() {
printf("Module A cleaned up.\n");
}
// 优先级为 100 的析构函数,会在 cleanup_module_a 之前执行
__attribute__((destructor(100)))
void cleanup_module_b() {
printf("Module B cleaned up.\n");
}
int main() {
printf("Main function is running.\n");
return 0;
}
输出结果:
Module A initialized.
Module B initialized.
Main function is running.
Module B cleaned up.
Module A cleaned up.
总结与最佳实践
| 属性类别 | 属性名 | 作用 | 示例场景 |
|---|---|---|---|
| 函数 | always_inline |
强制内联 | 性能关键的小函数 |
noinline |
禁止内联 | 调试、性能分析 | |
deprecated |
标记为过时 | API 迁移,标记旧接口 | |
noreturn |
函数不返回 | exit(), abort() 等函数 |
|
format |
检查格式字符串 | 自定义 printf/scanf 风格函数 |
|
const/pure |
纯函数/无副作用函数 | 编译器优化,缓存结果 | |
| 变量/类型 | aligned(n) |
指定对齐方式 | SIMD 数据、避免伪共享 |
packed |
紧凑存储,无填充 | 硬件寄存器、网络协议 | |
| 构造/析构 | constructor |
main() 前执行 |
模块初始化、全局对象构造 |
destructor |
程序退出时执行 | 资源清理、全局对象析构 |
最佳实践:
- 谨慎使用:
__attribute__是编译器扩展,会降低代码的可移植性,只在确实需要其强大功能时使用,并做好注释。 - 清晰注释:在代码中清晰地解释你为什么使用某个
__attribute__,以及它带来的影响。// Using noreturn to help the compiler optimize control flow.。 - 版本控制:如果你的项目需要支持多种编译器,使用宏来包裹
__attribute__,确保在非 GCC/Clang 环境下能平滑编译。 - 优先使用标准:C 标准已经提供了类似功能(如
inline),优先使用标准功能,仅在需要额外控制时再使用__attribute__。
__attribute__ 是 C 语言中一个非常强大的工具,熟练掌握它可以帮助你写出更高效、更健壮、更易于维护的代码,尤其是在系统编程和高性能计算领域。
