第 2 章 特定于编译器的关键字和运算符
总结了特定于编译器的关键字和运算符,它们是 C 和 C++ 标准的扩展。
它包含以下部分:
2.1 编译器特定的关键字和运算符.
2.2 __alignof__.
2.3 __asm.
2.4 __declspec attributes.
2.5 __declspec(noinline).
2.6 __declspec(noreturn).
2.7 __declspec(nothrow).
2.8 __inline.
2.1 编译器特定的关键字和运算符.
ARM® 编译器 armclang 提供了对 C 和 C++ 标准进行扩展的关键字。
不记录没有特定于 ARM 编译器的行为或限制的标准 C 和标准 C++ 关键字。
ARM 编译器支持的关键字扩展:
__alignof__
__asm
__declspec
__inline
2.2 __alignof__
__alignof__ 关键字使您能够查询类型或变量的对齐方式。
注意:
此关键字是 ARM® 编译器支持的 GNU 编译器扩展。
语法
__alignof__(type)
__alignof__(expr)
其中:
type 是一个类型
expr 是一个左值。
返回值
__alignof__(type) 返回类型的对齐要求,如果没有对齐要求,则返回 1。
__alignof__(expr) 返回左值 expr 类型的对齐要求,如果没有对齐要求,则返回 1。
示例
下面的示例显示了各种数据类型的对齐要求,首先直接来自数据类型,然后来自相应数据类型的左值:
#include <stdio.h> int main(void) { int var_i; char var_c; double var_d; float var_f; long var_l; long long var_ll; printf("Alignment requirement from data type:\n"); printf(" int : %d\n", __alignof__(int)); printf(" char : %d\n", __alignof__(char)); printf(" double : %d\n", __alignof__(double)); printf(" float : %d\n", __alignof__(float)); printf(" long : %d\n", __alignof__(long)); printf(" long long : %d\n", __alignof__(long long)); printf("\n"); printf("Alignment requirement from data type of lvalue:\n"); printf(" int : %d\n", __alignof__(var_i)); printf(" char : %d\n", __alignof__(var_c)); printf(" double : %d\n", __alignof__(var_d)); printf(" float : %d\n", __alignof__(var_f)); printf(" long : %d\n", __alignof__(var_l)); printf(" long long : %d\n", __alignof__(var_ll)); }
使用以下命令编译会产生以下输出:
armclang --target=arm-arm-none-eabi -march=armv8-a alignof_test.c -o alignof.axf
Alignment requirement from data type: int : 4 char : 1 double : 8 float : 4 long : 4 long long : 8 Alignment requirement from data type of lvalue: int : 4 char : 1 double : 8 float : 4 long : 4 long long : 8
2.3 __asm
这个关键字将信息传递给 armclang 汇编器。
此关键字的确切作用取决于其用法。
用法
内联汇编
__asm 关键字可以将内联 GCC 语法汇编代码合并到一个函数中。例如:
#include <stdio.h> int add(int i, int j) { int res = 0; __asm ( "ADD %[result], %[input_i], %[input_j]" : [result] "=r" (res) : [input_i] "r" (i), [input_j] "r" (j) ); return res; } int main(void) { int a = 1; int b = 2; int c = 0; c = add(a,b); printf("Result of %d + %d = %d\n", a, b, c); }
__asm 内联汇编语句的一般形式是:
__asm(
code
[: output_operand_list
[: input_operand_list
[: clobbered_register_list
]]]);
code 是汇编代码。在我们的示例中,这是“ADD %[result], %[input_i], %[input_j]”。
output_operand_list 是可选的输出操作数列表,以逗号分隔。每个操作数由方括号中的符号名称、约束字符串和括号中的 C 表达式组成。在我们的示例中,有一个输出操作数:[result] "=r" (res)。
input_operand_list 是输入操作数的可选列表,以逗号分隔。输入操作数使用与输出操作数相同的语法。在我们的示例中,有两个输入操作数:[input_i] "r" (i),[input_j] "r" (j)。
clobbered_register_list 是一个可选的被破坏寄存器列表。在我们的示例中,这被省略了。
嵌入式汇编
对于嵌入式汇编,您不能在函数声明中使用 __asm 关键字。在函数声明中使用 __attribute__((naked)) 函数属性。有关详细信息,请参阅 __attribute__((naked))。例如:
__attribute__((naked)) void foo (int i);
具有 __attribute__((naked)) 函数属性的裸函数仅支持基本格式的汇编指令:
__asm(
code
);
汇编标签
__asm 关键字可以为 C 符号指定程序集标签。例如:
int count __asm__("count_v1"); // export count_v1, not count
2.4 __declspec 属性
__declspec 关键字使您能够指定对象和函数的特殊属性。
__declspec 关键字必须作为声明规范的前缀。例如:
__declspec(noreturn) void overflow(void);
可用的 __declspec 属性如下:
__declspec(noinline)
__declspec(noreturn)
__declspec(nothrow)
__declspec 属性是存储类修饰符。它们不影响函数或变量的类型。
2.5 __declspec(noinline)
__declspec(noinline) 属性在函数调用点抑制函数的内联。
__declspec(noinline) 也可以应用于常量数据,以防止编译器将值用于优化目的,而不影响其在对象中的位置。这是一个可用于可修补常量的功能,即后来修补为不同值的数据。在需要常量值的上下文中尝试使用此类常量是错误的。例如,数组维度。
注意:
此 __declspec 属性具有等效的函数属性 __attribute__((noinline))。
示例
/* Prevent y being used for optimization */ __declspec(noinline) const int y = 5; /* Suppress inlining of foo() wherever foo() is called */ __declspec(noinline) int foo(void);
2.6 __declspec(noreturn)
__declspec(noreturn) 属性断言函数永远不会返回。
注意:
此 __declspec 属性具有等效的函数属性 __attribute__((noreturn))。
用法
使用此属性可降低调用从不返回的函数的成本,例如 exit()。如果 noreturn 函数返回其调用者,则行为未定义。
限制
调用 noreturn 函数时不保留返回地址。这限制了调试器显示调用堆栈的能力。
示例
__declspec(noreturn) void overflow(void); // never return on overflow int negate(int x) { if (x == 0x80000000) overflow(); return -x; }
2.7 __declspec(nothrow)
__declspec(nothrow) 属性断言对函数的调用永远不会导致 C++ 异常从被调用者传播到调用者。
ARM 库头文件会自动将此限定符添加到 C 函数的声明中,根据 ISO C 标准,这些函数永远不会引发异常。但是,为 C 库函数生成的展开表存在一些限制,这些函数可能会在 C++ 上下文中引发异常,例如 bsearch 和 qsort。
注意:
此 __declspec 属性具有等效的函数属性 __attribute__((nothrow))。
用法
如果编译器知道一个函数永远不会抛出异常,它可能会为该函数的调用者生成更小的异常处理表。
限制
如果对函数的调用导致 C++ 异常从被调用者传播到调用者,则行为未定义。
当未启用异常进行编译时,此修饰符将被忽略。
示例
struct S { ~S(); }; __declspec(nothrow) extern void f(void); void g(void) { S s; f(); }
2.8 __inline
__inline 关键字向编译器建议它内联编译 C 或 C++ 函数,如果这样做是明智的。
__inline 可以在 C90 代码中使用,并作为 C99 内联关键字的替代品。
armclang 支持 __inline 和 __inline__。
示例
static __inline int f(int x){ return x*5+1; } int g(int x, int y){ return f(x) + f(y); }