pragma
#pragma是 C 语言的一种预处理指令,类似于#include。用于告诉编译器进行某种操作。pragma 单词本身的释义为“编译指令”。
最简单的一种 pragma 可以在编译时输出文本提示。
1
2
3
4
5
|
#pragma message(">>> Compiling ...")
int main(void) {
return 0;
}
|
编译上面的 C 文件结果输出如下。
1
2
3
4
5
|
❯ gcc pragma.c
pragma.c:1:9: warning: >>> Compiling ... [-W#pragma-messages]
1 | #pragma message(">>> Compiling ...")
| ^
1 warning generated.
|
_Pragma
由于宏定义之间不能嵌套,当某种 pragma 想要复用时,无法通过#define复用,此时可以通过_Pragma指令,如下所示。
1
2
3
4
5
6
7
8
9
|
#define MSG _Pragma("message(\">>> Compiling ...\")")
MSG
_Pragma("message(\">>> Compiling ...\")")
int main(void) {
return 0;
}
|
编译后输出如下提示信息。其中的 note 消息可以忽略,关注warning: >>> Compiling ... [-W#pragma-messages]段即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
❯ gcc pragma.c
pragma.c:3:1: warning: >>> Compiling ... [-W#pragma-messages]
3 | MSG
| ^
pragma.c:1:13: note: expanded from macro 'MSG'
1 | #define MSG _Pragma("message(\">>> Compiling ...\")")
| ^
<scratch space>:4:2: note: expanded from here
4 | message(">>> Compiling ...")
| ^
pragma.c:5:1: warning: >>> Compiling ... [-W#pragma-messages]
5 | _Pragma("message(\">>> Compiling ...\")")
| ^
<scratch space>:6:2: note: expanded from here
6 | message(">>> Compiling ...")
| ^
2 warnings generated.
|
根据上面的编译输出结果,我们可以知道,如下两种编译指令是近似等价的。但是_Pragma语法本身可以放在#define中复用。
1
2
|
#pragma message(">>> Compiling ...")
_Pragma("message(\">>> Compiling ...\")")
|
pragma 指令功能表
| 指令 |
功能 |
| clang diagnostic push |
保存当前的诊断设置(哪些警告开、哪些关)到内部栈 |
| clang diagnostic ignored “-Wexcessive-regsave” |
告知编译器对接下来遇到的 -Wexcessive-regsave 警告保持沉默 |
| clang diagnostic pop |
从栈中恢复诊断设置,这样后面的代码不受影响 |
-Wexcessive-regsave: 这是一个在某些架构上(尤其是 ARM64)编译器生成代码时可能触发的警告:函数太复杂,编译器需要把大量寄存器保存/恢复到栈上,影响性能。通常在非常大的内联函数或深层嵌套调用时出现。
attribute
__attribute__ 是 GCC(GNU Compiler Collection) 提供的一种语言扩展,用于在 C/C++ 代码中指定函数、变量、类型的特殊属性(attributes),从而告诉编译器对这些实体进行特定的优化、检查或行为控制。
它也被 Clang 等兼容 GCC 的编译器支持。其基本语法如下
1
|
__attribute__((attribute-list))
|
- 可放在声明末尾,或紧跟在标识符之后。
- 多个属性用逗号分隔,例如
__attribute__((aligned(8), packed))
常见属性表
函数属性
| 属性 |
说明 |
noreturn |
函数永不返回(如 exit、abort),帮助消除 unreachable-code 警告 |
format(archetype, string-index, first-to-check) |
声明类 printf/scanf 函数的格式串检查,例如 format(printf, 1, 2) |
nonnull(arg-index, ...) |
声明指针参数不可为 NULL |
const |
纯函数 - 只依赖参数值,不读全局内存,无副作用(比 pure 更强) |
pure |
函数无副作用,返回值仅依赖参数和全局内存(可被 CSE 优化,但调用次数不可消) |
malloc |
返回新分配的内存指针,且该指针不会与任何已有指针别名 |
warn_unused_result |
调用者忽略返回值时发出警告 |
used |
即使未引用也保留函数代码(防止被链接器丢弃) |
unused |
抑制"未使用函数"警告 |
weak |
弱符号 - 可被同名强符号覆盖,用于提供默认实现 |
constructor |
在 main() 之前自动执行(可带优先级参数) |
destructor |
在 main() 返回后或 exit() 时自动执行 |
noinline |
禁止内联此函数 |
always_inline |
强制内联(即使优化级别不支持) |
flatten |
将此函数内的每个调用都视为 always_inline |
hot |
标记为热点路径,优化分支预测和指令布局 |
cold |
标记为冷路径,优化代码体积而非速度 |
no_sanitize("sanitizer-name") |
对此函数禁用指定 sanitizer(如 address、thread) |
interrupt / interrupt_handler |
声明中断处理函数(自动保存/恢复寄存器,使用特殊返回指令) |
sentinel |
参数列表必须以 NULL 结尾(如 execl) |
alloc_size(pos) / alloc_size(pos1, pos2) |
标明第几个参数控制分配大小,辅助优化和安全检查 |
returns_nonnull |
函数保证返回非 NULL 指针 |
leaf |
函数不会回调当前编译单元内的任何函数 |
nothrow |
函数保证不抛出 C++ 异常 |
target("feature") |
为特定 CPU 特性编译此函数(如 target("avx2")) |
类型属性
| 属性 |
说明 |
aligned(N) |
指定类型或变量的对齐字节数(N 为 2 的幂) |
packed |
紧凑布局 - 移除成员间的填充字节 |
deprecated / deprecated("message") |
标记为已弃用,使用时产生警告 |
unavailable |
标记为不可用,使用时产生错误 |
visibility("type") |
符号可见性:default、hidden、internal、protected |
mode(TI/QI/HI/SI/DI) |
指定整数类型的宽度(如 __attribute__((mode(DI)))) |
may_alias |
声明此类型可能与其他类型别名,抑制严格别名优化 |
transparent_union |
透明联合体 - 传参时各成员类型可隐含转换 |
designated_init |
只能通过指定初始化器初始化(可用于 union) |
objc_root_class |
标记 Objective-C 根类 |
变量属性
| 属性 |
说明 |
section("name") |
将变量放入指定段(如 .init_array、.bss.my_section) |
used |
即使未引用也保留变量 |
unused |
抑制"未使用变量"警告 |
aligned(N) |
指定对齐(同类型属性,直接作用于变量) |
packed |
去除结构体成员对齐填充(作用于变量声明时) |
cleanup(function) |
变量离开作用域时自动调用给定函数(类似 C++ RAII) |
weak |
弱符号 - 可被同名强符号覆盖 |
tls_model("model") |
指定 TLS 模型:global-dynamic、local-dynamic、initial-exec、local-exec |
noinit |
变量不初始化为零(用于保持复位后的数据) |
用法示例
1
2
3
4
5
6
7
8
9
|
typedef struct {
acpi_sdt_header_t header;
uint32_t PointerToOtherSDT;
} __attribute__((packed)) rsdt_t;
__attribute__((noinline)) void compiler_barrier(void)
{ __asm__ volatile("" ::: "memory"); }
volatile float __x __attribute__((unused));
|
aligned(N) 做了两件事:
| 效果 |
说明 |
| 起始地址 |
变量/结构体地址满足 addr % N == 0 |
| sizeof 补整 |
末尾补 padding 使 sizeof % N == 0 |
volatile 关键字
volatile 是 C 语言的一个类型修饰符,用于告诉编译器:该变量的值可能在程序控制流之外被意外修改,因此编译器不能对该变量的访问进行某些优化(如缓存到寄存器、消除冗余读取等)。
__asm__和volatile可以一起使用,如下。
1
|
__asm__ volatile("inw %1, %0" : "=a"(ret) : "dN"(port));
|
编译器必须:
- 保留这段汇编,即使输出没用
- 每次都生成,不能合并
- 保持原位,不和其它 volatile asm 重排