第 5 章 编译器特定的编译指示
总结了 ARM® 编译器特定的编译指示,它们是 C 和 C++ 标准的扩展。
它包含以下部分:
5.1 #pragma clang system_header.
5.2 #pragma clang diagnostic.
5.3 #pragma once.
5.4 #pragma pack(...).
5.5 #pragma unroll[(n)], #pragma unroll_completely.
5.6 #pragma weak symbol, #pragma weak symbol1 = symbol2.
5.1 #pragma clang system_header
使当前文件中的后续声明被标记为好像它们出现在系统头文件中一样。
此 pragma 禁止文件生成的警告消息,从它声明之后的那一点开始。
5.2 #pragma clang diagnostic
Pragma 允许您在代码中抑制、启用或更改特定诊断消息的严重性。
例如,您可以在编译一个特定函数时抑制特定的诊断消息。
注意:
或者,您可以使用命令行选项 -Wname 来抑制或更改消息的严重性,但更改适用于整个编译。
#pragma clang 诊断被忽略
#pragma clang diagnostic ignored "-Wname"
此 pragma 禁用由名称指定的诊断消息。
#pragma clang 诊断警告
#pragma clang diagnostic warning "-Wname"
此编译指示将名称指定的诊断消息设置为警告严重性。
#pragma clang 诊断错误
#pragma clang diagnostic error "-Wname"
此 pragma 将 name 指定的诊断消息设置为错误严重性。
#pragma clang 诊断致命
#pragma clang diagnostic fatal "-Wname"
此 pragma 将 name 指定的诊断消息设置为致命错误严重性。致命错误导致编译失败而不处理文件的其余部分。
#pragma clang 诊断推送,#pragma clang 诊断弹出
pragma clang diagnostic push #pragma clang diagnostic pop
#pragma clang diagnostic push 保存当前的 pragma 诊断状态,以便以后可以恢复。
#pragma clang diagnostic pop 恢复之前使用 #pragma clang diagnostic push 保存的诊断状态。
使用 pragma 控制诊断的示例
以下示例显示了四个相同的函数,foo1()、foo2()、foo3() 和 foo4()。所有这些函数通常都会引发诊断消息警告:源代码行上的多字符字符常量 [-Wmultichar] char c = (char) 'ab';
使用 pragma,您可以抑制或更改各个功能的这些诊断消息的严重性。
对于 foo1(),当前的 pragma 诊断状态被推送到堆栈,#pragma clang diagnostic ignored 会抑制消息。然后通过#pragma clang diagnostic pop 重新启用诊断消息。
对于 foo2(),诊断消息不会被抑制,因为原始 pragma 诊断状态已恢复。
对于 foo3(),该消息最初被前面的 #pragma clang diagnostic ignored "-Wmultichar" 抑制,但是,该消息随后作为错误重新启用,使用 #pragma clang diagnostic error "-Wmultichar"。因此编译器会在 foo3() 中报告错误。
对于 foo4(),pragma 诊断状态恢复到之前 #pragma clang diagnostic push 保存的状态。因此,此状态包括#pragma clang diagnostic ignored “-Wmultichar”,因此编译器不会在 foo4() 中报告警告。
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmultichar" void foo1( void ) { /* Here we do not expect a diagnostic message, because it is suppressed by #pragma clang diagnostic ignored "-Wmultichar". */ char c = (char) 'ab'; } #pragma clang diagnostic pop void foo2( void ) { /* Here we expect a warning, because the suppression was inside push and then the diagnostic message was restored by pop. */ char c = (char) 'ab'; } #pragma clang diagnostic ignored "-Wmultichar" #pragma clang diagnostic push void foo3( void ) { #pragma clang diagnostic error "-Wmultichar" /* Here, the diagnostic message is elevated to error severity. */ char c = (char) 'ab'; } #pragma clang diagnostic pop void foo4( void ) { /* Here, there is no diagnostic message because the restored diagnostic state only includes the #pragma clang diagnostic ignored "-Wmultichar". It does not include the #pragma clang diagnostic error "-Wmultichar" that is within the push and pop pragmas. */ char c = (char) 'ab'; }
诊断消息使用它们生成时存在的编译指示状态。如果您使用编译指示来控制代码中的诊断消息,则必须知道在编译过程中何时生成该诊断消息。
如果仅在处理完所有函数后才生成针对函数 functionA 的诊断消息,则编译器使用在处理所有函数后出现的 pragma 诊断状态来控制此诊断消息。此诊断状态可能与函数 A 定义之前或之内的诊断状态不同。
5.3 #pragma once
允许编译器跳过该头文件的后续包含。
#pragma once 被接受是为了与其他编译器兼容,并使您能够使用其他形式的标头保护编码。但是,ARM 建议使用#ifndef 和#define 编码,因为这样更便于移植。
示例
以下示例显示了在文件主体周围放置#ifndef 保护,在#ifndef 之后使用保护变量的#define。
#ifndef FILE_H
#define FILE_H
#pragma once // optional
... body of the header file ...
#endif
在此示例中,#pragma once 被标记为可选。这是因为编译器识别 #ifndef 标头保护编码并跳过后续包含,即使不存在 #pragma once 也是如此。
5.4 #pragma pack(...)
这个pragma 将结构的成员对齐到n 的最小值和它们的自然对齐。使用非对齐访问读取和写入打包对象。您可以选择将对齐设置推送和恢复到内部堆栈。
注意:
此编译指示是 ARM® 编译器支持的 GNU 编译器扩展。
语法
#pragma pack([
])n
#pragma pack(push[,
])n
#pragma pack(pop)
其中:
n
是以字节为单位的对齐,有效的对齐值为 1、2、4 和 8。如果省略,则将对齐设置为编译开始时有效的对齐。
push[,n]
将当前对齐设置推送到内部堆栈,然后可选择设置新对齐。
pop
将对齐设置恢复为保存在内部堆栈顶部的对齐设置,然后删除该堆栈条目。
注意:
#pragma pack([n]) 不会影响此内部堆栈。因此,可以有#pragma pack(push) 后跟多个#pragma pack(n) 实例,然后由单个#pragma pack(pop) 完成。
默认值
默认值是编译开始时生效的对齐方式。
示例
此示例显示 pack(2) 如何将整数变量 b 与 2 字节边界对齐。
typedef struct { char a; int b; } S; #pragma pack(2) typedef struct { char a; int b; } SP; S var = { 0x11, 0x44444444 }; SP pvar = { 0x11, 0x44444444 };
S 的布局为:
图 5-1 非打包结构 S
SP 的布局为:
图 5-2 打包结构 SP
注意:
在此布局中,x 表示一个字节的填充。
SP 是一个 6 字节的结构。 b 之后没有填充。
5.5 #pragma unroll[(n)], #pragma unroll_completely
指示编译器将循环展开 n 次迭代。
语法
#pragma unroll
#pragma unroll_completely
#pragma unroll
n
#pragma unroll(
)n
其中:
n
是一个可选值,指示要展开的迭代次数。
默认
如果您没有为 n 指定值,编译器会尝试完全展开循环。编译器只能完全展开循环,它可以确定迭代次数。
如果在编译时不知道迭代次数,#pragma unroll_completely 将不会展开循环。
用法
此 pragma 仅对优化级别 -O2 及更高级别有效。
使用 -O3 进行编译时,编译器会自动展开循环,以便这样做。此 pragma 可用于要求编译器展开尚未自动展开的循环。
#pragma unroll[(n)] 可以在 for 循环、while 循环或 do ... while 循环之前立即使用。
限制
此 pragma 是对编译器的请求,以展开尚未自动展开的循环。它不保证循环展开。
5.6 #pragma weak symbol, #pragma weak symbol1 = symbol2
这个pragma 是一种语言扩展,用于将符号标记为弱或定义符号的弱别名。
示例
在以下示例中,weak_fn 被声明为 __weak_fn 的弱别名:
extern void weak_fn(int a); #pragma weak weak_fn = __weak_fn void __weak_fn(int a) { ... }