gnu(gcc)属性语法

目录

1. GCC 相关属性

1.1. GCC 相关属性指定方式

1.2. gcc 属性用法说明

1.2.1  变量属性中的属性用法

1.2.2  函数属性中的属性用法

1.2.3  typedef属性用法

1.2.4  struct、union或C++ 类中属性用法

1.2.5  枚举中属性用法

1.2.6  语句中属性用法

1.2.7  多属性用法

2. GNU属性

2.1. GNU  属性分类

2.1.1   标签属性

2.1.2   枚举器属性

2.1.3   语句属性

2.1.4   类型属性

2.2  GNU属性注意事项

2.3  函数属性(与通用属性有重叠)

2.3.1  noreturn

2.3.2  noinline

2.3.3  always_inline

2.3.4  pure

2.3.5  const

2.3.6  nothrow

2.3.7  format (archetype, string-index, first-to-check)

2.3.8  format_arg (string-index)

2.3.9  nonnull(arg-index, …)

2.3.10  no_instrument_function

2.3.11  section(“section-name”)

2.3.12  constructor,destructor

2.3.13  unused

2.3.14  used

2.3.15  depecated(弃用标识)

2.3.16  warn_unused_result

2.3.15  weak

2.3.16  malloc

2.3.17  alias(“target”)

2.3.18  visibility ("visibility_type")

2.3.19  regparm(number)

2.3.20  stdcall

2.3.21  fastcall

2.3.22  cdecl

2.3.23  longcall/shortcall

2.3.24  long_call/short_call

2.3.25  function_vector

2.3.26  interrupt

2.3.27  interrupt_handler

2.3.28  sp_switch

2.3.29  trap_exit

2.3.30  eightbit_data

2.3.31  tiny_data

2.3.32  saveall

2.3.33  signal

2.3.34  naked

2.3.35  model(model-name)

2.3.36  far

2.3.37  near

2.3.38  dllimport

2.3.39  dllexport

2.4  C++ 专有变量、函数、和类型属性

2.4.1  abi_tag(“tag”,…)

2.4.2  init_priority (priority)

2.4.3  no_dangling(无悬空引用)

2.4.4  warn_unused

2.4.5  cold,hot

2.4.6  trivial_abi

2.4.7  no_specializations(禁特化)

2.5  通用属性(可能适用于多种类型)

2.5.1   access(access-mode, ref-index) , access(access-mode, ref-index,size-index)  

2.5.2  alias(“target”)

2.5.3  aligned, aligned(alignment)

2.5.4   alloc_align(position)  

2.5.5   alloc_size(position), alloc_size(position-1, position-2)  

2.5.6   always_inline

2.5.7   artificial

2.5.8   assume

2.5.9   assume_aligned(alignment), assume_aligned(alignment,offset)

2.5.10   btf_decl_tag

2.5.11   btf_type_tag(argument)

2.5.12   cleanup(cleanup_function)

2.5.13   cold,hot

2.5.14   common,nocommon

2.5.15   const

2.5.16   constructor,desctructor, constructor(priority), desctructor(priority)

2.5.17   copy,copy(name)

2.5.18   counted_by(count)

2.5.19   deprecated, deprecated (msg)

2.5.20   designated_init

2.5.21   error(“message”),warning(“message”)

2.5.22   expected_throw

2.5.23   externally_visible

2.5.24   fallthrough

2.5.25  fd_arg(N ), fd_arg_read(N ), fd_arg_write(N )

2.5.26  flag_enum

2.5.27  flatten

2.5.28  format(archetype, string-index, first-to-check)

2.5.29  format_arg(string-index)

2.5.30  gnu_inline

2.5.31  hard_bool, hard_bool(false_value), hard_bool(false_value,true_value)

2.5.32  ifunc(“resolver”)

2.5.33  interrupt, interrupt_handler

2.5.34  leaf(叶函数属性)

2.5.35  malloc, malloc(deallocator), malloc(deallocator,pre-index)

2.5.36  may_alias

2.5.37  mode(mode)

2.5.38  musttail

2.5.39  naked

2.5.40  no_icf

2.5.41  no_instrument_function(无插桩代码函数)

2.5.42  no_profile_instrument_function(无性能插桩代码函数)

2.5.43  no_reorder

2.5.44  no_sanitize ("sanitize_option")

2.5.45  no_sanitize_address, no_address_safety_analysis

2.5.46  no_sanitize_coverage

2.5.47  no_sanitize_thread

2.5.48  no_sanitize_undefined

2.5.49  no_split_stack

2.5.50  no_stack_limit

2.5.51  no_stack_protector

2.5.52  no_clone

2.5.53  noinit

2.5.54  noinline

2.5.55  noipa

2.5.56  nonnull, nonnull(arg-index,…)

2.5.57  nonnull_if_nonzero(arg-index, arg2-index), nonnull_if_nonzero(arg-index, arg2-index, arg3-index)

2.5.58  nonstring

2.5.59  noplt

2.5.60  noreturn

2.5.61  nothrow

2.5.62  null_terminated_string_arg, null_terminated_string_arg(N)

2.5.63  objc_nullability(nullability kind)( 仅 Objective-C 和 Objective-C++ )

2.5.64  objc_root_class( 仅 Objective-C 和 Objective-C++ )

2.5.65  optimize (level, …), optimize (string, …)

2.5.66  packed

2.5.67  patchable_function_entry

2.5.68  persistent

2.5.69  pure

2.5.70  reproducible

2.5.71  retain

2.5.72  returns_nonnull

2.5.73  returns_twice

2.5.74  scalar_storage_order("endianness")

2.5.75  section("section-name")

2.5.76  sentinel, sentinel(position)

2.5.77  simd, simd(“mask”)

2.5.78  stack_protect

2.5.79  strict_flex_array(level)

2.5.80  strub

2.5.81  symver ("name2@nodename")

2.5.82  tainted_args

2.5.83  target(string, …)

2.5.84  target_version (option)

2.5.85  target_clones(options)

2.5.86  tls_model ("tls_model")

2.5.87  transparent_union

2.5.88  unavailable , unavailable(msg)

2.5.89  uninitialized

2.5.90  unsequenced

2.5.91  unused

2.5.92  used

2.5.93  vector_size(bytes)

2.5.94  visibility("visibility_type")

2.5.95  default

2.5.96  hidden

2.5.97  internal

2.5.98  protected

2.5.99  warn_if_not_aligned(alignment)

2.5.100  warn_unused_result

2.5.101  weak

2.5.102  weakref,weakref ("target")  

2.5.103  zero_call_used_regs("choice")  


1. GCC 相关属性

         GCC 相关属性基本就是GNU属性,GCC最新版本基本都支持GNU属性。

1.1. GCC 相关属性指定方式

         GCC 提供了两种指定属性(attribute)的方式一种是使用双方括号([[...]])的标准 C 和 C++ 语法另一种是较早出现的 GNU 扩展语法,即使用 __attribute__ 关键字。后者早于标准语法的引入,且至今仍广泛应用于旧代码中。本节将针对 C 语言,详细介绍 GNU 扩展的 __attribute__ 语法以及属性说明符所作用的语言构造。对于 C++ 和 Objective-C,某些细节可能会有所不同。受限于属性语法的局限性,此处描述的某些形式在某些情况下可能无法被成功解析。

    C++ 中的属性语义存在一些问题。例如,尽管属性可能会影响代码生成,但它们并不参与名称修饰(mangling),因此当带有属性的类型与模板或重载结合使用时,可能会引发问题。同样,typeid 也无法区分具有不同属性的类型。未来,C++ 对属性的支持可能会受到限制,仅限于应用于声明(declarations)的属性,而不支持应用于嵌套声明符(nested declarators)的属性。

属性(attributes)提供了一种机制,用于声明函数、变量、类型及语句的附加特性。例如,属性可用于控制对象在特定内存段中的布局,或指定有助于编译器生成更优代码或诊断信息的特性(如声明某函数永不返回)。GCC 支持多种此类属性,本节将对其进行详细说明。

GCC 提供了两种指定属性的不同方式:一种是使用 __attribute__ ((...)) 标注的传统 GNU 语法;另一种是较新的标准 C 和 C++ 语法,即使用 [[...]] 并在属性名称前加上 gnu:: 命名空间前缀。这两种语法在所有受支持的 C 和 C++ 语言中均被接受。不过,如果使用了 -Wpedantic 选项(参见“请求或抑制警告的选项”),GCC 会针对在 C23 之前的 C 语言或 C++11 之前的 C++ 语言中使用标准属性语法的行为发出警告,因为这些旧版本标准并不包含该特性。你可以通过在属性名称前加上 __extension__ 关键字来抑制这些警告;参见“替代关键字”。

传统语法在“GNU 属性语法”(GNU Attribute Syntax)中有详细描述。关于标准属性放置位置的具体规则,请参阅 C 或 C++ 标准。

GCC 也接受 __attribute 关键字作为 __attribute__ 的同义词。

每个 GNU 特有的属性都有一个前后各带两条下划线的别名;例如,weak 属性的别名是 __weak__。当这些属性包含于将属性原名重定义为预处理器宏的文件中时,这些别名在库头文件中就非常有用。同样,对于每个受支持的标准属性或带作用域的属性(例如 [[gnu::weak]]),也存在相应的变体写法,如 [[__gnu__::__weak__]]、[[gnu::__weak__]] 或 [[__gnu__::weak]]。唯一的例外是 Clang 带作用域属性的命名空间,其别名写法为 _Clang 而非 __clang__,这是为了保持与该编译器的兼容性。

1.2. gcc 属性用法说明

以下是一些展示属性(attributes)用法的示例。

1.2.1  变量属性中的属性用法

无论采用哪种语法,变量声明中的属性既可以置于声明的开头(此时它们适用于该声明中的所有声明符),也可以置于具体声明符之后(仅适用于该变量)

[[gnu::aligned (16)]] int v1 = 0;

int v2 __attribute__ ((aligned (16))) = 0;

int v3 [[gnu::aligned (16)]] = 0;

int v4 __attribute__ ((aligned (16))) = 0;

1.2.2  函数属性中的属性用法

     在两种语法中,函数声明或定义的属性都可以出现在最前面

[[gnu::section ("bar")]] extern void f1 (void);

__attribute__ ((section ("bar"))) extern void f2 (void);

不过,在使用 GNU 语法时,要注意将属性置于参数列表之后这种常见写法。在标准语法中,位于该位置的属性会被视为修饰函数类型,而非修饰函数本身或其名称。

extern void f3 (void) [[gnu::section ("bar")]];  /* Error!  */

extern void f4 (void) __attribute__ ((section ("bar")));

f3 的声明引发了错误,因为 section 属性不能应用于类型。若要将其正确应用于函数,既可以将该属性置于声明的开头,也可以将其置于函数名之后,如下所示:

extern void f3 [[gnu::section ("bar")]] (void);

    某些函数属性接受一个或多个参数,这些参数通过函数参数列表中的位置来引用函数的参数;此类属性参数称为位置参数。除非另有说明,否则用于指定指针类型参数属性的位置参数,同样适用于指定非静态成员函数中隐式 C++ this 参数的相应属性,以及指向指针类型的引用参数的相应属性。对于普通函数,位置 1 指的是参数列表中的第一个参数;而在 C++ 非静态成员函数中,位置 1 指的是隐式的 this 指针。

    同样的限制和效果也适用于与普通函数或 C++ 成员函数配合使用的函数属性。

1.2.3  typedef属性用法

typedef 遵循与其他声明相同的属性放置规则。这些声明均有效,且含义相似。

[[gnu::unavailable]] typedef int *t1;

typedef int *t2 [[gnu::unavailable]];

__attribute__ ((unavailable)) typedef int *t3;

typedef int *t4 __attribute__ ((unavailable));

1.2.4  struct、union或C++ 类中属性用法

struct、union或 C++ 类成员在属性放置方面遵循与其他声明类似的规则,但需注意,许多旨在用于顶层声明的属性(例如指定符号链接器属性的属性)在此处并不适用

    将适用于整个 struct、union、class 或 enum 类型的属性放置在关键字与类型名称之间

struct [[gnu::aligned (8)]] s1 { short f[3]; };

struct __attribute__ ((aligned (8))) s2 { short f[3]; };

1.2.5  枚举中属性用法

仅有少数几个属性适用于枚举成员;这些属性直接置于枚举成员名称之后。

1.2.6  语句中属性用法

    语句属性置于语句或标签之前。置于空语句之前的属性属于一种特殊情况,即属性声明,其语义取决于具体的属性。在其他情况下,语句属性通常作用于其所修饰的语句或标签。

这两个函数是等价的:

int foo1 (int x, int y)

{

  [[gnu::assume (x == 42)]];

  return x + y;

}

int foo2 (int x, int y)

{

  __attribute__((assume(x == 42)));

  return x + y;

}

1.2.7  多属性用法

如果希望在同一个实体上指定多个属性,这两种语法都允许在同一个属性说明符中列出这些属性,并用逗号分隔

[[gnu::section ("bar"), gnu::weak]]

void g1 (void) { f1 (); }

__attribute__ ((section ("bar"), weak))

void g2 (void) { f2 (); }

    或者,你也可以使用多个属性说明符:

[[gnu::section ("bar")]] [[gnu::weak]]

void g3 (void) { f1 (); }

__attribute__ ((section ("bar"))) __attribute__ ((weak))

void g4 (void) { f2 (); }

2. GNU属性

    一个GNU属性修饰符形式为 __attribute__ ((attribute-list))attribute-list是一个由逗号分隔的属性序列(该序列可以为空),其中每个属性均为下列各项之一:

●  空:空属性会被忽略。

●  一个属性名:其中可能是诸如 unused 之类的标识符,也可能是诸如 const 之类的保留字)。

●   属性名称,后跟该属性的参数列表(括在括号内)。这些参数采用以下形式之一:

(1) 一个标识符。例如,mode 属性就采用这种形式。

(2) 一个标识符,后跟一个逗号以及一个非空的、以逗号分隔的表达式列表。例如,format属性就采用这种形式。

(3) 一个由逗号分隔的表达式列表(该列表可以为空)。例如,format_arg 属性采用这种形式,其中列表包含单个整数常量表达式;而 alias 属性也采用这种形式,其中列表包含单个字符串常量。

    属性说明符列表是一个由一个或多个属性说明符组成的序列,这些说明符之间不以任何其他记号分隔。

    你可以选择在属性名称的前后加上“__”来指定该属性。这样,你就可以在头文件中使用这些属性,而无需担心与同名宏发生冲突(注:如果同名宏也加了前后双下划线呢?)。例如,你可以使用属性名 __noreturn__ 来代替 noreturn。

    GCC 也接受 __attribute 关键字作为 __attribute__ 的同义词。

    你可以在声明中指定多个属性,方法是在双括号内用逗号分隔它们,或者在一个属性声明后紧跟另一个属性声明

    有些人反对 __attribute__ 特性,建议改用 ISO C 的 #pragma 指令。在设计 __attribute__ 时,不采用这种做法有两个原因:

(1) 无法通过宏生成 #pragma 命令。

(2) 同样的 #pragma 命令在其他编译器中可能代表什么含义,我们无从知晓。

这两个原因几乎适用于所有可能使用 #pragma 的应用场景。基本上,使用 #pragma 都是错误的。

    ISO C99 标准包含了 _Pragma,它允许从宏生成编译指示。此外,现在还使用了一个 #pragma GCC 命名空间来存放 GCC 特有的编译指示。然而,人们发现使用 __attribute__ 来实现属性与其对应声明的自然关联更为便捷,而 #pragma GCC 则适用于那些并非语法固有组成部分的结构。

2.1. GNU  属性分类

2.1.1   标签属性

    在 GNU C 中,属性说明符列表可以出现在标签(case 或 default 标签除外)后的冒号之后。而在 GNU C++ 中,仅当属性说明符紧跟分号时(即标签对应于空语句),才允许在标签上使用属性。如果缺少分号,C++ 中的标签属性就会产生歧义,因为 C++ 允许为声明(声明本身可能以属性列表开头)添加标签。由于 C90 和 C99 不允许为声明添加标签,因此在这些标准中不会出现这种歧义。

2.1.2   枚举器属性

    在 GNU C 中,属性说明符列表可以作为枚举器(enumerator)的一部分出现。如果存在属性,它应置于枚举常量之后、= 之前。枚举器中的可选属性修饰的是该枚举常量;若存在常量表达式,则不能将属性置于其后。

2.1.3   语句属性

    在 GNU C 中,属性说明符列表可以作为空语句的一部分出现。该属性位于分号之前。某些采用新式语法的属性也支持用于非空语句。

2.1.4   类型属性

属性说明符列表可以作为 struct、union 或 enum 说明符的一部分出现。它可以紧跟在 struct、union 或 enum 关键字之后,也可以位于右花括号之后;通常优先采用前一种语法。当属性说明符位于右花括号之后时,它们被视为与所定义的struct、union或enum类型相关联,而非与该类型说明符所在的外部声明相关联;此外,所定义的类型直到属性说明符之后才算完整。

2.2  GNU属性注意事项

    此外,属性说明符作为声明的一部分出现(包括针对未命名参数和类型名称的声明),并关联到该声明(该声明可能嵌套在另一声明中,例如参数声明的情况)或声明中的特定声明符。当属性说明符应用于声明为函数或数组的参数时,它应当作用于该函数或数组,而不是作用于该参数隐式转换成的指针;然而,这一点目前尚未得到正确实现

    在声明起始处的说明符(specifier)和限定符(qualifier)列表中,无论该上下文是否允许出现存储类说明符,都可以包含属性说明符(attribute specifier)。(不过,某些属性本质上属于存储类说明符,仅在允许使用存储类说明符的场合才有意义;例如 section 属性。) 该语法有一项必要的限制:函数定义中的第一个旧式(old-style)参数声明不能​​以属性说明符开头,因为根据下文所述的语法规则,此类属性会被视为应用于整个函数(尽管该语法规则在此特定情形下尚未实现)。在某些其他情况下,尽管该语法允许使用属性说明符,但编译器尚不支持。此处出现的所有属性说明符均作用于整个声明。在那种因缺少类型说明符而默认类型为 int 的旧式用法中,上述说明符与限定符列表可以仅由属性说明符构成,而不包含其他说明符或限定符。

    目前,函数原型中的第一个参数必须包含非属性说明符(attribute specifier)的类型说明符这一规定解决了对 void f(int (__attribute__((foo)) x)) 进行解析时可能产生的歧义,但未来可能会有所变动。此外,如果函数声明符的括号内仅包含属性,则这些属性会被忽略——既不会引发错误或警告,也不会被解读为包含一个 int 类型参数——不过这一行为也可能发生变化。

    在用同一组说明符和限定符声明多个标识符时,属性说明符列表可以紧跟在声明符(第一个声明符除外)之前,以逗号分隔。此类属性说明符仅适用于其前面的声明符。例如:

__attribute__((noreturn)) void d0 (void),__attribute__((format(printf, 1, 2))) d1 (const char *, ...),d2 (void);

    noreturn 属性适用于所有声明的函数;format 属性仅适用于 d1。

   

    属性说明符列表可以出现在标识符声明(函数定义除外)末尾的逗号、= 或分号之前。此类属性说明符适用于所声明的对象或函数。若指定了对象或函数的汇编器名称(参见“控制汇编代码中使用的名称”),则该属性必须置于 asm 规范之后。

    将来,函数定义中的声明符之后(在任何旧式参数声明或函数体之前)可能会允许出现属性说明符列表。

    在 C99 语法中,针对形参数组声明符的 [] 内部出现的类型限定符(这些限定符作用于数组隐式转换成的指针),可以同时指定属性说明符。这些属性说明符作用于指针而非数组,但目前尚未实现该功能,因此它们会被忽略。

    属性说明符列表可以出现在嵌套声明符的开头。目前,这种用法存在一些局限性:虽然属性能够正确地应用于声明符,但对于大多数具体属性而言,其隐含的语义尚未实现。当属性说明符位于指针声明符的 `*` 之后时,它们可以与现有的类型限定符混合使用。下文描述了该语法的形式语义。如果你熟悉 ISO C 标准中关于声明符的形式规范,理解起来会更容易。

    考虑(如 C99 标准第 6.7.5 节第 4 段所述)声明 T D1,其中 T 包含指定某种类型 Type(例如 int)的声明说明符,而 D1 是包含标识符 ident 的声明符。对于类型中不包含属性说明符的派生声明符,其标识符 ident 所指定的类型遵循 ISO C 标准的规定。

    如果 D1 的形式为 (attribute-specifier-list D),且声明 T D 为标识符指定了类型“derived-declarator-type-list Type”,则 T D1 为该标识符指定了类型“derived-declarator-type-list attribute-specifier-list Type”。

    如果 D1 的形式为 * type-qualifier-and-attribute-specifier-list D,且声明 T D 为标识符指定了类型“derived-declarator-type-list Type”,那么声明 T D1 即为该标识符指定了类型“derived-declarator-type-list type-qualifier-and-attribute-specifier-list pointer to Type”。

    例如:

void (__attribute__((noreturn)) ****f) (void);

指定类型为“指向指向不返回 void 的函数的指针”。例如:

char *__attribute__((aligned(8))) *f;

指定了“指向 8 字节对齐的 char 指针的指针”这一类型。再次提醒,这不适用于大多数属性;例如,上文提到的 ‘aligned’ 和 ‘noreturn’ 属性的用法尚不支持。

为了兼容那些针对未在嵌套声明符上实现属性支持的编译器版本所编写的现有代码,属性的放置位置允许有一定的灵活性。如果一个仅适用于类型的属性被应用于某项声明,则该属性被视为应用于该声明的类型;反之,如果一个仅适用于声明的属性被应用于某项声明的类型,则该属性被视为应用于该声明本身。此外,为了兼容将属性直接置于被声明标识符之前的代码,当此类属性应用于函数返回类型时,它被视为应用于函数类型;当应用于数组元素类型时,它被视为应用于数组类型。如果一个仅适用于函数类型的属性被应用于函数指针类型,则该属性被视为应用于指针所指向的类型;如果该属性被应用于非函数指针类型的函数返回类型,则它被视为应用于函数类型。

2.3  函数属性(与通用属性有重叠)

    在 GNU C 中,你可以针对程序中调用的函数声明某些特性,从而帮助编译器优化函数调用并更仔细地检查代码

    关键字 __attribute__ 允许你在进行声明时指定特殊属性。该关键字后面紧跟一对双括号,括号内包含具体的属性说明。目前,针对所有目标平台上的函数,已定义了属性:noreturn, noinline, always_inline, pure, const, nothrow, format, format_arg, no_instrument_function, section, constructor, destructor, used, unused, deprecated, weak, malloc, alias, warn_unused_result 和nonnull。此外,针对特定目标系统上的函数,还定义了其他一些属性。包括 section 在内的其他属性也支持用于变量声明(参见指定变量属性)和类型声明(参见第 6.33 节指定类型属性)。

    你还可以使用 __ 来指定属性,它位于每个关键字的前后。这样,你就可以在头文件中使用它们,而无需担心可能存在同名宏(注:如果别的同名宏也加了下划线咋办?)。例如,你可以使用 __noreturn__ 代替 noreturn。

    有关使用属性的详细语法,请参阅属性语法。

2.3.1  noreturn

    某些标准库函数(如 abort 和 exit)无法返回,GCC 会自动识别这一点。有些程序会定义自己的、永不返回的函数;你可以通过将它们声明为 noreturn 来告知编译器这一特性。例如:

void fatal () __attribute__ ((noreturn));

void

fatal (/* … */)

{

  /* … */ /* Print error message. */ /* … */

  exit (1);

}

noreturn关键字告知编译器:假定 fatal 函数不会返回。这样,编译器在进行优化时,无需考虑 fatal 万一返回后会发生什么情况,从而生成效率稍高的代码。更重要的是,这有助于避免关于未初始化变量的误报警告。

    noreturn 关键字不会影响异常处理流程(如果涉及的话):被标记为 noreturn 的函数仍可以通过抛出异常的方式返回给调用者。

    切勿假定由调用函数保存的寄存器会在调用 noreturn 函数之前被恢复。

    noreturn 函数的返回类型若不是 void,则没有意义。

    GCC 2.5 之前的版本未实现 noreturn 属性。若要声明函数不返回,另一种在当前版本及部分旧版本中均可使用的方法如下:

typedef void voidfn ();

volatile voidfn fatal;

2.3.2  noinline

    该函数属性可防止函数被考虑进行内联。

2.3.3  always_inline

    通常,除非指定了优化,否则函数不会被内联。对于声明为 inline 的函数,该属性会强制进行内联,即使未指定优化级别也是如此

2.3.4  pure

    许多函数除了返回值外没有其他副作用,且其返回值仅取决于参数和/或全局变量。此类函数可以像算术运算符一样,进行公共子表达式消除和循环优化。应当使用 pure 属性来声明这些函数。例如:

int square (int) __attribute__ ((pure));

这表明,对于那个假设的 square 函数,其调用次数少于程序原定次数也是安全的。

    strlen 和 memcmp 是pure函数的一些常见例子。值得注意的非pure函数包括包含无限循环的函数,以及依赖于易变内存或其他系统资源(这些资源在两次连续调用之间可能会发生变化)的函数(例如多线程环境下的 feof )。

    GCC 2.96 之前的版本未实现 pure 属性。

2.3.5  const

    许多函数除了参数之外不读取任何值,除了返回值之外也不产生任何副作用。从本质上讲,这属于比前述 const 属性更为严格的一类函数,因为此类函数不允许读取全局内存。

请注意,如果函数包含指针参数并读取指针所指向的数据,则不得将其声明为 const。同样,如果函数调用了非 const 函数,通常也不应将其声明为 const。此外,将 const 函数声明为返回 void 也是没有意义的。

    GCC 2.5 之前的版本并未实现 const 属性。若要声明函数无副作用,另一种适用于当前版本及部分旧版本的方法如下:

typedef int intfn ();

extern const intfn square;

这种方法在 2.6.0 及更高版本的 GNU C++ 中不再适用,因为该语言规定 const 必须修饰返回值。

2.3.6  nothrow

    nothrow 属性用于告知编译器某个函数不会抛出异常。例如,标准 C 库中的大多数函数都可以保证不抛出异常,但接收函数指针作为参数的 qsort 和 bsearch 函数则是明显的例外。GCC 3.2 之前的版本并未实现 nothrow 属性。

2.3.7  format (archetype, string-index, first-to-check)

         format 属性指定某个函数接受 printf、scanf、strftime 或 strfmon 风格的参数,并要求根据格式字符串对这些参数进行类型检查。例如,以下声明:

extern int

my_printf (void *my_object, const char *my_format, ...)

      __attribute__ ((format (printf, 2, 3)));

使编译器检查对 my_printf 的调用中的参数,以确保其与 printf 风格的格式字符串参数 my_format 相匹配。

●  参数 archetype 决定了如何解析格式字符串,其值应为 printf ,scanf,strftime 或 strfmon(也可以使用 __printf__,__scanf__,__strftime__ 或 __strfmon__)。

●  参数 string-index 指定哪个参数是格式字符串参数(从 1 开始计数)

●  first-to-check 指定了需要根据格式字符串进行检查的第一个参数的编号。对于无法检查参数的函数(例如 vprintf),应将第三个参数指定为 0;在这种情况下,编译器仅检查格式字符串本身的一致性。对于 strftime 格式,第三个参数必须为 0。

由于非静态 C++ 成员函数包含一个隐式的 this 参数,因此在为 string-index 和 first-to-check 指定数值时,应从 2 而不是 1 开始计数。

在上述示例中,格式字符串(my_format)是函数 my_print 的第二个参数,而需要检查的参数从第三个参数开始,因此 format 属性的正确参数值应为 2 和 3。

format 属性允许你指定接受格式字符串作为参数的自定义函数,以便 GCC 能够检查对这些函数的调用是否存在错误。只要启用了相关警告(使用 -Wformat 选项),编译器总是(除非使用了 -ffreestanding 选项)会对标准库函数 printf, fprintf, sprintf, scanf, fscanf, sscanf, strftime, vprintf,    vfprintf  和 vsprintf 进行格式检查,因此无需修改头文件 stdio.h。在 C99 模式下,snprintf, vsnprintf, vscanf, vfscanf 和 vsscanf 函数也会受到检查。除了在严格符合 C 标准的模式下,X/Open 函数 strfmon 以及 printf_unlocked 和 fprintf_unlocked 也会受到检查。

2.3.8  format_arg (string-index)

    format_arg 属性用于指定某个函数接收一个用于 printf,scanf,strftime 或 strfmon 类函数的格式字符串,并对其进行修改(例如将其翻译为另一种语言),以便将结果传递给 printf , scanf ,strftime 或 strfmon 类函数(此时,传递给该格式化函数的其余参数与未修改字符串时相同)。例如,以下声明:

extern char *

my_dgettext (char *my_domain, const char *my_format)

      __attribute__ ((format_arg (2)));

该属性指示编译器检查对 printf ,scanf ,strftime 或 strfmon 类函数的调用(其中格式字符串参数是对 my_dgettext 函数的调用),以验证其参数是否与格式字符串参数 my_format 一致。如果未指定 format_arg 属性,编译器在遇到此类格式化函数调用时,仅能识别出格式字符串参数并非常量;虽然使用 -Wformat-nonliteral 选项时会触发警告,但若没有该属性,编译器将无法对这些调用进行实际的参数检查。

    参数 string-index 指定哪个参数是格式字符串参数(从 1 开始计数)由于非静态 C++ 成员函数包含一个隐式的 this 参数,因此这类函数的参数应从 2 开始计数

    format-arg 属性允许你指定那些修改格式字符串的自定义函数,从而使 GCC 能够检查对 printf ,scanf,strftime 或 strfmon 类函数的调用——前提是这些函数的参数包含对你自定义函数的调用。除非使用了 -ansi 或相应的 -std 选项以要求严格支持 ISO C 标准,或者使用了 -ffreestanding 选项,否则编译器始终会按此方式处理 gettext,dgettext 和 dcgettext。

2.3.9  nonnull(arg-index, …)

    nonnull 属性指定某些函数参数应该是非空指针。例如,声明:

extern void *

my_memcpy (void *dest, const void *src, size_t len)

    __attribute__((nonnull (1, 2)));

这会促使编译器检查 my_memcpy 的调用,确保 dest 和 src 参数非空(non-null)。如果编译器检测到在标记为“非空”的参数位置传入了空指针,且启用了 -Wnonnull 选项(W前缀是Warn的首字母),就会发出警告。此​​外,编译器也可能利用“某些函数参数非空”这一信息来进行优化。

    如果未向 nonnull 属性提供参数索引列表,则所有指针参数都会被标记为非空。举例来说,以下声明与前面的示例等价:

extern void *

my_memcpy (void *dest, const void *src, size_t len)

    __attribute__((nonnull));

2.3.10  no_instrument_function

    如果指定了 -finstrument-functions 选项,编译器会在大多数用户编译的函数入口和出口处生成用于性能分析(profiling)的函数调用代码;但带有此属性的函数将不会被插入此类代码

2.3.11  section(“section-name”)

    通常,编译器会将生成的代码放置在代码段(text section)中。然而,有时你需要其它的段,或者需要将某些特定函数放置在特殊的段中。section 属性用于指定函数位于特定的段中。例如,以下声明:

extern void foobar (void) __attribute__ ((section ("bar")));

将函数 foobar 放入 bar 段中。

某些文件格式不支持任意段(section),因此并非所有平台都支持 section 属性。如果需要将模块的全部内容映射到特定段,请考虑改用链接器提供的功能

2.3.12  constructor,destructor

constructor 属性使得该函数在程序进入 main() 函数执行之前自动被调用;同样,destructor 属性使得该函数在 main() 函数执行完毕或调用 exit() 后自动被调用。带有这些属性的函数非常适合用于初始化那些将在程序运行期间被隐式使用的数据。

    这些特性目前尚未在 Objective-C 中实现。

2.3.13  unused

    应用于函数的这一属性表明,该函数可能不会被使用;GCC 不会针对此函数发出警告

2.3.14  used

    当此属性应用于某个函数时,意味着即使该函数看似未被引用,编译器也必须为其生成代码。例如,当函数仅在内联汇编中被引用时,该属性就非常有用。

2.3.15  depecated(弃用标识)

    如果在源文件中的任何位置使用了带有 deprecated 属性的函数,就会触发警告。这对于识别那些预计将在程序未来版本中移除的函数非常有用。警告信息中还包含了该弃用函数的声明位置,方便用户轻松查找关于函数为何被弃用或应采取何种替代方案的详细信息。请注意,此类警告仅在函数被调用时才会触发:

int old_fn () __attribute__ ((deprecated));

int old_fn ();

int (*fn_ptr)() = old_fn;

导致第 3 行出现警告,但第 2 行没有。

deprecated 属性也可用于变量和类型(参见变量属性和类型属性)。

2.3.16  warn_unused_result

    warn_unused_result 属性的作用是:如果带有该属性的函数的调用者未利用其返回值,编译器便会发出警告。对于那些不检查返回值即意味着存在安全隐患或必然导致程序错误的函数(例如 realloc),该属性非常有用。

int fn () __attribute__ ((warn_unused_result));

int foo ()

{

  if (fn () < 0) return -1;

  fn ();

  return 0;

}

在第 5 行会出现警告。

2.3.15  weak

    weak 属性使得声明输出为弱符号(weak symbol)而非全局符号。这主要用于定义可在用户代码中被覆盖的库函数,不过也可用于非函数声明。弱符号在 ELF 目标上受支持,在使用 GNU 汇编器和链接器时,在 a.out 目标上也受支持。

2.3.16  malloc

    malloc 属性用于告知编译器:可以将该函数视为返回一个非空(non-NULL)指针,且该指针不会与函数返回时已存在的任何其他指针发生别名(alias)关系。这通常有助于提升优化效果。具有此特性的标准函数包括 malloc 和 calloc。对于类似 realloc 的函数,只要在函数返回非空值之后不再引用旧指针(包括将其与新指针进行比较),它们也具备这一特性。

2.3.17  alias(“target”)

alias 属性会使该声明作为另一个符号(必须指定)的别名。例如:

void __f () { /* Do something. */; }

void f () __attribute__ ((weak, alias ("__f")));

将 f 声明为 __f 的弱别名。在 C++ 中,必须使用目标名称的修饰名(mangled name)。

并非所有目标机器都支持该属性。

2.3.18  visibility ("visibility_type")

在 ELF(Executable and Linkable Format) 目标上,visibility 属性会使声明以 default, hidden, protected 或 internal visibility输出

void __attribute__ ((visibility ("protected")))

f () { /* Do something. */; }

int i __attribute__ ((visibility ("hidden")));

有关完整详情,请参阅 ELF gABI,但简而言之:

●  default —— 在 ELF 格式中,default visibility是常规情况。该值可用于visibility属性,以覆盖其他可能改变符号default visibility设定的选项。

●  hidden —— hidden visibility意味着该符号不会被放入动态符号表中,因此其他模块(可执行文件或共享库)无法直接引用它。

●  protected ——protected visibility表明,该符号会被放入动态符号表中,但定义该符号的模块内部的引用将绑定到该局部符号上。即,该符号无法被其他模块覆盖。

●  internal —— internal visibility 类似于hidden visibility,但包含特定于处理器的额外语义。除非 psABI 另有规定,GCC 将internal定义为:该函数绝不会被其他模块调用。需要注意的是,虽然hidden符号无法被其他模块直接引用,但仍可通过函数指针进行间接引用。通过标明某个符号无法从模块外部调用,GCC 可以进行某些优化——例如,由于已知调用方函数已加载了正确的值,编译器便无需再加载 PIC 寄存器。

并非所有 ELF 目标都支持该属性。

2.3.19  regparm(number)

    在 Intel 386 架构上,regparm 属性指示编译器通过寄存器 EAX、EDX 和 ECX(而非栈)来传递最多指定数量的整型参数。对于接受可变数量参数的函数,其所有参数仍将通过栈进行传递。

    请注意,在某些采用延迟绑定(默认行为)的 ELF 系统上,该属性不适用于共享库中的全局函数。延迟绑定机制在首次调用时会通过动态链接器(loader)中的符号解析代码进行处理;根据标准调用约定,这些解析代码可能会改写 EAX、EDX 和 ECX 寄存器。不过,使用 GLIBC 2.1 或更高版本的 GNU 系统以及 FreeBSD 系统通常被认为是安全的,因为这些系统的动态链接器会保存所有寄存器的状态。(如有需要,可通过链接器或动态链接器禁用延迟绑定,从而规避此问题。)

2.3.20  stdcall

    在 Intel 386 架构上,stdcall 属性会让编译器假定被调用函数负责弹出用于传递参数的栈空间,除非该函数接受可变数量的参数。

2.3.21  fastcall

    在 Intel 386 架构上,fastcall 属性指示编译器通过 ECX 和 EDX 寄存器传递前两个参数,后续参数则通过栈传递。被调用函数负责从栈中弹出这些参数。如果参数数量可变,则所有参数均通过栈传递。

2.3.22  cdecl

    在 Intel 386 架构上,cdecl 属性会使编译器假定由调用函数负责弹出用于传递参数的栈空间。这对于覆盖 -mrtd 选项的效果非常有用。

2.3.23  longcall/shortcall

         在 RS/6000 和 PowerPC 平台上,longcall 属性会使编译器始终通过指针来调用该函数,其效果与指定 -mlongcall 选项相同;而 shortcall 属性则会阻止编译器这样做。这些属性的优先级高于 -mlongcall 选项和 #pragma longcall 设置。

2.3.24  long_call/short_call

    该属性指定了在 ARM 架构上调用特定函数的方式。这两个属性都会覆盖 -mlong-calls 命令行选项及 #pragma long_calls 设置。long_call 属性指示编译器始终通过先将函数地址加载到寄存器、再使用该寄存器内容的方式来调用函数;而 short_call 属性则指示编译器始终将调用点到目标函数的偏移量直接写入 BL 指令中

2.3.25  function_vector

    在 H8/300、H8/300H 和 H8S 架构上使用此属性,可指定通过函数向量(function vector)来调用相应的函数。通过函数向量调用函数有助于减小代码体积;但需要注意的是,函数向量的容量有限(H8/300 上最多 128 个条目,H8/300H 和 H8S 上最多 64 个条目),且与中断向量共用存储空间。

    要使该属性正常工作,必须使用 GNU binutils 2.7 或更高版本的 GAS 和 GLD。

2.3.26  interrupt

    在 ARM、AVR、C4x、M32R/D 和 Xstormy16 移植版本上使用此属性,以标明指定的函数为中断处理程序当存在此属性时,编译器将生成适用于中断处理程序的函数进入和退出序列

    注意:对于 m68k,H8/300,H8/300,H8S 和 SH 处理器,可以通过 interrupt_handler 属性指定中断处理程序。

    注意:在 AVR 平台上,中断将在函数内部被启用。

    注意:对于 ARM 平台,可以通过向 interrupt 属性添加可选参数来指定要处理的中断类型,示例如下:

void f () __attribute__ ((interrupt ("IRQ")));

该参数的允许值为:IRQ、FIQ、SWI、ABORT 和 UNDEF。

2.3.27  interrupt_handler

    在 m68k、H8/300、H8/300H、H8S 和 SH 架构上使用此属性,以指明指定的函数为中断处理程序。当使用此属性时,编译器将生成适用于中断处理程序的函数入口和出口代码序列。

2.3.28  sp_switch

    在 SH 架构上使用此属性,以指示 interrupt_handler 函数应切换到备用栈。该属性需要一个字符串参数,用于指定一个存储备用栈地址的全局变量。

void *alt_stack;

void f () __attribute__ ((interrupt_handler,

                          sp_switch ("alt_stack")));

2.3.29  trap_exit

    在 SH 架构上,将此属性用于中断处理程序(interrupt_handler),可使其使用 trapa 指令而非 rte 指令返回。该属性需要一个整数参数,用于指定要使用的陷阱编号(trap number)。

2.3.30  eightbit_data

在 H8/300、H8/300H 和 H8S 架构上使用此属性,可指定将变量放置在 8 位数据段中。对于位于 8 位数据段中的数据,编译器针对某些操作生成的代码效率更高。请注意,8 位数据段的容量限制为 256 字节。

要使该属性正常工作,必须使用 GNU binutils 2.7 或更高版本的 GAS 和 GLD。

2.3.31  tiny_data

    在 H8/300H 和 H8S 架构上使用此属性,可指定将变量放置在 tiny 数据段中。对于位于 tiny 数据段中的数据,编译器生成的加载(load)和存储(store)指令将更为高效。请注意,tiny 数据段的容量限制在略小于 32KB 的范围内。

2.3.32  saveall

    在 H8/300、H8/300H 和 H8S 上使用此属性,以指示无论是否被使用,除栈指针以外的所有寄存器都应在序言(prologue)中保存。

   

2.3.33  signal

    在 AVR 上使用此属性,以标明指定的函数为信号处理程序。当使用此属性时,编译器将生成适用于信号处理程序的函数进入和退出代码序列。该函数执行期间将禁用中断。

2.3.34  naked

    在 ARM、AVR、C4x 和 IP2K 移植版本上使用此属性,以指示编译器无需为指定函数生成序言(prologue)或尾声(epilogue)序列;这些序列需由程序员自行提供。

2.3.35  model(model-name)

    在 M32R/D 平台上,使用此属性可设定对象及函数生成代码的寻址方式。标识符 model-name 可取 small, medium 或 large,分别对应不同的代码模型。

small model对象位于内存的低 16MB 区域内(因此可使用 ld24 指令加载其地址),并可通过 bl 指令进行调用。

    medium model对象可位于 32 位地址空间的任意位置(编译器会生成 seth/add3 指令序列来加载其地址),并可通过 bl 指令进行调用。

    large model对象可位于 32 位地址空间的任意位置(编译器会生成 seth/add3 指令序列来加载其地址),且可能无法通过 bl 指令直接调用(此时编译器会生成速度较慢的 seth/add3/jl 指令序列)。

    在 IA-64 平台上,使用此属性可设定对象的寻址方式。目前,model-name 仅支持 small 这一标识符,表示采用small(22 位)地址寻址(即可以使用 addl 指令加载其地址)。注意:此类寻址方式本质上不具备位置无关性,因此不得将此属性用于共享库中定义的对象。

2.3.36  far

    对于 68HC11 和 68HC12 架构,far 属性会促使编译器采用一种特定的调用约定,该约定负责在进入和退出函数时处理内存库(memory bank)的切换。当使用 -mlong-calls 选项时,该调用约定也会成为默认设置。

    在 68HC12 上,编译器会使用 call 和 rtc 指令来执行函数调用及返回操作。

在 68HC11 上,编译器会生成一系列指令,用于调用特定于电路板的例程,以切换内存库并调用实际函数。该特定于电路板的例程会模拟调用过程。在函数结束时,程序会跳转至特定于电路板的例程,而不是使用 rts 指令;该特定于电路板的返回例程负责模拟 rtc 指令的行为。

2.3.37  near

    在 68HC11 和 68HC12 架构上,near 属性会使编译器使用基于 jsr 和 rts 指令的常规调用约定。该属性可用于抵消 -mlong-calls 选项的效果。

2.3.38  dllimport

在 Microsoft Windows 目标平台上,dllimport 属性会指示编译器通过一个由 Microsoft Windows DLL 库设置的“指向指针的全局指针”来引用函数或变量。该指针的名称由 _imp__ 前缀与函数或变量名组合而成。该属性隐含了 extern 存储类型

    目前,对于内联函数,该属性会被忽略。如果将该属性应用于符号定义,则会报错。如果某个符号先被声明为 dllimport,随后又进行了定义,则后续引用将忽略该属性并发出警告。此​​外,如果后续声明将其指定为 dllexport,则该属性会被覆盖。

当应用于 C++ 类时,该属性会将非内联成员函数和静态数据成员标记为导入项。不过,对于虚方法,该属性会被忽略,以便利用 thunk(跳转存根)机制创建虚函数表(vtable)。

    在 Cygwin, MinGW 和 ARM-PE 目标平台上,为了兼容其他 Microsoft Windows 编译器,__declspec(dllimport) 被视为 __attribute__ ((dllimport)) 的同义词。

    在函数上使用 dllimport 属性并非必须,但通过消除 DLL 中的 thunk,它可以带来微小的性能提升。在旧版本的 GNU ld 中,导入变量时必须使用 dllimport 属性,但现在可以通过向 ld 传递 -enable-auto-import 选项来避免这一要求。与函数的情况一样,对变量使用该属性也能消除 DLL 中的 thunk。

    使用该属性的一个缺点是,指向被标记为 dllimport 的函数或变量的指针不能用作常量地址。可以通过设置 -mnop-fun-dllimport 标志来禁用函数的该属性。

2.3.39  dllexport

在 Microsoft Windows 目标平台上,dllexport 属性会促使编译器在 DLL 中提供一个指向指针的全局指针,以便能够通过 dllimport 属性对其进行引用。该指针的名称由前缀 _imp__ 与函数名或变量名组合而成。

    目前,对于内联函数,dllexport 属性会被忽略,但可以通过使用 -fkeep-inline-functions 标志强制进行导出。对于未定义的符号,该属性同样会被忽略。

    当应用于 C++ 类时,该属性会将已定义的非内联成员函数和静态数据成员标记为导出项。在类内初始化的静态常量(static consts)除非同时在类外进行了定义,否则不会被标记为导出。

    在 Cygwin、MinGW 和 ARM-PE 目标平台上,为了兼容其他 Microsoft Windows 编译器,__declspec(dllexport) 被视为 __attribute__ ((dllexport)) 的同义词

    若要将符号包含在 DLL 的导出表中,其他可选方法包括:使用包含 EXPORTS 段的 .def 文件,或者在使用 GNU ld 链接器时指定 -export-all 标志。

2.4  C++ 专有变量、函数、和类型属性

    一些属性仅对C++ 程序有意义。

2.4.1  abi_tag(“tag”,…)

    abi_tag 属性可应用于函数、变量、类声明或内联命名空间(inline namespace)。

    当应用于函数、变量或类声明时,它会修改该实体的修饰名(mangled name)以包含标签名称,从而将该函数或类与具有不同 ABI(Application Binary Interface) 的早期版本区分开来;例如,类的大小可能发生了变化,或者函数的返回类型发生了变化(且该类型未在修饰名中编码)。

    当应用于内联命名空间时,它不会影响该命名空间的修饰名;此时,它仅用于触发 -Wabi-tag警告以及对函数和变量进行自动标记。通常,标记内联命名空间优于标记单个声明,但在某些情况下(例如仅需标记类的特定成员时),标记单个声明也是必要的。

    该属性的参数可以是任意长度的字符串列表。这些字符串在输出时会进行排序,因此列表中的顺序无关紧要。

实体的重声明(redeclaration)不得添加新的 ABI 标签,因为这样做会改变修饰名。

    ABI 标签作用于名称,因此模板的所有实例化和特化都具有相同的标签。如果将该属性应用于显式特化或显式实例化,它将被忽略。

-Wabi-tag 标志会针对以下情况发出警告:某个类未包含其子对象和虚函数所使用的全部 ABI 标签;如果你的代码需要与旧版 ABI 共存,使用此选项有助于找出所有需要添加标签的相关类型。

   

当涉及 ABI 标签的类型被用作变量类型或函数返回类型,且该标签未出现在函数签名中时,该标签会自动应用于相应的变量或函数。`-Wabi-tag` 也会针对这种情况发出警告;你可以通过显式标记该变量或函数,或者将其移入已标记的内联命名空间来避免此警告。

2.4.2  init_priority (priority)

    此属性适用于命名空间作用域的变量

    在标准 C++ 中,命名空间作用域内定义的对象保证会按照其在给定编译单元中的定义顺序进行初始化。但对于跨编译单元的初始化顺序,则没有保证。不过,GNU C++ 允许你使用init_priority 属性来控制命名空间作用域内定义的对象的初始化顺序,通过指定相对优先级来实现,其含义与构造函数属性相同(参见“通用属性”)。

    在以下示例中,A 通常在 B 之前创建,但 init_priority 属性会反转此顺序:

Some_Class  A  __attribute__ ((init_priority (2000)));

Some_Class  B  __attribute__ ((init_priority (543)));

请注意,优先级的具体数值并不重要,重要的是它们的相对顺序。与构造函数和析构函数属性的优先级参数一样,部分目标平台不支持 init_priority 属性。在这种情况下,该属性会被拒绝并返回错误,而不是被忽略

2.4.3  no_dangling(无悬空引用)

    此属性可应用于类类型、函数或成员函数。使用此属性标记的类的悬空引用将被抑制 -Wdangling-reference 诊断信息(悬空引用:悬空引用是编译器设计中的一种情况,指指针或引用指向了已被释放或回收的内存位置。);从 gnu::no_dangling-marked 函数返回的引用也会被抑制。例如:

class [[gnu::no_dangling]] S { … };

class A {

  int *p;

  [[gnu::no_dangling]] int &foo() { return *p; }

};

[[gnu::no_dangling]] const int &

foo (const int &i)

{

  …

}

该属性接受一个可选参数,该参数必须是一个求值为真或假的表达式:

template <typename T>

struct [[gnu::no_dangling(std::is_reference_v<T>)]] S {

  …

};

template <typename T>

[[gnu::no_dangling(std::is_lvalue_reference_v<T>)]]

decltype(auto) foo(T&& t) {

  …

};

2.4.4  warn_unused

该属性适用于类型。对于具有非平凡(non-trivial)构造函数和/或析构函数的 C++ 类型,如果未引用该类型的变量,编译器往往无法确定其是否真的未被使用。该类型属性告知编译器:对于此类变量,若看似未被使用,则应像对待基本类型变量那样发出警告

    该属性适用于仅表示值的类型(例如 std::string),而不适用于管理资源的类型(例如 std::lock_guard)。

    该属性在 C 语言中也是合法的,但并无必要,因为 C 语言没有构造函数或析构函数。

2.4.5  cold,hot

除了函数和标签之外,GNU C++ 还允许在 C++ 类、struct或union上使用cold属性和hot属性。

将这些属性应用于类型会将其传播到该类型的每一个成员函数,包括隐式特殊成员函数。请参阅“通用属性”。

2.4.6  trivial_abi

trivial_abi 属性可应用于 C++ 类、struct或union。它指示编译器:即使该类型在函数调用层面本应被视为“非平凡”(non-trivial)类型,在传递和返回该类型时,仍应使用其底层类型对应的 C ABI 规则

为了兼容 Clang 中的原始实现,该属性既可以写成 __attribute__ ((trivial_abi)),也可以写成 [[clang::trivial_abi]]。为避免可移植性方面的问题,[[gnu::trivial_abi]] 会被忽略,并伴随一条警告信息。

带有 trivial_abi 标注的类即使拥有非平凡的析构函数或拷贝/移动构造函数,也不会因此在函数调用层面自动变为非平凡类型。例如:

// 尽管用户提供了特殊成员函数,但就调用而言,A 是平凡的。

struct __attribute__ ((trivial_abi)) A {

  ~A();

  A(const A &);

  A(A &&);

  int x;

};

// 就函数调用而言,B 是平凡的,因为 A 也是如此。

struct B {

  A a;

};

     如果一个类型对于函数调用而言是平凡的,但它具有非平凡的析构函数,并且作为值传递的参数,则约定被调用方会在返回之前销毁该对象。调用方中参数副本的生命周期会在调用开始时结束,而无需调用析构函数。

     当使用标记为 trivial_abi 的类型作为函数参数时,编译器可能会省略对复制构造函数的调用。因此,复制构造函数的副作用可能不会执行。例如,包含指向自身的指针或以其他方式依赖于自身地址(或其子对象地址)的对象不应使用 trivial_abi 声明。

     属性 trivial_abi 在以下情况下无效:

⦁    该类包含虚基类或虚成员函数。

⦁    该类的拷贝构造函数和移动构造函数均被删除。

⦁    该类拥有一个在调用方面非平凡(non-trivial)的基类。

⦁    该类包含一个非静态数据成员,其类型在调用方面是非平凡的,这包括:

(1)  就函数调用而言属于non-trivial类型的类;

(2)  上述任意类型的数组。

2.4.7  no_specializations(禁特化)

     no_specializations 属性可应用于 C++ 类模板、变量模板或函数模板,但不能应用于它们的特化版本。该属性指示编译器:如果对此类模板进行显式特化,则报错。此属性主要用于标准库场景,因为标准规定对某些模板进行特化属于未定义行为。可以通过 -Wno-invalid-specialization 选项或 #pragma GCC ignored -Winvalid-specialization 指令来禁用该错误提示。

     该属性既可以写成 __attribute__ ((no_specializations)),也可以写成[[clang::no_specializations]](以便与 Clang 中的原始实现保持兼容)。为避免可移植性方面的问题,使用 [[gnu::no_specializations]] 时会触发警告并被忽略。

     该属性接受一个可选参数(字符串字面量),该字符串会包含在诊断信息中,用于说明不应特化该模板的原因。

template <typename T>

struct __attribute__ ((__no_specializations__)) A {};

template <typename T, typename U>

[[clang::no_specializations]] int b = 1;

template <typename T>

[[_Clang::__no_specializations__ ("foo cannot be specialized")]] int

foo () { return 42; }

template <> struct A <int> {}; // Error

template <typename T> int b <T, T> = 2; // Error

template <> int foo <int> () { return 43; } // Error

};

2.5  通用属性(可能适用于多种类型)

     以下 GNU 特有属性在大多数目标平台上均受 C 和 C++ 支持。使用标准语法时,必须在属性名称前加上“gnu::”,例如 gnu::section。

     使用 -fopenmp 选项时,GCC 还会识别以 omp:: 为前缀、并采用标准 [[]] 语法的 OpenMP 指令。

2.5.1   access(access-mode, ref-index) , access(access-mode, ref-index,size-index)  

    access 属性支持检测由函数或其调用方执行的无效或不安全访问,以及针对从未被读取过的对象的“只写”访问。此类访问可能会触发诸如 -Wstringop-overflow, -Wuninitialized,-Wunused 等警告。

    access 属性指定函数的一个指针或引用参数将根据 access-mode 进行访问;该模式必须是 read_only,read_write,write_only 或 none 之一。这些模式的语义如下所述。

    该属性所适用的参数由 ref-index 标识,这是一个表示参数在参数列表中位置的整型常量。参数编号从 1 开始。你可以指定多个访问属性来描述不同参数的访问模式,但不允许针对同一参数指定多个访问属性。

    可选的 size-index 参数指定了函数中某个整型参数的位置,该参数用于规定访问操作的最大尺寸。此尺寸指的是 ref-index 所引用的类型对应的元素数量;若指针类型为 void*,则指字节数。若未指定 size-index 参数,则该指针参数必须为 null,或者指向一块经过适当对齐且足够容纳至少一个所引用类型对象的内存区域(这意味着指向对象末尾之后位置的指针并非有效参数)。实际访问的尺寸可以小于该最大值,但绝不可超过它。

    read_only 访问模式指定:应用该模式的指针仅用于读取所引用的对象,而不对其进行写入。除非由 size-index 指定的访问大小为零,否则所引用的对象必须已初始化。该模式提供的保证比 const 限定符更强;对于 const 限定符,若通过类型转换去除了该限定符,则无法阻止所指向的对象被修改。read_only 访问模式的使用示例包括 puts 函数的参数,或 memcpy 函数的第二个参数。

[[gnu::access (read_only, 1)]]

int puts (const char*);

[[gnu::access (read_only, 2, 3)]]

void* memcpy (void*, const void*, size_t);

read_write 访问模式适用于不带 const 限定符的指针类型参数。它指定该指针既用于读取也用于写入其所引用的对象。除非由 size-index 指定的访问大小为零,否则该指针所引用的对象必须已初始化。strcat 函数的第一个参数就是使用 read_write 访问模式的一个例子。

[[gnu::access (read_write, 1), gnu::access (read_only, 2)]]

char* strcat (char*, const char*);

    write_only 访问模式适用于不带 const 限定符的指针类型参数。它指定该指针仅用于向其指向的对象写入数据,而不从中读取数据;因此,该指针所指向的对象无需进行初始化。write_only 访问模式的使用示例包括 strcpy 函数的第一个参数,或 fgets 函数的前两个参数。

__attribute__ ((access (write_only, 1), access (read_only, 2)))

char* strcpy (char*, const char*);

__attribute__ ((access (write_only, 1, 2), access (read_write, 3)))

int fgets (char*, int, FILE*);

none 访问模式指定:所适用的指针参数完全不用于访问其指向的对象。除非该指针为空(null),否则其指向的对象必须存在,且大小至少应符合 size-index 参数所指定的值。对于 void* 类型的参数,若省略了可选的 size-index 参数,则实际传入的指针参数将被忽略。所指向的对象无需经过初始化。该模式旨在辅助验证预期的对象大小,例如在调用 __builtin_object_size 的函数中。请参阅“对象大小检查”(Object Size Checking)相关内容。

    请注意,access 属性仅指定如何访问指针参数引用的对象;这并不意味着将会发生访问。此外,access 属性并不意味着属性 nonnull 也不意味着属性 nonnull_if_nonzero;在通过指针参数无条件操作缓冲区的函数声明中添加这两个属性可能是合适的。有关更多信息和注意事项,请参阅nonnull 或 nonnull_if_nonzero 函数属性。

2.5.2  alias(“target”)

     该属性适用于变量和函数。

     alias 属性使得该声明被输出为另一个符号(称为“别名目标”)的别名。

     例如:

int var_target;

extern int var_alias [[gnu::alias ("var_target")]];

将 var_alias 定义为 var_target 变量的别名。

     又例如:

void __f () { /* Do something. */; }

void f () __attribute__ ((weak, alias ("__f")));

     将 ‘f’ 定义为 ‘__f’ 的弱别名。

     除了顶级限定符之外,别名目标必须与别名具有相同的类型。对于变量,它还必须具有相同的大小和对齐方式。

     别名目标必须与别名定义在同一个编译单元中。在 C++ 中,必须使用目标的修饰名称。

     请注意,如果没有此属性,GCC 会假定具有外部链接的不同声明表示不同的对象。在未使用此属性声明别名的编译单元中,同时使用别名和别名目标访问同一对象是未定义的。

     此属性需要汇编程序和目标文件的支持,并且可能并非所有目标都支持。

2.5.3  aligned, aligned(alignment)

     此属性适用于函数、变量、typedef、union和struct字段

     aligned 属性指定其所应用实体的最小对齐方式,以字节为单位。如果指定了 alignment,则其值必须是 2 的幂次方整数

     例如,以下声明:

int x [[gnu::aligned (16)]] = 0;

使编译器将全局变量 x 分配在 16 字节边界上。

这些声明:

struct __attribute__ ((aligned (8))) S { short f[3]; };

typedef int more_aligned_int __attribute__ ((aligned (8)));

强制编译器确保(在能力范围内)将类型为 struct S 或 more_aligned_int 的每个变量分配在至少 8 字节的边界上并进行对齐。

     当应用于函数时,若未指定对齐参数,则意味着采用针对目标架构的理想对齐方式。可以使用 __alignof__ 运算符来确定该对齐值(参见“确定函数、类型或变量的对齐方式”一节)。如果未在同一编译单元中提供该函数的定义,则此属性不起作用。

     该属性不能用于降低先前已声明为具有更严格对齐要求的函数的对齐度;它只能用于提高对齐度。若尝试进行此类操作,编译器会报错。某些目标平台为函数规定了大于 1 的默认最小对齐要求;在此类平台上,若指定的对齐要求宽松于该默认值,则会被静默忽略。对特定函数使用该属性,将覆盖针对该函数生效的 -falign-functions 选项(参见“控制优化的选项”)一节的效果。

     对于变量、类型和struct字段,提高对齐要求(使其高于默认值)可以允许在对象复制时使用更大的数据块,并提升指针算术运算及数组寻址的效率。例如,某些体系结构要求 64 位数值必须在内存中按 8 字节边界对齐,才能进行加载和存储操作。若显式地将包含两个 32 位字段的struct类型按 8 字节边界对齐,则只需一对 64 位加载与存储指令即可完成复制,而无需执行两对 32 位操作。

     如果在属性中省略对齐方式,则意味着采用目标架构(即编译目标)的默认最大对齐方式。这对所有标量类型而言已足够,但对于支持向量操作的目标架构,这可能不足以满足所有向量类型的对齐要求默认对齐方式由特定的目标 ABI 确定

     GCC 还提供了一个特定于目标平台的宏 __BIGGEST_ALIGNMENT__,它表示目标机器上任何数据类型所使用的最大对齐值。例如,你可以这样写:

    short array[3] [[gnu::aligned (__BIGGEST_ALIGNMENT__)]];

这会使编译器将数组的对齐方式设置为 __BIGGEST_ALIGNMENT__。请注意,__BIGGEST_ALIGNMENT__ 的值可能会随命令行选项的不同而变化。

     当用于struct或struct成员时,aligned 属性只能增加对齐要求;若要降低对齐要求,则必须同时指定 packed 属性。当作为 typedef 的一部分使用时,aligned 属性既可以增加也可以降低对齐要求,且指定 packed 属性会引发警告。

     根据 ISO C 标准,任何struct或union类型的对齐要求,必须至少是该类型所有成员各自对齐要求的最小公倍数的整数倍。这意味着,虽然可以通过为该类型的任一成员指定对齐属性来间接调整整个struct或union的对齐方式,但上述示例中展示的写法提供了一种更显眼、直观且易于阅读的方式,用于明确要求编译器调整整个struct或union类型的对齐方式。

     请注意,对齐属性的有效性可能受限于系统链接器和/或目标文件格式的固有限制。在某些系统中,链接器只能将函数或数据对齐到一定的最大对齐程度。(对于某些链接器,支持的最大对齐程度可能非常小。)有关更多信息,请参阅链接器文档。

     栈变量不受链接器限制的影响;GCC 能够在任何目标平台上正确地对其进行对齐。

2.5.4   alloc_align(position)  

alloc_align 属性可应用于返回指针且至少接受一个整数或枚举类型参数的函数。

这表明返回的指针是按照函数参数所指定的边界进行对齐的。有效的对齐值应为大于 1 的 2 的幂。GCC 利用这一信息来改进指针对齐分析。

用于指定分配对齐方式的函数参数,由一个常量整数实参来标识;该实参的编号即为属性的实参值。实参编号从 1 开始

例如:

void* my_memalign (size_t, size_t) __attribute__ ((alloc_align (1)));

声明 my_memalign 返回的内存满足由参数 1 指定的最小对齐要求。

2.5.5   alloc_size(position), alloc_size(position-1, position-2)  

alloc_size 属性可应用于以下对象:返回指针且至少接受一个整数或枚举类型参数的函数;此类函数的类型声明;或者类型为“指向此类函数的指针”的类型或变量。

这表明返回的指针指向一块内存,其大小由位于位置 1 的函数参数指定,或者是位置 1 和位置 2 处参数的乘积。有效的大小值应为小于 PTRDIFF_MAX 的正数。GCC 利用这些信息来改进 __builtin_object_size 的结果。

用于指定分配大小的函数参数,由传递给该属性的一个或两个整数参数来确定。分配的大小既可以是所指定的单个函数参数的值,也可以是所指定的两个函数参数的乘积。参数编号对于普通函数从 1 开始,对于 C++ 非静态成员函数则从 2 开始

例如:

void* my_calloc (size_t, size_t) __attribute__ ((alloc_size (1, 2)));

void* my_realloc (void*, size_t) __attribute__ ((alloc_size (2)));

声明 my_calloc 返回大小等于参数 1 与参数 2 乘积的内存,且 my_realloc 返回大小等于参数 2 的内存。

    类似地,下列例子

typedef __attribute__ ((alloc_size (1, 2))) void*

  (*calloc_ptr) (size_t, size_t);

typedef __attribute__ ((alloc_size (1))) void*

  (*malloc_ptr) (size_t);

指定 calloc_ptr 是一个函数指针,该函数类似于标准 C 库函数 calloc,返回一个大小等于其第 1 个和第 2 个参数乘积的对象;同样地,指定 malloc_ptr 是一个函数指针,该函数类似于标准 C 库函数 malloc,返回一个大小等于其第 1 个参数值的对象。

2.5.6   always_inline

    always_inline 属性适用于函数。

通常,除非指定了优化,否则函数不会被内联。对于声明为 inline 的函数,若加上此属性,则无论通常适用于内联的限制条件如何,该函数都会被强制内联;若未能内联此类函数,编译器会报错。请注意,如果此类函数被间接调用,编译器可能会也可能不会对其进行内联(具体取决于优化级别),且针对间接调用未能内联的情况,编译器既可能报错,也可能不报错。

    如果需要在多个编译单元中使用该内联函数,应将 always_inline 属性应用于头文件中的函数定义,并将该头文件包含在所有使用该函数的编译单元中。链接时优化(LTO)能够跨编译单元内联函数,但这需要额外指定一个通常会启用内联功能的优化级别。

2.5.7   artificial

    该属性应用于函数。

    artificial属性适用于小型内联包装函数;在理想情况下,调试时应将此类函数视为一个整体。根据调试信息格式的不同,该属性的实现方式可能是将函数标记为artificial函数,或者是将内联函数体内的所有指令都关联到调用者的位置。

2.5.8   assume

    这是一个只能作为属性声明出现的语句属性。

    带有空语句的 assume 属性充当一种可移植的假设机制。它接受单个参数(一个条件表达式),该表达式不会被求值。如果该参数在出现位置的求值结果为真,则该属性不起作用;否则,将导致未定义行为。这是 ISO C++23 标准中 assume 属性的一个 GNU 变体,但它可在 C 和 C++ 的任意版本中使用。

int

foo (int x, int y)

{

  [[gnu::assume(x == 42)]];

  [[gnu::assume(++y == 43)]];

  return x + y;

}

y 实际上并未自增,编译器既可以选择将其优化为直接返回 42 + 42,也可以选择不这样做。

2.5.9   assume_aligned(alignment), assume_aligned(alignment,offset)

    这表明返回的指针按照 alignment 指定的边界进行对齐。如果该属性包含两个参数,则第二个参数表示错位偏移量(misalignment offset)。alignment 的有效值应为大于 1 的 2 的幂;offset 的有效值应大于 0 且小于 alignment。

例如:

void* my_alloc1 (size_t) __attribute__((assume_aligned (16)));

void* my_alloc2 (size_t) __attribute__((assume_aligned (32, 8)));

声明 my_alloc1 返回 16 字节对齐的指针,而 my_alloc2 返回的指针其值模 32 等于 8

2.5.10   btf_decl_tag

    此属性可应用于函数、变量、结构或联合成员声明以及函数参数声明

    btf_decl_tag 属性将所修饰的实体与一个任意字符串关联起来。编译器不会以任何方式解析这些字符串,它们也不会对代码生成产生影响。相反,这些由用户提供的字符串会被记录在 DWARF(通过 DW_AT_GNU_annotation 和 DW_TAG_GNU_annotation 扩展)及 BTF 信息(通过 BT​​F_KIND_DECL_TAG 记录)中,并与相应的声明关联。如果未生成 DWARF 或 BTF 信息,则该属性不产生任何作用。

    该参数被视为一个以空字符(null)结尾的序列,包含零个或多个非空字节。不支持宽字符字符串

    对于单个声明,该属性可以多次指定;在此情况下,每个不同的参数字符串都会被记录在与该声明相关联的独立 DIE 或 BTF 记录中。当单个声明包含多个 btf_decl_tag 属性时,生成的 DW_TAG_GNU_annotation DIE 的顺序并不保证与源代码中属性的顺序一致。

    例如:

int *foo [[gnu::btf_decl_tag ("__percpu")]];

    当使用 -gbtf 选项进行编译时,会在 BTF 信息中额外生成一条 BTF_KIND_DECL_TAG 类型的 BTF 记录,将字符串 __percpu 与变量 foo 对应的 BTF_KIND_VAR 记录关联起来。

2.5.11   btf_type_tag(argument)

    该属性适用于类型。

    btf_type_tag 属性可用于将特定类型与任意字符串注解相关联(即进行“标记”)。这些注解会被记录在支持的调试格式(目前包括 DWARF——通过 DW_AT_GNU_annotation 和 DW_TAG_GNU_annotation 扩展——以及 BTF——通过 BT​​F_KIND_TYPE_TAG 记录)的调试信息中。编译器不会以任何方式解析这些注解字符串,它们也不会对代码生成产生影响。如果未生成 DWARF 或 BTF 信息,则该属性不起作用。

    该参数被视为一个以空字符(null)结尾的序列,包含零个或多个非空字节。不支持宽字符字符串

    对于同一个类型,该属性可以多次指定;在这种情况下,每个不同的参数字符串都会被记录在独立的 DIE 或 BTF 记录中,并与该类型相关联。对于拥有多个 btf_type_tag 属性的单一类型,生成的 DW_TAG_GNU_annotation DIE 的顺序并不保证与源代码中属性的顺序一致。

    例如下列代码:

    int * [[gnu::btf_type_tag ("__user")]] foo;

当使用 -gbtf 选项进行编译时,会在 BTF 信息中额外生成一条 BTF_KIND_TYPE_TAG 类型的 BTF 记录;该记录将字符串 __user 与声明中所使用的“指向整数的指针”类型对应的常规 BTF_KIND_PTR 记录关联起来。

请注意,目前的 BTF 格式仅支持表示与指针类型相关联的类型标签(type tags)。在生成 BTF 时,非指针类型上的类型标签可能会被静默忽略。

2.5.12   cleanup(cleanup_function)

    该属性适用于变量。

cleanup 属性用于在变量超出作用域时执行指定的函数。该属性仅适用于函数作用域内的自动变量(auto variables),不能应用于参数或具有静态存储期的变量。该函数必须接受一个参数,即指向与该变量类型兼容的类型的指针;函数的返回值(若有)会被忽略。

当同一作用域内的多个变量具有cleanup 属性时,在退出该作用域时,它们关联的清理函数将按定义的相反顺序执行(即最后定义的变量最先进行清理)。

如果启用了 -fexceptions,那么在处理异常期间进行栈展开(stack unwinding)时,cleanup_function 将会执行。请注意,cleanup 属性并不允许捕获异常,它仅用于执行某种操作。如果 cleanup_function 未能正常返回,其行为是未定义的。

2.5.13   cold,hot

    这些属性可以适用于函数或标签。在 C++ 中,它们同样适用于class、struct、或union。

    函数的 cold 属性告知编译器该函数不太可能被执行。此类函数在优化时侧重于减小代码体积而非提升运行速度;在许多目标平台上,它们会被放置在代码段(text section)的一个特殊子段中,从而使所有 cold 函数在内存中彼此相邻,进而改善程序中非 cold 部分的代码局部性。分支预测机制会将代码中通往 cold 函数调用点的路径标记为“不太可能执行”(unlikely)。因此,将用于处理罕见情况的函数(例如 perror)标记为 cold 是有益的,这有助于优化那些仅在极少数情况下才会调用这些函数的“热点”函数。

    函数上的 hot 属性告知编译器,该函数是编译后程序中的热点。编译器会对该函数进行更激进的优化;此外,在许多目标平台上,该函数会被放置在代码段(text section)的一个特殊子段中,从而使所有热点函数在内存中彼此靠近,进而提升局部性。

    在 C++ 中,应用于类型的 cold 或 hot 属性会使该类型的所有成员函数(包括隐式生成的特殊成员函数)都具有该属性。如果某个成员函数被显式标记为相反的 hot 或 cold 属性,则该成员函数的属性设置优先于类上指定的属性。

    当通过 -fprofile-use 获得配置反馈(profile feedback)时,系统会自动检测hot函数和cold函数,并忽略此属性。

    当作为与标签关联的语句属性使用时,cold 属性告知编译器该标签后的执行路径不太可能被执行;而 hot 属性则告知编译器,相比未加此标注的路径,该标签后的路径更有可能被执行。此属性适用于无法使用 __builtin_expect 的场景,例如涉及计算跳转(computed goto)或 asm goto 的情况。

2.5.14   common,nocommon

    该属性适用于变量。

    -fno-common 属性请求 GCC 将变量放置在“公共”存储空间中。-fno-common 属性则请求相反的做法——直接为其分配空间。

    这些属性分别覆盖了 -fno-common 和 -fcommon 标志所选择的默认值。

2.5.15   const

    该属性适用于函数。

    对于那些返回值不受程序可观测状态变化影响、且除了返回值外不会对该状态产生可观测影响的函数调用,编译器可以进行诸如“公共子表达式消除”之类的优化。若使用 const 属性声明此类函数,GCC 便能在以相同参数值重复调用该函数时,避免生成某些实际的函数调用指令

    例如:

[[gnu::const]] int square (int);

这会告知 GCC:对于后续使用相同参数值调用 square 函数的情况,无论两次调用之间执行了何种语句,都可以直接使用首次调用的结果来替代。

    const 属性禁止函数在连续调用期间读取那些会影响其返回值的对象。不过,声明为 const 的函数可以安全地读取那些不会改变其返回值的对象,例如非易失性(non-volatile)常量。

    与类似的 pure 属性相比,const 属性对函数定义施加了更严格的限制。如果同时使用 const 和 pure 属性声明同一个函数,编译器会发出警告或报错。由于 const 函数不能产生任何可观测的副作用,因此将其声明为返回 void 是没有意义的;若进行此类声明,编译器也会发出警告或报错。

    注意,如果函数包含指针参数并会读取指针所指向的数据,且该数据在函数连续调用期间可能会发生变化,则该函数绝不应声明为 const。通常情况下,由于函数无法区分可能变化的数据与不可变的数据,因此 const 函数不应接受指针参数(在 C++ 中也不应接受引用参数)。同样地,调用了非 const 函数的函数,其自身通常也不应声明为 const。

2.5.16   constructor,desctructor, constructor(priority), desctructor(priority)

    这些属性适用于函数。

    constructor 属性使得该函数在程序进入 main() 函数之前被自动调用;同样地,destructor 属性使得该函数在 main() 执行完毕或调用 exit() 之后被自动调用。带有这些属性的函数非常适合用于初始化那些在程序执行期间被隐式使用的数据。

    在大多数目标平台上,这些属性还接受一个整数参数,用于指定优先级,从而控制构造函数和析构函数的运行顺序。该优先级参数必须是一个介于 101 到 65535(含边界值)之间的常量整型表达式;0 到 100 的优先级范围保留给编译器及其运行时库使用。优先级数值较小的构造函数会先于优先级数值较大的构造函数运行;析构函数的运行顺序则相反。因此,如果有一个分配资源的构造函数和一个释放同一资源的析构函数,这两个函数通常应设为相同的优先级。注意,如果不指定参数,则默认优先级为 65535。

    对于具有静态存储期的 C++ 对象,其构造函数的调用顺序与带有 constructor 属性的函数之间的相对顺序通常是不确定的。你可以在命名空间作用域内的 C++ 对象声明上使用 init_priority 属性(参见“C++ 特有的变量、函数和类型属性”一节)来强制指定顺序;init_priority 属性的优先级参数与 constructor 属性的优先级参数具有相同的作用。

    如果在不支持该特性的目标平台上使用带有参数形式的 constructor 或 destructor 属性,编译器会报错并拒绝该用法。只有少数目标平台(通常是不使用 ELF 目标文件格式或 GNU 链接器的平台)会拒绝这种用法。

    当指定了多个此类属性时,最后一个属性将决定最终的优先级。

2.5.17   copy,copy(name)

    copy 属性可以出现在函数、变量、或类型声明上。

    该特性将与指定名称相关联的一组属性,应用到当前正在声明的函数、变量或类型上。此特性专为定义别名或函数解析器(resolver)的库而设计,这些库通常需要使其属性与目标对象的属性保持一致。copy 特性可用于函数、变量或类型;不过,应用该特性的符号类型(函数或变量)必须与参数所指向的符号类型相匹配。copy 特性仅复制语法和语义属性,而不复制影响符号链接或visibility的属性(例如 alias、visibility 或 weak);此外,deprecated 和 target_clones 特性也不会被复制。

    例如,下文中的 StrongAlias 宏利用 alias 和 copy 属性,为函数 allocate 定义了一个名为 alloc 的别名;该原函数声明时带有 alloc_size、malloc 和 nothrow 属性。得益于 __typeof__ 运算符,该别名与目标函数具有相同的类型;而 copy 属性则确保了该别名与目标函数拥有相同的属性。

#define StrongAlias(TargetFunc, AliasDecl)  \

  extern __typeof__ (TargetFunc) AliasDecl  \

    __attribute__ ((alias (#TargetFunc), copy (TargetFunc)));

extern __attribute__ ((alloc_size (1), malloc, nothrow))

  void* allocate (size_t);

StrongAlias (allocate, alloc);

再举一例:假设下文中的struct A 定义于某个第三方库的头文件中,其对齐要求为 N,且由于使用了 packed 属性,任何未按此要求对齐的该类型变量都会触发警告。若在定义无关struct B 时指定 copy 属性,其效果是将指针表达式所引用的类型的所有相关属性复制到struct B 上。

struct __attribute__ ((aligned (N), warn_if_not_aligned (N)))

A { /* */ };

struct __attribute__ ((copy ( (struct A *)0)) B { /* */ };

2.5.18   counted_by(count)

    counted_by 属性可以附加到 C99 柔性数组成员或struct的指针字段上

    这表明,由柔性数组( flexible array )成员字段所持有的数组元素数量(注:柔性数组成员(Flexible Array Member)是 C99 标准中的一项特性,允许在struct的末尾声明一个大小未指定的数组),或由指针字段所指向的数组元素数量,由同一struct中标识符为 count 的字段指定(该struct同时也包含上述柔性数组成员或指针字段)。

    该属性目前仅在 C 中可用。在 C++ 中,该属性被忽略。

    GCC 可能会利用这些信息来改进对此类结构的对象大小信息的检测,从而在编译时诊断以及数组边界检查器(array bound sanitizer)和 __builtin_dynamic_object_size 等运行时功能中提供更优的结果。

    例如,下列代码:

struct P {

  size_t count;

  char other;

  [[gnu::counted_by (count)]] char array[];

} *p;

指定该数组为柔性数组成员,其元素数量由同一struct中的 count 字段给出。

struct PP {

  size_t count2;

  char other1;

  [[gnu::counted_by (count2)]] char *array2;

  int other2;

} *pp;

该属性指定 array2 是一个由指针字段指向的数组,其元素数量由同一struct中的 count2 字段指定

    表示元素数量的字段必须为整数类型;否则,编译器会报错并忽略该属性。

    当表示元素数量的字段被赋予负整数值时,编译器会将该值视为 0。

    counted_by 属性不能用于函数指针,也不能用于指向包含柔性数组成员的struct或union的指针。不过,它可用于指向非 void 类型的不完整struct或union类型的指针,前提是该类型在首次引用该指针之前能够完成定义。

    该属性允许用于 void 指针。但在指定了 -Wpointer-arith 选项的情况下,此类用法会触发警告。当该属性应用于 void 指针时,该指针所指数组的每个元素大小被视为 1。

    显式的 counted_by 注解定义了两个对象(p->array 和 p->count)之间的关系,且该关系需满足以下要求:

●  在首次引用 p->array 之前,必须先初始化 p->count;

●  p->array 始终至少包含 p->count 个可用元素。即使在程序运行期间这些相关对象发生更新,这一关系也必须保持成立。

    除了上述要求外,当且仅当 p->array 是由该指针字段所指向的数组时,这对元素之间还存在另一项要求:

p->array 和 p->count 只能通过同时更改整个struct来修改。

    程序员有责任确保始终满足上述要求。否则,编译器会发出警告,且数组边界检查器(array bound sanitizer)及 __builtin_dynamic_object_size 内置函数的结果将是未定义的。

    该属性的一个重要特性是,在引用柔性数组成员字段时,会使用该字段(即表示该引用之前元素数量的字段)所赋予的最新值。例如:

p->count = val1;

  p->array[20] = 0;  // ref1 to p->array

  p->count = val2;

  p->array[30] = 0;  // ref2 to p->array

在上述情况中,ref1 使用 val1 作为 p->array 的元素数量,而 ref2 使用 val2 作为 p->array 的元素数量。

    但请注意,上述特性不适用于指针字段。

2.5.19   deprecated, deprecated (msg)

      此属性可用于函数、变量、类型或enum成员的声明。

    如果源文件中任何地方使用了带有 deprecated(已弃用)属性的实体,编译器便会发出警告。这对于标记那些预计将在程序未来版本中移除的函数非常有用。警告信息中还会包含该已弃用实体的声明位置,以便用户轻松查找相关信息,了解其被弃用的原因或应采取的替代方案。请注意,仅在实际使用该实体时才会触发此类警告:

int old_fn () __attribute__ ((deprecated));

int old_fn ();

int (*fn_ptr)() = old_fn;

这会导致第 3 行出现警告,而第 2 行则不会。如果提供了可选的 msg 参数(该参数必须为字符串),则该参数会显示在警告信息中。

    当应用于某种类型时,仅在实际使用该类型时才会触发警告,且仅当该类型被应用于一个本身未被声明为deprecated的标识符时,才会产生此类警告。

typedef int T1 __attribute__ ((deprecated));

T1 x;

typedef T1 T2;

T2 y;

typedef T1 T3 __attribute__ ((deprecated));

T3 z __attribute__ ((deprecated));

这会导致第 2 行和第 3 行出现警告,但第 4、5 或 6 行则不会。第 4 行未发出警告,因为 T2 并未被明确标记为已弃用;第 5 行没有警告,因为 T3 已被明确标记为已弃用;第 6 行的情况也类似。

    此示例使用已弃用的 enumerator 属性来标示 oldval 枚举成员已弃用:

enum E {

  oldval __attribute__((deprecated)),

  newval

};

int

fn (void)

{

  return oldval;

}

可选的 msg 参数(必须为字符串)若存在,将包含在警告信息中输出。msg 字符串的显示受 -fmessage-length 选项设置的影响。字符串中的控制字符会被替换为转义序列;若 -fmessage-length 选项设为 0(默认值),则其中的换行符会被忽略。

2.5.20   designated_init

    该属性仅适用于结构类型。

    它指示此类型的对象的任何初始化都必须使用指定的初始值设定项而不是位置初始值设定项。该属性的目的是允许程序员指示结构的布局可能会发生变化,因此依赖位置初始化将导致将来的破坏

    GCC 默认会针对此属性发出警告;可使用 -Wno-designated-init 选项来抑制这些警告。

2.5.21   error(“message”),warning(“message”)

    该属性适用于函数。

    如果在函数声明中使用了 error 或 warning 属性,且对该函数的调用未被死代码消除或其他优化手段移除,编译器便会报出相应的错误或警告信息(包含指定的提示内容)。这对于编译时检查非常有用,特别是在结合使用 __builtin_constant_p 和内联函数的情况下——因为此时无法通过 extern char [(condition) ? 1 : -1]; 这类技巧来检查内联函数的参数。

    虽然可以通过不定义该函数来触发链接错误(从而在 .gnu.warning* 段中定义带有提示信息的函数),但使用这些属性可以在更早阶段诊断出问题,并且能精确指出调用位置——即使在涉及内联函数或未生成调试信息的情况下也是如此。

2.5.22   expected_throw

    该属性适用于函数。

它告诉编译器,该函数更有可能抛出或传播异常,而不是返回、无限循环或终止程序。

    编译器通常会忽略此提示。只有当它应用于 noreturn 函数,并且启用了 -fharden-control-flow-redundancy 选项,且未覆盖 -fhardcfr-check-noreturn-calls=not-always 选项时,才会生效。

2.5.23   externally_visible

该属性适用于全局变量和函数。

    它会抵消 -fwhole-program 命令行选项的效果,从而确保该对象在当前编译单元之外依然可见。

    如果同时使用 -fwhole-program 和 -flto,并采用 gold 作为链接器插件,系统会根据 gold 生成的解析文件(resolution file),自动为那些在 LTO 对象之外被访问的函数添加 externally_visible 属性(受限于 gold 目前存在的问题,该机制暂不适用于变量)。对于无法生成解析文件的其他链接器,仍需显式指定 externally_visible 属性。

2.5.24   fallthrough

    该语句属性只能作为属性声明出现。

    fallthrough 属性的作用相当于一条 fallthrough 语句。它向编译器表明,从当前位置继续执行(即“穿透”/fall through)到 switch 语句中的下一个 case 标签或用户定义标签是有意为之的,因此不应触发 -Wimplicit-fallthrough 警告。fallthrough 属性在每个属性列表中最多只能出现一次,且不能与其他属性混用。它只能在 switch 语句中使用(否则编译器会报错),且必须位于某条语句之后、逻辑上的下一个 case 标签或用户定义标签之前。

    以下示例使用了 fallthrough 语句属性,以指示不应发出 -Wimplicit-fallthrough 警告:

switch (cond)

  {

  case 1:

    bar (1);

    __attribute__((fallthrough));

  case 2:

    …

  }

2.5.25  fd_arg(N ), fd_arg_read(N ), fd_arg_write(N )

         这些属性可应用于在指定参数 N 处接收已打开文件描述符的函数。

fd_arg 属性表明传入的文件描述符必须处于未关闭状态。因此,当启用 -fanalyzer 选项时,如果分析器检测到某条代码路径中调用了带有此属性的函数,且传入的文件描述符已被关闭,它可能会发出 -Wanalyzer-fd-use-after-close 诊断信息。

    该属性还表明文件描述符在使用前必须经过有效性检查。因此,如果分析器检测到某条代码路径中调用了带有此属性的函数,且传入的文件描述符未经过有效性检查,它可能会发出 -Wanalyzer-fd-use-without-check 诊断信息。

    fd_arg_read 属性与 fd_arg 相同,但增加了一项要求:由于函数可能会从该文件描述符读取数据,因此该文件描述符不得以“只写”模式打开。

    如果分析器检测到某条代码路径中调用了带有此属性的函数,且操作的文件描述符是以 O_WRONLY 模式打开的,它可能会发出 -Wanalyzer-access-mode-mismatch 诊断信息。

    同样,fd_arg_write 属性也与 fd_arg 相同,区别在于:如果分析器检测到某条代码路径中调用了带有此属性的函数,且操作的文件描述符是以 O_RDONLY 模式打开的,它可能会发出 -Wanalyzer-access-mode-mismatch 诊断信息。

2.5.26  flag_enum

    此属性可应用于enum类型。

    它表示枚举器用于位运算,因此,例如,-Wswitch 不应针对枚举器位运算组合对应的情况发出警告。

2.5.27  flatten

此属性适用于函数。

    通常,函数内部的内联操作是有限的。对于标记了此属性的函数,只要条件允许,函数内部的每一次调用都会被内联——包括由内联操作本身引入的调用(但不包括对函数自身的递归调用)。声明为 noinline 或类似属性的函数不会被内联。至于该函数自身是否会被考虑进行内联,则取决于其规模大小以及当前的内联参数设置。

2.5.28  format(archetype, string-index, first-to-check)

    该属性适用于函数。

    它指定该函数接受 printf,scanf,strftime 或 strfmon 风格的参数,并应根据格式字符串对这些参数进行类型检查。例如,以下声明:

[[gnu::format (printf, 2, 3)]]

extern int

my_printf (void *my_object, const char *my_format, ...);

使编译器检查对 my_printf 的调用中的参数,以确保其与 printf 风格的格式字符串参数 my_format 相匹配。

    参数 archetype 决定了如何解析格式字符串。有效的 archetype 包括 printfscanfstrftimegnu_printfgnu_scanfgnu_strftime(也可以使用 __printf____scanf____strftime__ 或 __strfmon__)。像 printf 这样的 archetype 值指的是系统 C 运行时库所支持的格式,而以“gnu_”为前缀的值则始终指的是 GNU C 库所支持的格式。

    在 MinGW 和 Microsoft Windows 目标平台上,还支持 ms_printf, ms_scanf 和 ms_strftime。带有 ms 前缀的值指的是 msvcrt.dll 库所支持的格式。

    Solaris 目标平台还支持 cmn_err(或 __cmn_err__)原型。cmn_err 支持标准 printf 转换说明符的一个子集,以及用于显示位域(bit-field)的双参数 %b 转换。有关更多信息,请参阅 Solaris 中关于 cmn_err 的 man 手册页。

Darwin 目标平台还在 format 属性中支持 CFString(或__CFString__)原型。对于使用此原型的声明,编译器会解析其语法和参数类型是否正确。然而,目前尚未对格式字符串本身进行解析,也未在调用此类函数时根据格式字符串验证参数。

    对于 Objective-C 方言,在相同上下文中也会识别 NSString(或__NSString__)。包含这些格式属性的声明会进行语法正确性解析,但针对此类格式字符串的检查结果尚未定义,且当前版本的编译器也不会执行此类检查。

    参数 string-index 指定哪个参数是格式字符串参数(从 1 开始计数),而 first-to-check则是要根据格式字符串进行检查的第一个参数的编号。对于无法检查参数的函数(例如 vprintf),应将第三个参数指定为 0。在这种情况下,编译器仅检查格式字符串的一致性。对于 strftime 格式,第三个参数必须为 0。由于非静态 C++ 成员函数具有隐式的 this 参数,因此在指定 string-index 和 first-to-check 的值时,应从 2 而不是 1 开始计数。

    在上述示例中,格式字符串(my_format)是函数 my_print 的第二个参数,而需要检查的参数从第三个参数开始,因此 format 属性的正确参数值应为 2 和 3。

    format 属性允许你标识那些接受格式字符串作为参数的自定义函数,以便 GCC 能够检查对这些函数的调用是否存在错误。只要启用了格式检查警告(使用 -Wformat 选项),编译器总会(除非使用了 -ffreestanding 或 -fno-builtin)对标准库函数 printffprintfsprintfscanffscanfsscanfstrftimevprintfvfprintf 和 vsprintf 进行格式检查,因此无需修改头文件 stdio.h。在 C99 模式下,snprintf, vsnprintf, vscanf, vfscanf  和 vsscanf 函数也会受到检查。除严格符合 C 标准的模式外,X/Open 函数 strfmon 以及 printf_unlocked 和 fprintf_unlocked 也会受到检查。请参阅“控制 C 语言方言的选项”一节。

2.5.29  format_arg(string-index)

    该属性适用于函数。

    该属性指定函数接收一个或多个用于 printf,scanf,strftime 或 strfmon 类函数的格式字符串,并对其进行修改(例如将其编译为另一种语言),以便将结果传递给 printf,scanf,strftime 或 strfmon 类函数(此时,传递给该格式化函数的其余参数与未修改字符串时相同)。同一个函数可以应用多个 format_arg 属性,每个属性指定不同的参数作为格式字符串。例如,以下声明:

extern char *

my_dgettext (char *my_domain, const char *my_format)

      __attribute__ ((format_arg (2)));

该属性指示编译器检查对 printf ,scanf, strftime 或 strfmon 类函数的调用,若其格式字符串参数实际上是对 my_dgettext 函数的调用,则检查该调用是否与 my_format 格式字符串参数一致。如果未指定 format_arg 属性,编译器在遇到此类格式化函数调用时,仅能识别出格式字符串参数并非常量;这虽会在使用 -Wformat-nonliteral 选项时触发警告,但若无该属性,编译器便无法对调用进行实际检查。

    对于声明了多个 format_arg 属性(且每个属性指定不同的参数索引)的函数,编译器会针对这些属性所指定的所有格式字符串,对相应的实际函数参数进行检查。此功能旨在支持 GNU ngettext 系列函数。

    参数 string-index 用于指定哪个参数是格式字符串参数(索引从 1 开始计数)。由于非静态 C++ 成员函数包含一个隐式的 this 参数,因此此类方法的参数计数应从 2 开始

    format_arg 属性允许你标记那些会修改格式字符串的自定义函数,从而使 GCC 能够检查对 printf, scanf, strftime 或 strfmon 类函数的调用——前提是这些函数的参数中包含对你自定义函数的调用。编译器始终以这种方式处理 gettext,dgettext 和 dcgettext,除非使用了 -ansi 或相应的 -std 选项要求严格支持 ISO C 标准,或者使用了 -ffreestanding 或 -fno-builtin 选项。请参阅“控制 C 语言方言的选项”一节。

    对于 Objective-C 方言,format_arg 属性可以指向 NSString 引用,以保持与前述 format 属性的兼容性。

    同样,在 Darwin 目标平台上,CFStringRef(由 CoreFoundation 头文件定义)也可作为格式参数使用。请注意,相关头文件通常仅在 Darwin (macOS) 系统中可用。在此类系统中,Xcode 和系统文档提供了关于 CFString ,CFStringRef 及相关函数的详细说明。

2.5.30  gnu_inline

    该属性适用于函数。

    它应与同样使用 inline 关键字声明的函数配合使用。它指示 GCC 即使在 C99 或 gnu99 模式下编译时,也将该函数视为在 gnu90 模式下定义的那样进行处理。

    如果该函数被声明为 extern,则该函数定义仅用于内联。在任何情况下,该函数都不会被编译为独立函数,即使显式获取其地址也是如此。此类地址将成为外部引用,就像你仅声明了该函数而未定义它一样。这几乎起到了宏的作用。使用方法是:将带有此属性的函数定义放在头文件中,并将不带 extern 的函数副本放在库文件中。头文件中的定义使得对该函数的大多数调用都能被内联。如果仍有未内联的函数调用,它们将引用库中的那个副本。请注意,这两个函数定义不必完全相同,尽管如果它们的效果不一致,程序可能会出现异常行为。

    在 C 语言中,如果函数既不是 extern 也不是 static,则该函数既会被编译为独立函数,也会在可能的情况下进行内联。

    这是 GCC 传统上处理 inline 声明函数的方式。由于 ISO C99 为 inline 规定了不同的语义,因此提供此函数属性既作为一种过渡措施,也是一项有用的独立功能。此属性在 GCC 4.1.3 及更高版本中可用。当预处理器宏 __GNUC_GNU_INLINE__ 或 __GNUC_STDC_INLINE__ 中任意一个被定义时,即可使用此属性。请参阅“内联函数与宏一样快”一节。

    在 C++ 中,此属性不依赖于 extern,但仍需要 inline 关键字来启用其特殊行为。

2.5.31  hard_bool, hard_bool(false_value), hard_bool(false_value,true_value)

    该属性适用于 C 中的整数类型。

hardbool 属性引入了强化布尔类型(hardened boolean types)。它将整型转换为一种具有相同大小和精度的类布尔类型,并使用指定的值来表示false和true。在底层实现上,它实际上是一种enum类型,但其表现出的行为与 _Bool 类似,唯一的区别在于其内部表示受到严格限制,并由运行时检查进行验证。

    如果省略了 true_value,则使用 false_value 的按位取反值;如果省略了 false_value,则使用零。指定的表示值在转换为原始整型时必须互不相同;如果表示值变得无法区分,则较窄的位域(bitfield)定义会被拒绝。

    此类类型的值会自动转换为 _Bool 类型,此时,所选的表示值会被映射为相应的 _Bool 值。若在编译时无法确定其表示值是 false_value 还是 true_value,则在运行时进行校验:若该值既非 false_value 也非 true_value,程序将调用 __builtin_trap。正是这一特性使其成为“加固型布尔类型”(hardened boolean types)。

当标量类型隐式或显式转换为此类加固型布尔类型时,其行为相当于先转换为 _Bool,随后将 false 和 true 分别映射为 false_value 和 true_value。

typedef char __attribute__ ((__hardbool__ (0x5a))) hbool;

hbool first = 0;       /* False, stored as (char)0x5a.  */

hbool second = !first; /* True, stored as ~(char)0x5a.  */

static hbool zeroinit; /* False, stored as (char)0x5a.  */

auto hbool uninit;     /* Undefined, may trap.  */

当对“加固布尔类型”(hardened boolean type,通常位于静态存储区)的变量或字段进行零初始化时,隐含的零初始化值会先转换为 _Bool 类型,再转换为该加固布尔类型,从而确保初始值即为表示false的加固形式。使用该值是定义良好的行为。然而,若此类类型的变量或字段未被初始化(通常位于自动或动态存储区),情况则不同:它们的值是不确定的,使用它们会引发未定义行为。是否触发陷阱取决于存储该变量的内存区域中实际包含的位模式,同时也取决于编译器可能进行的优化——编译器往往基于“使用未初始化值属于未定义行为”这一前提进行优化。

    使用 -ftrivial-auto-var-init 选项的用户应注意,该选项所使用的初始化位模式并不会转换为加固布尔类型;因此,如果为false和true所选定的内部表示值与该选项使用的初始化位模式不匹配,那么使用由 -ftrivial-auto-var-init 隐式初始化的加固布尔变量时,可能会触发陷阱。

由于这是一项仅适用于 C 语言的语言扩展,因此与其他语言的互操作可能会存在困难。该类型应当能够与满足以下条件的类型互操作:大小相同且具有等效表示规范(representation clauses)的 Ada 布尔类型,以及与 C 语言所选底层整型相对应的enum类型或其他语言的整型。

2.5.32  ifunc(“resolver”)

    该属性适用于函数。

    ifunc 属性利用 ELF 标准中 STT_GNU_IFUNC 符号类型扩展,将函数标记为间接函数(indirect function)。这使得符号值的解析能够在加载时动态进行,并能根据当时确定的处理器或其他系统特性,选择该例程的优化版本。要使用此属性,首先需要定义可用的实现函数,以及一个返回所选实现函数指针的解析器函数(resolver function)。实现函数的声明必须与所实现函数的 API 保持一致。解析器函数应声明为不带参数,且返回一个指向与实现函数类型相同的函数的指针。例如:

void *my_memcpy (void *dst, const void *src, size_t len)

{

  …

  return dst;

}

static void * (*resolve_memcpy (void))(void *, const void *, size_t)

{

  return my_memcpy; // we will just always select this routine

}

声明用户所调用函数的导出头文件将包含:

extern void *memcpy (void *, const void *, size_t);

允许用户像调用普通函数一样调用 memcpy,而无需了解其具体实现。最后,间接函数必须与解析器函数定义在同一个编译单元中:

void *memcpy (void *, const void *, size_t)

     __attribute__ ((ifunc ("resolve_memcpy")));

在 C++ 中,ifunc 属性接受一个字符串作为参数,该字符串即解析器函数的修饰名(mangled name)。针对类 C 的非静态成员函数的 C++ 解析器,其声明应返回一个指向非成员函数的指针;该非成员函数的第一个参数是指向 C 的指针,后续参数则与实现函数的参数一致。G++ 会检查这两个函数的签名,若不匹配,则会发出 -Wattribute-alias 警告。若需消除因将实现成员函数的指针转换为相应非成员函数类型而产生的警告,可使用 -Wno-pmf-conversions 选项。例如:

class S

{

private:

  int debug_impl (int);

  int optimized_impl (int);

  typedef int Func (S*, int);

  static Func* resolver ();

public:

  int interface (int);

};

int S::debug_impl (int) { /* */ }

int S::optimized_impl (int) { /* */ }

S::Func* S::resolver ()

{

  int (S::*pimpl) (int)

    = getenv ("DEBUG") ? &S::debug_impl : &S::optimized_impl;

  // Cast triggers -Wno-pmf-conversions.

  return reinterpret_cast<Func*>(pimpl);

}

int S::interface (int) __attribute__ ((ifunc ("_ZN1S8resolverEv")));

间接函数不能是弱符号。使用此功能需要 Binutils 2.20.1 或更高版本以及 GNU C 库 2.11.1 版本。

2.5.33  interrupt, interrupt_handler

    这些属性适用与函数。

    许多 GCC 后端都支持通过属性来标识中断处理函数,这会指示编译器生成与普通函数不同的函数入口和出口序列。具体的语法和行为因目标架构而异;详情请参阅后续小节。

2.5.34  leaf(叶函数属性)

  该属性适用于函数。

    带有此属性的外部函数调用,必须仅通过 return 语句或异常处理机制返回到当前的编译单元。具体而言,叶函数(leaf function)不允许调用由当前编译单元传入的回调函数、直接调用该单元导出的函数,或通过 longjmp 跳转回该单元。叶函数仍可调用其他编译单元中的函数,因此,这里的“叶”并不意味着函数内部完全不包含任何函数调用。

    该属性旨在供库函数使用,以改进数据流分析。编译器会据此推断:任何未逃逸出当前编译单元的数据,均不会被该叶函数使用或修改。例如,sin 函数属于叶函数,而 qsort 则不然。

    需要注意的是,叶函数可能会间接触发当前编译单元中定义的、且使用了静态变量的信号处理程序(signal handler)。同样,在启用延迟符号解析(lazy symbol resolution)的情况下,叶函数可能会调用间接函数,而这些函数的解析器函数或实现函数恰好定义在当前编译单元中并使用了静态变量。目前尚无符合标准的方式来编写此类信号处理程序、解析器函数或实现函数;最佳应对方案是移除 leaf 属性,或者将所有相关静态变量标记为 volatile。最后,对于支持符号插入(symbol interposition)的 ELF 系统,务必确保当前编译单元中定义的函数不会根据所选的标准模式及特性测试宏(feature test macros),意外地覆盖(interpose)其他符号;否则可能会引入非预期的回调。

    该属性对定义在当前编译单元内部的函数无效。这样设计是为了便于将多个编译单元合并为一个(例如通过链接时优化)。正因如此,该属性不能用于标注间接调用的类型。

2.5.35  malloc, malloc(deallocator), malloc(deallocator,pre-index)

    该属性适用于函数。

malloc 属性用于标识某个函数具有类似 malloc 的行为:即该函数返回的指针 P 不会与函数返回时有效的任何其他指针发生别名重叠(alias),且 P 所指向的存储区域内不包含指向有效对象的指针。此外,GCC 还会据此推断,带有该属性的函数在大多数情况下会返回非空指针。

    该属性还支持带有一个或两个参数的形式,用于指定某个函数为“释放函数”(deallocator),专门负责释放由该类 malloc 风格函数返回的指针。其中的 ptr-index 参数指定了释放函数接收指针的位置索引;当把该指针传入释放函数的对应位置时,即会执行释放操作。

    不带参数使用该属性时,编译器会利用其隐含的无别名(non-aliasing)特性来优化代码。像 malloc 和 calloc 这样的函数具备此特性,因为它们返回的是指向新获取的、未初始化或已清零存储区域的指针。相比之下,realloc 等函数则不具备此特性,因为它们返回的指针可能指向包含指向现有对象之指针的存储区域。此外,由于此类函数通常极少返回空指针,编译器可基于这一假设对调用方代码进行优化。

    将函数与释放函数关联起来,有助于检测分配与释放函数不匹配的情况,并配合 -Wmismatched-dealloc 等选项进行诊断。此外,利用 -Wfree-nonheap-object 选项,还能诊断出试图释放非动态分配对象的行为。若要同时表明某分配函数既满足无别名特性又关联了特定的释放函数,则必须同时使用该属性的简单形式(无参数)和带释放函数参数的形式。同一个函数既可以是分配函数,也可以是释放函数。由于仅内联其中一个关联函数而未内联另一个可能导致表面上的不匹配,因此 inline 函数不支持这种形式的 malloc 属性。出于同样的原因,使用该属性会阻止分配函数和释放函数被内联展开。

    例如,以下声明不仅指明了这些函数返回的指针不与其他指针发生别名重叠,还指定 fclose 是除 popen 以外所有函数所返回指针的合适释放函数,而 pclose 则是 popen 所返回指针的唯一合适释放函数。在属性中引用释放函数之前,必须先声明这些函数。

int fclose (FILE*);

int pclose (FILE*);

[[gnu::malloc, gnu::malloc (fclose, 1)]]

  FILE* fdopen (int, const char*);

[[gnu::malloc, gnu::malloc (fclose, 1)]]

  FILE* fopen (const char*, const char*);

[[gnu::malloc, gnu::malloc (fclose, 1)]]

  FILE* fmemopen(void *, size_t, const char *);

[[gnu::malloc, gnu::malloc (fclose, 1)]]

  FILE* popen (const char*, const char*);

[[gnu::malloc, gnu::malloc (fclose, 1)]]

  FILE* tmpfile (void);

由 -fanalyzer 启用的警告会遵循标记为 malloc 的分配与释放配对。具体而言:

●  如果存在某条执行路径,使得内存分配调用的结果被传递给了一个不匹配的释放函数,分析器就会发出 -Wanalyzer-mismatching-deallocation 诊断信息。

●  如果存在某条执行路径,使得同一个值被多次传递给释放函数,分析器就会发出 -Wanalyzer-double-free 诊断信息。

●  分析器会考虑到内存分配函数可能失败并返回空指针(null)的情况。如果存在执行路径,使得未经检查的分配调用结果被解引用,或者被传递给要求非空参数的函数,分析器就会发出 -Wanalyzer-possible-null-dereference 和 -Wanalyzer-possible-null-argument 诊断信息。如果分配函数总是返回非空指针,可以使用 __attribute__ ((returns_nonnull)) 来抑制这些警告。例如:

char *xstrdup (const char *)

  __attribute__((malloc (free), returns_nonnull));

●   如果存在某条执行路径,使得通过指针传递给释放函数的内存块在释放后仍被使用,分析器会发出 -Wanalyzer-use-after-free 诊断信息。

●   如果存在某条执行路径,使得内存分配函数的返回值发生泄漏(即未传递给释放函数),分析器会发出 -Wanalyzer-malloc-leak 诊断信息。

●   如果对全局变量或栈变量调用了释放函数,分析器会发出 -Wanalyzer-free-of-non-heap 诊断信息。

该分析器假定释放函数能够妥善处理空指针。如果实际情况并非如此,可以为该释放函数添加 __attribute__((nonnull)) 标记;这样,当代码路径中出现向该释放函数传入空指针的情况时,-fanalyzer 就会发出 -Wanalyzer-possible-null-argument 诊断信息。

2.5.36  may_alias

    may_alias 属性适用于指针声明类型。

    通过具有此属性类型的指针进行的访问,不遵循基于类型的别名分析(type-based alias analysis)规则;相反,系统会假定这些指针能够与任何其他类型的对象发生别名关系(即指向同一内存位置)。根据 C99 标准第 6.5 节第 7 段的规定,对这类指针进行解引用的左值表达式,其处理方式被视为具有字符类型(character type)。有关别名问题的更多信息,请参阅 -fstrict-aliasing 选项的说明。引入此扩展是为了支持某些向量 API,这些 API 允许指向某种向量类型的指针与指向另一种向量类型的指针发生别名关系。

    请注意,具有此属性类型的对象不具备任何特殊语义。

    例如:

typedef short __attribute__ ((__may_alias__)) short_a;

int

main (void)

{

  int a = 0x12345678;

  short_a *b = (short_a *) &a;

  b[1] = 0;

  if (a == 0x12345678)

    abort();

  exit(0);

}

如果在变量声明中将 short_a 替换为 short,那么在使用 -fstrict-aliasing 选项(该选项在 -O2 或更高优化级别下默认开启)进行编译时,上述程序将会终止运行。

2.5.37  mode(mode)

    该属性可适用于变量或类型声明。

    它指定声明的数据类型——与模式 mode 对应的类型。这实际上允许你根据其宽度请求整数或浮点类型。

    有关模式的可用关键字列表,请参阅 GNU 编译器集合 (GCC) 内部文档中的“机器模式”部分。你还可以指定 byte 或 __byte__ 模式来表示对应于单字节整数的模式,word 或 __word__ 模式来表示对应于单字整数的模式,以及 pointer 或 __pointer__ 模式来表示用于表示指针的模式。

2.5.38  musttail

    此属性可应用于包含函数调用作为返回值的 return 语句。

    它要求该调用必须是不会分配额外栈空间的尾调用,因此使用尾递归来实现长时间运行的循环是安全的。

[[gnu::musttail]] return foo();

__attribute__((musttail)) return bar();

如果编译器无法生成 musttail 尾调用,则会报错。在某些目标平台上,可能根本不支持尾调用。musttail 属性声明:自动变量、函数参数和临时对象(除非具有非平凡析构)的生命周期可以在实际调用指令之前结束,且在被调用函数内部访问这些对象将被视为未定义行为。启用 -O1 或 -O2 优化可以提高尾调用生成的成功率。

int foo (int *);

void bar (int *);

struct S { S (); ~S (); int s; };

int

baz (int *x)

{

  if (*x == 1)

    {

      int a = 42;

      /* The call is a tail call (would not be without the

         attribute).  Dereferencing the pointer in the callee is

         undefined behavior, and there is a warning emitted

         for this by default (-Wmusttail-local-addr).  */

      [[gnu::musttail]] return foo (&a);

    }

  else if (*x == 2)

    {

      int a = 42;

      bar (&a);

      /* The call is a tail call (would not be without the

         attribute).  If bar stores the pointer anywhere, dereferencing

         it in foo is undefined behavior.  There is a warning

         emitted for this with -Wextra, which implies

         -Wmaybe-musttail-local-addr.  */

      [[gnu::musttail]] return foo (nullptr);

    }

  else

    {

      S s;

      /* The s variable requires non-trivial destruction which ought

         to be performed after the foo call returns, so this is

         rejected.  */

      [[gnu::musttail]] return foo (&s.s);

    }

}

为了避免上述 *x == 2 场景及类似代码中出现的 -Wmaybe-musttail-local-addr 警告,如果条件允许,可以考虑在一个独立的作用域内定义那些可能发生逃逸(maybe-escaped)的变量,并确保该作用域在 return 语句之前结束;这样可以明确表明该变量在函数调用期间已不再活跃。即:

else if (*x == 2)

    {

      {

        int a = 42;

        bar (&a);

      }

      /* The call is a tail call (would not be without the

         attribute).  If bar stores the pointer anywhere, dereferencing

         it in foo is undefined behavior even without tail call

         optimization, and there is no warning.  */

      [[gnu::musttail]] return foo (nullptr);

    }

如果那个可能发生逃逸的变量是函数参数,就无法通过这种方式避免警告,因为参数在整个函数的作用域内都有效。

2.5.39  naked

    该属性适用于函数。

    它允许编译器构建必要的函数声明,同时允许函数体采用汇编代码编写。对于此类函数,编译器不会生成函数序言(prologue)或尾声(epilogue)代码序列。在 naked 函数中,仅可安全使用基本汇编语句(参见“Basic Asm — 无操作数的汇编指令”)尽管使用扩展汇编或混合使用基本汇编与 C 代码看似可行,但这种做法无法保证可靠运行,且不受官方支持。

    并非所有目标架构都支持此属性。支持该属性的架构包括 ARC, ARM, AVR, BPF, C-SKY, MCORE, MSP430, NDS32, RISC-V, RL78, RX, 和 x86。

2.5.40  no_icf

    该属性适用于函数或变量。

    它防止该实体与另一个语义等价的函数或变量合并。

2.5.41  no_instrument_function(无插桩代码函数)

         该属性适用于函数。

    如果指定了 -finstrument-functions,-p 或 -pg 中的任意一个,编译器会在大多数用户编译的函数的入口和出口处生成用于性能分析的代码;但带有此属性的函数则不会被插入此类代码。

2.5.42  no_profile_instrument_function(无性能插桩代码函数)

该属性适用于函数。

    no_profile_instrument_function 属性告知编译器,不应对该函数进行基于性能反馈优化(PGO- profile-feedback-based optimization code)的代码插桩处理。

2.5.43  no_reorder

该属性适用于函数或变量。

         标记为 no_reorder 的函数或变量,在可执行文件中不会相对于彼此或顶层汇编语句进行重排序。它们在程序中的实际顺序取决于链接器的命令行参数。这与 -fno-toplevel-reorder 选项的效果类似,但仅适用于被标记的符号。

2.5.44  no_sanitize ("sanitize_option")

    该属性适用于函数。

    no_sanitize 属性告知编译器,不要对 sanitize_option 中指定的任何选项执行净化(sanitization)操作。该属性接受 -fsanitize 选项所支持的值列表。

void __attribute__ ((no_sanitize ("alignment", "object-size")))

f () { /* Do something. */; }

void __attribute__ ((no_sanitize ("alignment,object-size")))

g () { /* Do something. */; }

2.5.45  no_sanitize_address, no_address_safety_analysis

         该属性适用于函数。

         no_sanitize_address 属性告知编译器,在使用 -fsanitize=address 选项进行编译时,不应对该函数中的内存访问操作进行插桩。

    no_address_safety_analysis 是 no_sanitize_address 属性的一个已弃用别名;新代码应使用 no_sanitize_address。

2.5.46  no_sanitize_coverage

    该属性适用于函数。

    no_sanitize_coverage 属性告知编译器不要进行覆盖率引导的模糊测试代码插桩(即不应用 -fsanitize-coverage)。

2.5.47  no_sanitize_thread

    该属性适用于函数。

    no_sanitize_thread 属性通知编译器在使用 -fsanitize=thread 选项进行编译时不应在函数中检测内存访问。

2.5.48  no_sanitize_undefined

    该属性适用于函数。

no_sanitize_undefined 属性告知编译器,在使用 -fsanitize=undefined 选项进行编译时,不应对该函数中的未定义行为进行检查。

2.5.49  no_split_stack

    该属性适用于函数。

    如果指定了 -fsplit-stack 选项,函数会包含一段用于决定是否拆分栈的小型序言代码(prologue)。带有 no_split_stack 属性的函数则不包含该序言,因此它们可能仅在极有限的栈空间下运行。

2.5.50  no_stack_limit

    该属性适用于函数。

    该属性在局部覆盖了 -fstack-limit-register 和 -fstack-limit-symbol 命令行选项;其作用是禁用所应用函数中的栈限制检查。

2.5.51  no_stack_protector

    该属性适用于函数。

    这会阻止 GCC 为该函数生成栈保护代码。

2.5.52  no_clone

    该属性适用于函数。

该属性阻止函数被纳入克隆(cloning)的考量范围——克隆是一种生成函数专用副本的机制,目前由过程间常量传播(interprocedural constant propagation)来实现。

2.5.53  noinit

    该属性适用于变量。

    带有 noinit 属性的变量不会由 C 运行时启动代码或程序加载器进行初始化。以这种方式不初始化数据可以缩短程序启动时间。

    该属性特定于 ELF 目标,并依赖于链接器脚本将带有 .noinit 前缀的段放置在正确的位置。

2.5.54  noinline

    该属性适用于变量。

    它会阻止函数被内联。它还会禁用一些其他的程序间优化;如果这是你的需求,最好使用更全面的 noipa 属性。

即使函数声明使用了 noinline 属性,除了内联之外,还有其他一些优化可能会导致函数调用被优化掉,即使该函数调用是有效的,只要它没有副作用。为了防止此类调用被优化掉,请添加以下属性:

     asm ("");

2.5.55  noipa

    该属性适用于函数。

    该属性会禁用带有此属性的函数与其调用者之间的过程间优化,其效果就如同在优化调用者时该函数的函数体不可见,而在优化该函数体时调用者也不可见一样。该属性隐含了 noinline,noclone 和 no_icf 属性。不过,该属性并不等同于其他属性的组合,因为它的目的是抑制所有利用过程间分析的现有及未来优化——包括那些尚无对应属性可单独禁用的优化。

2.5.56  nonnull, nonnull(arg-index,…)

    nonnull 属性可以应用于至少接受一个指针类型参数的函数。

    它表示被引用的参数必须是非空指针。例如,以下声明:

extern void *

my_memcpy (void *dest, const void *src, size_t len)

        __attribute__((nonnull (1, 2)));

通知编译器,在调用 my_memcpy 函数时,参数 dest 和 src 必须非空。

    该属性对函数调用和函数定义均有影响。

    对于函数调用:

●   如果编译器检测到在标记为非空(non-null)的参数位置传入了空指针,且启用了 -Wnonnull 选项,则会发出警告。请参阅“请求或抑制警告的选项”。

●   可以指定 -fisolate-erroneous-paths-attribute 选项,让 GCC 将针对非空函数且包含空参数的调用转换为陷阱(trap)。请参阅“控制优化的选项”。

●   编译器也可能利用“某些函数参数不能为空”这一信息进行优化。这些优化可以通过 -fno-delete-null-pointer-checks 选项禁用。请参阅“控制优化的选项”。

对于函数定义:

●   如果编译器检测到标记为 nonnull 的函数参数与 null 进行了比较,且启用了 -Wnonnull-compare 选项,则会发出警告。请参阅“请求或抑制警告的选项”一节。

●   编译器还可能利用“nonnull 参数不为 null”这一信息进行优化。目前,除了移除 nonnull 属性外,无法禁用此行为。

如果未向 nonnull 属性指定参数索引(arg-index),则所有指针参数都会被标记为非空。举例来说,以下声明与前面的示例等价:

extern void *

my_memcpy (void *dest, const void *src, size_t len)

        __attribute__((nonnull));

2.5.57  nonnull_if_nonzero(arg-index, arg2-index), nonnull_if_nonzero(arg-index, arg2-index, arg3-index)

    此属性可应用于至少接受一个指针类型参数的函数。

    nonnull_if_nonzero 属性是 nonnull 属性的一种条件变体。它接受两个或三个参数:第一个参数指定一个指针类型参数的索引(该指针在某些情况下不得为空),第二个参数指定一个整型参数(非布尔型)的索引。若该整型参数为零,则对应的指针参数允许为空;若该整型参数非零,则指针参数不得为空。若提供了三个参数,则第三个参数指定另一个整型参数(非布尔型)的索引;此时,只要其中任一整型参数为零,指针参数便允许为空;若两个整型参数均非零,则指针参数不得为空。

[[gnu::nonnull (1, 2)]]

extern void *

my_memcpy (void *dest, const void *src, size_t len);

[[gnu::nonnull_if_nonzero (1, 3),

  gnu::nonnull_if_nonzero (2, 3)]]

extern void *

my_memcpy2 (void *dest, const void *src, size_t len);

[[gnu::nonnull (4),

  gnu::nonnull_if_nonzero (1, 2, 3)]]

extern size_t

my_fread (void *buf, size_t size, size_t count, FILE *stream);

根据这些声明,调用 my_memcpy(NULL, NULL, 0),my_memcpy2(NULL, NULL, 4),my_fread(buf, 0, 0, NULL) 或 my_fread(NULL, 1, 1, stream) 是无效的;但调用 my_memcpy2(NULL, NULL, 0),my_fread(NULL, 0, 0, stream),my_fread(NULL, 0, 1, stream) 或 my_fread(NULL, 1, 0, stream) 则是有效的。该属性应应用于那些针对零尺寸(zero sizes)设有特殊情况(即允许传入 NULL)的声明。

2.5.58  nonstring

    此属性适用于变量,或struct、union或类中的成员,若其类型为 char,signed char 或 unsigned char 类型的数组,或是指向此类类型的指针。

    nonstring 属性用于指定某对象或该类型的成员旨在存储字符数组,而这些数组未必包含终止符 NUL。该属性有助于检测此类数组或指针在预期 NUL 结尾字符串的函数中的使用情况,并避免在将此类数组或指针作为参数传递给诸如 strncpy 等有长度限制的字符串操作函数时触发警告。例如,若不使用该属性,GCC 会针对下文中的 strncpy 调用发出警告,因为该操作可能会在不追加终止符 NUL 的情况下截断复制内容;而使用该属性则可以抑制此类警告。不过,当数组声明了该属性时,对 strlen 的调用会触发诊断提示,因为若数组中不包含 NUL 结尾的字符串,该调用行为即为未定义。若需复制、比较或搜索非字符串字符数组,应使用 memcpy,memcmp,memchr 等针对字节数组操作的函数。此外,只要指定了合适的长度限制,对这些数组调用 strnlen 和 strndup 也是安全的,且不会触发诊断提示。

struct Data

{

  char name [32] __attribute__ ((nonstring));

};

int f (struct Data *pd, const char *s)

{

  strncpy (pd->name, s, sizeof pd->name);

  …

  return strlen (pd->name);   // unsafe, gets a warning

}

2.5.59  noplt

    该属性适用于函数。

    noplt 属性对应于 -fno-plt 选项。在位置无关代码中,对标有该属性的函数的调用不会使用 PLT。

/* Externally defined function foo.  */

[[gnu::noplt]] int foo ();

int

main (/* … */)

{

  /* … */

  foo ();

  /* … */

}

函数 foo 的 noplt 属性告诉编译器假定函数 foo 是外部定义的,并且在位置无关代码中,对 foo 的调用必须避免使用位置逻辑表 (PLT)。

    在位置相关代码中,一些目标还会将标记为不使用 PLT 的函数调用转换为使用全局逻辑表 (GOT)。

2.5.60  noreturn

    该属性适用于函数。

    某些标准库函数(如 abort 和 exit)无法返回,GCC 会自动识别这一点。有些程序会定义自己的、永不返回的函数;你可以通过将它们声明为 noreturn 来告知编译器这一特性。例如:

void fatal () __attribute__ ((noreturn));

void

fatal (/* … */)

{

  /* … */ /* Print error message. */ /* … */

  exit (1);

}

noreturn 关键字告诉编译器假定 fatal 函数不会返回。这样,编译器就可以在不考虑 fatal 函数实际返回时会发生什么的情况下进行优化。这可以生成性能略微提升的代码。更重要的是,它有助于避免出现未初始化变量的虚假警告。

    noreturn 关键字不会影响异常处理路径(如果适用):标记为 noreturn 的函数仍然可以通过抛出异常或调用 longjmp 返回给调用者。

    为了保留回溯信息,GCC 永远不会将对 noreturn 函数的调用转换为尾调用。    不要假定调用函数保存的寄存器在调用 noreturn 函数之前会被恢复。noreturn 函数的返回类型不应该是 void 以外的类型。

2.5.61  nothrow

    该属生适用于函数。

    nothrow 属性用于告知编译器某个函数不会抛出异常。例如,标准 C 库中的大多数函数都可以保证不抛出异常,但也有显著的例外情况,即接收函数指针作为参数的 qsort 和 bsearch 函数。

2.5.62  null_terminated_string_arg, null_terminated_string_arg(N)

    null_terminated_string_arg 属性可以应用于以 char * 或 const char * 类型作为参数的函数。

    它表示传递的参数必须是 C 风格的以空字符结尾的字符串。具体来说,该属性的存在意味着,如果指针非空,则函数可能会扫描引用的缓冲区以查找第一个零字节。

    特别地,当启用分析器(通过 -fanalyzer)时,如果指针非空,它将模拟扫描引用缓冲区中的第一个零字节,并可能在缓冲区未正确终止时发出 -Wanalyzer-use-of-uninitialized-value 或 -Wanalyzer-out-of-bounds 错误。

    例如,给定以下代码:

char *example_1 (const char *p)

  __attribute__((null_terminated_string_arg (1)));

分析器会检查传递给函数的任何非空指针是否有效终止。如果参数必须非空,则应同时使用 this 属性和 nonnull 属性,例如:

extern char *example_2 (const char *p)

  __attribute__((null_terminated_string_arg (1),

                 nonnull (1)));

请参阅 nonnull 属性以获取更多信息和注意事项。如果指针参数也被函数上的 access 属性引用,且 access-mode 为 read_only 或 read_write,并且后者具有指向 size 参数的可选 size-index 参数,则此参数表示访问的最大大小。例如:

[[gnu::null_terminated_string_arg (1),

  gnu::access (read_only, 1, 2),

  gnu::nonnull (1)]]

extern char *example_fn (const char *p, size_t n);

该分析器要求第一个参数非空,且要么以有效的空字符(null terminator)结尾,要么在第二个参数指定的长度范围内可有效读取。

2.5.63  objc_nullability(nullability kind)( 仅 Objective-C 和 Objective-C++ )

    该属性仅适用于指针变量。

该机制允许为指针标记四种可能值之一,用以描述指针可能为 nil(空)的具体条件。在大多数情况下,该属性旨在作为属性和方法可空性(由语言关键字指定)的内部表示形式;不建议直接使用它。

    当可空性类型为unspecified或 0 时,关于指针在何种条件下可能为 nil 的信息是未知的。明确这一状态有助于避免诊断分析中出现误报。

    当可空性类型为nonnull或 1 时,若指针为 nil 则无意义;因此,如果编译器确定该值将为 nil,便可发出诊断提示。

当可空性类型为nullable或 2 时,指针可能为 nil,且此时该 nil 值本身具有特定含义。

当可空性类型为“可重置”(resettable)或 3 时(仅用于属性属性列表的上下文中),这描述了这样一种情况:属性的 setter(设置器)可以接受 nil 值(这可能会导致属性以某种方式重置为默认值),但属性的 getter(获取器)绝不会返回有效的 nil 值。

2.5.64  objc_root_class( 仅 Objective-C 和 Objective-C++ )

    该属性仅适用于  Objective-C 和 Objective-C++ 类。这会将该类标记为根类,从而允许编译器忽略关于缺少父类的警告,并根据需要对必须实现的方法进行额外检查。

2.5.65  optimize (level, …), optimize (string, …)

    该属性适应于函数。

optimize 属性用于指定函数应使用与命令行参数不同的优化选项进行编译。该属性的参数其作用效果等同于将它们追加到命令行参数之后。

    有效的参数包括非负整数常量和字符串。每个数值参数指定一个优化级别。每个字符串参数由一个或多个以逗号分隔的子字符串组成。以字母O开头的子字符串代表优化选项(例如 -O0 或 -Os);其他子字符串则被视为 -f 前缀的后缀,共同构成一个完整的优化选项名称(请参阅“控制优化的选项”一节)。

    可以使用 #pragma GCC optimize 为多个函数设置优化选项。有关该 pragma 指令的详细信息,请参阅“特定于函数的选项 Pragma”一节。

    通过逗号分隔提供多个字符串参数来指定多个选项,其效果等同于在单个字符串内用逗号(,) 分隔选项后缀。字符串内部不允许包含空格。

    并非该属性指定的所有以 -f 为前缀的优化选项都会对函数产生实际影响。optimize 属性仅应在调试时使用,不适合用于生产环境的代码中。

2.5.66  packed

         此属性可以附加到struct、union或 C++ class定义、成员或enum定义。struct成员的 packed 属性指定该成员应采用尽可能小的对齐方式——位域为一位,其他情况为一个字节,除非使用 aligned 属性指定了更大的值。此属性不适用于非成员对象。

    例如,在下面的struct中,成员数组 x 被压缩,使其紧跟在 <a> 标签之后,中间没有任何填充:

struct foo

{

  char a;

  int x[2] __attribute__ ((packed));

};

注意:GCC 4.1、4.2 和 4.3 系列会忽略 char 类型位域的 packed 属性。GCC 4.4 修复了此问题,但此更改可能会导致struct布局的差异。有关更多信息,请参阅 -Wpacked-bitfield-compat 的文档。

    应用于整个struct、union或 C++ class定义时,它指定其每个成员(零宽度位域除外)的放置方式应尽可能减少所需的内存。这相当于为每个成员指定 packed 属性。

    在以下示例中,struct my_packed_struct 的成员紧密排列,但其 s 成员的内部布局并未进行压缩——要实现这一点,还需要对struct my_unpacked_struct 进行压缩。

struct my_unpacked_struct

{

  char c;

  int i;

};

struct [[gnu::__packed__]] my_packed_struct

{

  char c;

  int  i;

  struct my_unpacked_struct s;

  };

当应用于enum时,该属性指示使用能容纳该枚举值的最小整数类型来表示该类型。在命令行中指定 -fshort-enums 标志,其效果等同于为所有枚举定义指定 packed 属性。

    packed 属性只能在定义enum、struct、union或class时指定;若仅使用 typedef 而未同时定义相应的enum、struct、union或class,则不能指定该属性。

2.5.67  patchable_function_entry

    该属性适用于函数。

    如果目标程序的代码段(text segment)能够在运行时被设为可写,那么在函数入口处填充若干 NOP 指令,便可作为一种通用的插桩(instrumentation)手段。

    利用 patchable_function_entry 函数属性,可以将 NOP 指令的数量设定为任意期望值。该属性支持双参数语法,与命令行选项 -fpatchable-function-entry=N,M 相同:它会生成 N 条 NOP 指令,并将函数入口点置于第 M 条 NOP 指令之前。若省略参数 M,则默认值为 0(即函数入口点位于第一条 NOP 指令之前)。

    如果通过命令行选项 -fpatchable-function-entry=N,M 全局启用了可修补函数入口(patchable function entries)功能,那么对于属于插桩框架自身的函数,必须使用 patchable_function_entry(0) 属性来禁用插桩,以防止发生递归调用。

2.5.68  persistent

    此属性适用于变量。

    任何带有 persistent 属性的变量都不会由 C 运行时启动代码初始化,而是由程序加载器初始化。这使得变量的值能够在处理器复位后保持不变。

    此属性特有于 ELF 目标,并且依赖于链接器脚本将带有 .persistent 前缀的段放置在正确的位置。具体来说,需要某种类型的非易失性可写内存。

2.5.69  pure

    该属性适用于函数。

    那些除了返回一个值之外不会对程序状态产生任何可观察影响的函数调用,可能适合进行诸如公共子表达式消除之类的优化。使用 pure 属性声明此类函数,可以让 GCC 避免在重复调用该函数时使用相同的参数值而产生额外的调用。

    pure 属性禁止函数修改除检查函数返回值之外的、可通过其他方式观察到的程序状态。但是,使用 pure 属性声明的函数可以安全地读取任何非易失性对象,并以不影响其返回值或程序可观察状态的方式修改对象的值。

    例如:

int hash (char *) __attribute__ ((pure));

这会告知 GCC:如果 hash 函数所能观测到的程序状态(包括数组本身的内容)在两次调用之间未发生变化,那么后续使用相同字符串调用该函数时,可以直接使用第一次调用的结果来替代。尽管 hash 接收一个非 const 指针参数,但它不得修改该指针指向的数组,也不得修改程序其他部分可能依赖其值的任何其他对象。不过,调用者可以在该函数的连续调用之间安全地修改数组内容(这样做会使该优化失效)。此限制同样适用于 C++ 非静态成员函数中通过 this 指针引用的成员对象。

    strlen 或 memcmp 是“纯函数”(pure function)的一些常见例子。非纯函数的典型例子包括:包含无限循环的函数,或者依赖于易变(volatile)内存或其他系统资源(其值可能在连续调用之间发生变化)的函数(例如多线程环境下的标准 C 库函数 feof)。

    与 const 属性相比,pure 属性对函数定义的限制相似但较为宽松:pure 允许函数读取任何非易变内存,即使该内存的值在函数的连续调用之间发生了变化。如果同时为同一个函数声明 pure 和 const 属性,编译器会发出警告或报错。由于纯函数不能产生任何可观测的副作用,因此返回类型为 void 的纯函数没有意义;若声明此类函数,编译器会发出警告或报错。

2.5.70  reproducible

    该属性适用于函数类型。

    该属性是 C23 标准中 [[reproducible]] 属性的 GNU 对应版本,用于指定指向符合 C23 定义的“无副作用且幂等”( effectless and idempotent)函数的函数指针。

    与标准的 C23 属性不同,该属性也可以在适用于函数声明的属性列表中指定,并且在这种情况下同样作用于该函数的类型。

    对于不包含指针或引用参数(或者虽包含此类参数但不修改其所指向/引用的对象)的“可重现”(reproducible)函数,其特性与具有 pure 属性的函数相似,唯一的区别在于 pure 属性还要求函数必须是有限的(即不会无限循环或陷入死循环)。因此,如果全局状态或通过指针/引用参数可访问的任何内容未被修改,那么具有 pure 属性和 reproducible 属性的函数都可以通过“公共子表达式消除”(common subexpression elimination)进行优化;但只有具有 pure 属性的函数,在结果未被使用或仅被死代码使用时,才能通过“死代码消除”(dead code elimination)进行优化。如果“可重现”函数既没有指针或引用参数,返回类型又是 void,编译器会发出诊断提示,因为此类函数既无法存储结果,也不产生任何可观测的副作用。

    具有指针或引用参数的“可重现”函数,可以通过这些指针、引用或指向指针的引用来存储额外的结果。

2.5.71  retain

    该属性适用于函数和变量。

    对于支持 GNU 或 FreeBSD OSABI 的 ELF 目标,该属性可防止其所修饰的函数或变量被链接器垃圾回收。为实现这一机制,未被指定放入特定段(例如通过 section 属性,或 -ffunction-sections / -fdata-sections 选项)的函数和变量,会被放置在新的、独立的段中。

    此项附加功能需要 Binutils 2.36 或更高版本。

2.5.72  returns_nonnull

    此属性适用于返回指针类型的函数。

    returns_nonnull 属性指定函数的返回值应为非空指针。例如,以下声明:

[[gnu::returns_nonnull]]

extern void *mymalloc (size_t len);

允许编译器基于“返回值绝不会为 null”这一信息对调用方进行优化。

2.5.73  returns_twice

    该属性适用于函数。

    returns_twice 属性告知编译器,某个函数可能会返回多次。在调用此类函数之前,编译器会确保所有寄存器均处于“死”(dead)状态,并针对那些在函数第二次返回后可能被破坏(clobbered)的变量发出警告。setjmp 和 vfork 就是此类函数的典型例子。如果存在与此类函数对应的 longjmp 类函数,则可能需要将其标记为 noreturn 属性。

2.5.74  scalar_storage_order("endianness")

    该属性适用于union 或 struct 。

    该属性用于设定类型中标量字段(以及元素为标量的数组字段)的存储顺序,即字节序(endianness)。支持的字节序包括大端序(big-endian)和小端序(little-endian)。该属性对union、struct或元素为union/struct的数组字段无效;这些字段的标量存储顺序可能与包含它们的类型不同。

    请注意,在此语境下,指针字段和vector字段不被视为标量字段,因此该属性对这些字段没有影响。

    该属性仅在采用统一默认标量存储顺序的目标架构上受支持(幸运的是,大多数架构都属于此类),即所有标量均统一采用大端序或小端序存储的目标架构。

    对于标量存储顺序与目标架构默认顺序相反的类型,还需遵循以下额外限制:

●  对于具有反向标量存储顺序(reverse scalar storage order)的union或struct,获取其标量字段的地址是不允许的,并会导致错误。

●  对于具有反向标量存储顺序的union或struct,获取其数组字段(该数组的元素为标量类型)的地址是允许的,但会产生警告(除非指定了 -Wno-scalar-storage-order 选项)。

●  获取具有反向标量存储顺序的union或struct的地址是允许的。

之所以存在这些限制,是因为当获取标量或包含标量分量的数组的地址时,存储顺序属性会丢失,因此通常无法通过该地址进行间接存储。不过,针对第二种情况(即涉及数组的情形),系统允许执行数组的块复制操作(无论是从数组复制数据还是向数组复制数据)。

    此外,系统不支持利用类型双关(type punning)或别名(aliasing)技术来改变存储顺序;换言之,如果某个标量对象可以通过多种不同的类型进行访问,且这些类型赋予了该对象不同的存储顺序,那么程序的行为将是未定义的。

2.5.75  section("section-name")

    该属性适用于变量和函数。

    通常,编译器会将生成的代码放置在代码段(text section)中,并将变量放置在数据段(data section)或 bss 段中。然而,有时你需要额外的段,或者需要将特定的函数或变量放置在特殊的段中。section 属性用于指定其修饰的函数或变量应位于特定的段中。例如,以下声明:

extern void foobar (void) __attribute__ ((section ("bar")));

将函数 foobar 放入 bar 部分。

以下是一个更复杂的示例:

struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };

struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };

char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };

int init_data __attribute__ ((section ("INITDATA")));

main()

{

  /* Initialize stack pointer */

  init_sp (stack + sizeof (stack));

  /* Initialize initialized data */

  memcpy (&init_data, &data, &edata - &data);

  /* Turn on the serial ports */

  init_duart (&a);

  init_duart (&b);

}

请参照示例,将 section 属性应用于全局变量,而非局部变量。

    section 属性既可用于已初始化的全局变量,也可用于未初始化的全局变量;但链接器要求每个对象只能定义一次。唯一的例外是,未初始化的变量通常会被归入 common(或 bss)段,从而允许存在多重“定义”。使用 section 属性会改变变量所属的段;如果未初始化的变量存在多重定义,这可能会导致链接器报错。你可以使用 -fno-common 编译选项或 nocommon 属性,强制对变量进行初始化。

    由于某些文件格式不支持任意段,因此 section 属性并非在所有平台上都可用。如果你需要将模块的全部内容映射到特定段,建议改用链接器提供的功能来实现

2.5.76  sentinel, sentinel(position)

    该属性适用于函数。

    该属性表明函数调用中的某个参数必须是显式的 NULL。此属性仅适用于变长参数函数。默认情况下,该“哨兵值”(sentinel)应位于函数调用的最后一个参数位置。若为该属性指定了可选的位置参数,则哨兵值必须位于从参数列表末尾倒数计算出的相应位置。

[[gnu::sentinel]] 等同于 [[gnu::sentinel(0)]]。

对于内置函数 execl 和 execlp,该属性的位置参数会自动设为 0;对于内置函数 execle,该属性的位置参数则设为 1。

    在此上下文中,有效的 NULL 定义为任意对象指针类型的零值。如果你的系统将 NULL 宏定义为整数类型,则需要进行显式类型转换。在安装过程中,GCC 会用一个适当地重新定义了 NULL 的副本替换系统头文件 <stddef.h>。

    当启用 -Wformat 选项时,编译器会针对缺失或错误的哨兵值发出警告。

2.5.77  simd, simd(“mask”)

    该属性适用于函数。

    该属性支持创建一种或多种函数版本,这些版本能够通过单次调用并利用 SIMD 指令来处理多个参数。指定此属性后,编译器可在链接时假定存在此类版本(这些版本由同一编译单元或另一个编译单元提供)。生成的版本与目标架构相关,具体规范详见相应的向量 ABI(Vector ABI)文档;对于 x86_64 目标架构,相关文档可在此处查阅。

    可选参数 mask 可取值为 notinbranch 或 inbranch,用于指示编译器分别生成无掩码(non-masked)或带掩码(masked)的克隆版本。若未指定该参数,则默认生成所有克隆版本。

    如果指定了该属性,且声明中同时包含 #pragma omp declare simd 指令,并启用了 -fopenmp 或 -fopenmp-simd 编译选项,则该属性将被忽略。

2.5.78  stack_protect

    该属性适用于函数。

    如果设置了 -fstack-protector,-fstack-protector-strong 或 -fstack-protector-explicit 标志,它会向所作用的函数添加栈保护代码。

2.5.79  strict_flex_array(level)

    strict_flex_array 属性可以应用于结构体的末尾数组字段。

    该属性控制在访问数组元素时,何时将结构体的末尾数组字段视为柔性数组成员(flexible array member)。level 必须是 0 到 3 之间的整数。

    level=0 是最宽松的级别,结构体的所有末尾数组都被视为柔性数组成员。level=3 是最严格的级别,只有当末尾数组按照 C99 及更高标准声明为柔性数组成员(即使用 [] 形式)时,才会被视为柔性数组成员。

    0 到 3 之间还有两个级别,旨在支持使用 GCC 零长度数组扩展([0])或单元素数组([1])作为柔性数组成员的旧代码。当 level 为 1 时,声明为 []、[0] 或 [1] 的末尾数组均被视为柔性数组成员。当 level 为 2 时,声明为 [] 或 [0] 的末尾数组被视为柔性数组成员。

    该属性既可以单独使用,也可以与 -fstrict-flex-arrays 命令行选项结合使用。当属性和选项同时存在时,特定末尾数组字段的严格程度由该属性决定。

    strict_flex_array 属性与 -Wstrict-flex-arrays 选项相关联。有关更多信息,请参阅“请求或抑制警告的选项”(Options to Request or Suppress Warnings)部分。

2.5.80  strub

    该属性适用于类型,也可能出现在变量和函数声明中。

    strub 属性用于定义函数和变量的栈清理(stack-scrubbing)特性,从而确保访问敏感数据的函数在返回或抛出异常时,其栈帧内容会被清零。该特性既可以通过为特定函数选择特定的 strub 模式来显式启用,也可以通过使用 strub 变量来隐式启用。

作为一种类型属性,它作用于类型本身,即使是在函数或变量声明中指定时也是如此。当应用于函数类型时,它接受一个可选的字符串参数;当应用于函数指针类型时,若提供了该可选参数,则该参数会传递给底层的函数类型。

/* A strub variable.  */

int __attribute__ ((strub)) var;

/* A strub variable that happens to be a pointer.  */

__attribute__ ((strub)) int *strub_ptr_to_int;

/* A pointer type that may point to a strub variable.  */

typedef int __attribute__ ((strub)) *ptr_to_strub_int_type;

/* A declaration of a strub function.  */

extern int __attribute__ ((strub)) foo (void);

/* A pointer to that strub function.  */

int __attribute__ ((strub ("at-calls"))) (*ptr_to_strub_fn)(void) = foo;

使用 at-calls strub 模式(即 strub("at-calls") 或简写为 strub)的函数,其接口会发生变更。调用该函数的代码需进行相应调整,以适应这些变更,并确保在被调用函数返回后,清除(即用零覆盖)其所使用的栈空间。由于接口变更导致该函数类型与未加修饰但功能等价的类型不兼容,因此所有用于调用该函数的声明及类型都必须关联此 strub 模式。

    使用 internal strub 模式(即 strub("internal"))的函数,其接口保持不变且类型兼容;不过,它可能会被转换为一个包装函数,该包装函数利用自定义接口来调用实际的函数体,并负责清除函数体所使用的栈空间。尽管函数体使用的栈空间会被清除,但包装函数自身的栈空间却不会,这意味着即使在启用了 strub 的函数中调用此类函数,参数和返回值仍可能未被清除。正因如此,在使用 -fstrub=strict 选项进行编译时,禁止在 strub 上下文中调用 internal strub 模式的函数。

 
/* A declaration of an internal-strub function.  */

extern int __attribute__ ((strub ("internal"))) bar (void);

int __attribute__ ((strub))

baz (void)

{

  /* Ok, foo was declared above as an at-calls strub function.  */

  foo ();

  /* Not allowed in strict mode, otherwise allowed.  */

  bar ();

}

与 strub 属性关联的自动分配变量会使(直接)包含该变量的函数启用 strub 模式。

与 strub 属性关联的静态分配变量会使读取该变量的函数(通过其 strub 数据类型)启用 strub 模式。通过解引用指向 strub 数据类型的指针来读取数据也会产生相同的效果。注意:该属性不会从复合类型传递到其组成部分的类型,因此对于非标量类型可能无法达到预期效果。

    当为未显式关联 strub 模式的函数选择启用 strub 模式时(例如由于 strub 变量或数据指针),即使 at-calls 模式也可用且效率更高,最终被选为优化选项,该函数也必须满足内部模式可行性要求(见下文)。

 

/* zapme is implicitly strub-enabled because of strub variables.

   Optimization may change its strub mode, but not the requirements.  */

static int

zapme (int i)

{

  /* A local strub variable enables strub.  */

  int __attribute__ ((strub)) lvar;

  /* Reading strub data through a pointer-to-strub enables strub.  */

  lvar = * (ptr_to_strub_int_type) &i;

  /* Writing to a global strub variable does not enable strub.  */

  var = lvar;

  /* Reading from a global strub variable enables strub.  */

  return var;

}

“Strub 上下文”是指启用了 strub 功能的函数的主体部分(区别于其接口);这种启用既可以是显式的(通过 at-calls 或内部模式),也可以是隐式的(因 strub 变量或命令行选项而触发)。

    若函数关联了 disabled 模式(即 strub("disabled")),则其栈空间不会被清除(scrubbed)。此类函数无法在 strub 上下文中被调用。

    若希望某个函数既能在 strub 上下文中被调用,又不清除其栈空间,可将其关联至 callable 模式(即 strub("callable"))。

    当未显式或隐式指定 strub 模式时,该模式默认为 callable;但在使用 -fstrub=strict 选项进行编译时,默认模式则变为 disabled。

 
[[gnu::strub("callable")]] extern int bac (void);

[[gnu::strub("disabled")]] extern int bad (void);

 /* Implicitly disabled with -fstrub=strict, otherwise callable.  */

extern int bah (void);

[[gnu::strub]]

int

bal (void)

{

  /* Not allowed, bad is not strub-callable.  */

  bad ();

  /* Ok, bac is strub-callable.  */

  bac ();

  /* Not allowed with -fstrub=strict, otherwise allowed.  */

  bah ();

}

标记为 callable 和 disabled 的函数类型彼此并不兼容,但它们的底层接口是兼容的,因此在两者之间转换指针,或者使用此类指针(或替代声明)来调用函数,都是安全的。这些类型与 internal 类型(注意:不包括 at-calls!)之间的接口也是可以互换的;不过,向指针类型添加 internal 属性并不会导致被指向的函数执行栈清理操作。

void __attribute__ ((strub))

bap (void)

{

  /* Assign a callable function to pointer-to-disabled.

     Flagged as not quite compatible with -Wpedantic.  */

  int __attribute__ ((strub ("disabled"))) (*d_p) (void) = bac;

  /* Not allowed: calls disabled type in a strub context.  */

  d_p ();

  /* Assign a disabled function to pointer-to-callable.

     Flagged as not quite compatible with -Wpedantic.  */

  int __attribute__ ((strub ("callable"))) (*c_p) (void) = bad;

  /* Ok, safe.  */

  c_p ();

  /* Assign an internal function to pointer-to-callable.

     Flagged as not quite compatible with -Wpedantic.  */

  c_p = bar;

  /* Ok, safe.  */

  c_p ();

  /* Assign an at-calls function to pointer-to-callable.

     Flaggged as incompatible.  */

  c_p = bal;

  /* The call through an interface-incompatible type will not use the

     modified interface expected by the at-calls function, so it is

     likely to misbehave at runtime.  */

  c_p ();

}

Strub 上下文绝不会被内联到非 Strub 上下文中。当一个 internal-strub 函数被拆分时,其包装器(wrapper)往往可以被内联,但被包装的函数体则绝不会被内联。若函数被标记为 always_inline,即便显式指定了 internal strub 模式,它也不会进行包装处理,因此其函数体将按预期进行内联。

inline int __attribute__ ((strub ("at-calls")))

inl_atc (void)

{

  /* This body may get inlined into strub contexts.  */

}

inline int __attribute__ ((strub ("internal")))

inl_int (void)

{

  /* This body NEVER gets inlined, though its wrapper may.  */

}

inline int __attribute__ ((strub ("internal"), always_inline))

inl_int_ali (void)

{

  /* No internal wrapper, so this body ALWAYS gets inlined,

     but it cannot be called from non-strub contexts.  */

}

void __attribute__ ((strub ("disabled")))

bat (void)

{

  /* Not allowed, cannot inline into a non-strub context.  */

  inl_int_ali ();

}

某些 -fstrub=* 命令行选项可在可行的情况下隐式启用 strub 模式。只有当函数符合某 strub 模式的适用条件,且满足下文详述的其他条件时,该模式对该函数才是可行的。若函数不适用于某模式,则尝试显式将其与该模式关联的操作将被拒绝,并报错。若函数适用该模式,既可通过​​此属性显式指定,也可通过命令行选项隐式指定(尽管隐式指定可能涉及额外的适用性要求)。

    如果函数显式指定了其他 strub 模式、带有 noipa 属性或调用了 __builtin_apply_args,则该函数不符合使用“at-calls” strub 模式的条件。对于符合条件的函数,若未通过函数类型显式请求“at-calls” strub 模式,则仅在以下情况下该模式才适用:该函数对其他编译单元不可见、其地址未被获取,且从未通过函数类型覆盖(function type overrider)进行调用。

/* bar is eligible for at-calls strub mode,

   but not viable for that mode because it is visible to other units.

   It is eligible and viable for internal strub mode.  */

void bav () {}

/* setp is eligible for at-calls strub mode,

   but not viable for that mode because its address is taken.

   It is eligible and viable for internal strub mode.  */

void setp (void) { static void (*p)(void); = setp; }

如果函数显式指定了其他 strub 模式,或者带有 noipa 属性,则该函数不符合使用内部 strub 模式的条件。对于 always_inline 函数,只要满足上述要求,即符合条件。带有 noclone 属性,使用了非局部标签(non-local labels)、计算跳转(computed gotos)、替代的可变参数传递接口、__builtin_next_arg 或 __builtin_return_address 等扩展特性,或者参数数量过多(约 64Ki)的函数,均不符合条件(除非该函数是 always_inline 的)。对于内部 strub 模式,所有符合条件的函数均适用。

 
/* flop is not eligible, thus not viable, for at-calls strub mode.

   Likewise for internal strub mode.  */

__attribute__ ((noipa)) void flop (void) {}

/* flip is eligible and viable for at-calls strub mode.

   It would be ineligible for internal strub mode, because of noclone,

   if it weren't for always_inline.  With always_inline, noclone is not

   an obstacle, so it is also eligible and viable for internal strub

   mode.  */

inline __attribute__ ((noclone, always_inline)) void flip (void) {}

2.5.81  symver ("name2@nodename")

    该属性适用于函数。

    在 ELF 目标平台上,该属性用于创建符号版本。参数中的 name2 部分是符号的实际名称,即外部引用该符号时所使用的名称;nodename 部分则应对应于构建共享库时提供给链接器的版本脚本(version script)中指定的节点名称。该带版本号的符号必须已定义,且必须default visibility导出。

    此示例在汇编器输出中生成一条 .symver foo_v1, foo@VERS_1 指令。

[[gnu::__symver__ ("foo@VERS_1")]] int

foo_v1 (void)

{

}

你还可以为特定符号定义多个版本(从 binutils 2.35 版本开始):

[[gnu::__symver__ ("foo@VERS_2"), gnu::__symver__ ("foo@VERS_3")]]

int symver_foo_v1 (void)

{

}

    此示例创建了一个名为 symver_foo_v1 的符号名称,该名称对应于 foo 的 VERS_2 和 VERS_3 版本。

    如果你使用的是较旧版本的 binutils,则需要使用符号别名:

__attribute__ ((__symver__ ("foo@VERS_2")))

int foo_v1 (void)

{

  return 0;

}

__attribute__ ((__symver__ ("foo@VERS_3")))

__attribute__ ((alias ("foo_v1")))

int symver_foo_v1 (void);

最后,如果参数是“name2@@nodename”,那么除了创建符号版本(就像使用了“name2@nodename”一样)之外,链接器还会使用该版本来解析 name2。

2.5.82  tainted_args

    该属性适用于函数。

    tainted_args 属性用于标识某类函数:其调用方式要求对参数进行净化(sanitization),例如操作系统内核中的系统调用。此类函数可被视为程序“攻击面”的一部分。该属性既可用于函数声明,也可用于包含函数指针的字段声明;在后一种情况下,任何用作此类回调字段初始值设定项(initializer)的函数,都会被视为在调用时接收了受污染(tainted)的参数。

    当启用 -fanalyzer 选项时,分析器会特别关注此类函数,并可能发出由以下选项控制的警告:-Wanalyzer-tainted-allocation-size,-Wanalyzer-tainted-array-index,-Wanalyzer-tainted-divisor,-Wanalyzer-tainted-offset 和 -Wanalyzer-tainted-size。

2.5.83  target(string, …)

该属性适用于函数。

多个目标架构后端实现了 target 属性,用于指定某个函数应使用与命令行参数不同的目标选项进行编译;此时,原有的命令行目标选项将被忽略。该属性接受一个或多个字符串作为参数,每个字符串由一个或多个以逗号分隔的后缀组成,这些后缀附加在 -m 前缀之后,共同构成特定于机器的选项名称(参见“Target-Specific Options”)。

例如,可以使用 target 属性让函数采用不同于默认指令集架构(ISA)的架构进行编译。#pragma GCC target 可用于为多个函数指定特定于目标的选项(关于该 pragma 的详细信息,请参阅“Function Specific Option Pragmas”)。

以 x86 架构为例,你可以声明一个带有 target("sse4.1,arch=core2") 属性的函数,以及另一个带有 target("sse4a,arch=amdfam10") 属性的函数。这等同于分别使用 -msse4.1 和 -march=core2 选项编译第一个函数,以及使用 -msse4a 和 -march=amdfam10 选项编译第二个函数。你需要自行确保函数仅在支持其编译所针对的特定 ISA 的机器上运行(例如,在 x86 平台上使用 cpuid 指令来检测所使用的功能位和架构系列)。

int core2_func (void) __attribute__ ((__target__ ("arch=core2")));

int sse3_func (void) __attribute__ ((__target__ ("sse3")));

     以逗号分隔多个字符串作为参数来指定多个选项,其效果等同于在单个字符串内用逗号(‘,’)分隔各个选项后缀。字符串内不允许包含空格。

所支持的选项因目标架构而异;详情请参阅 x86 属性、PowerPC 属性、ARM 属性、AArch64 属性、LoongArch 属性及 S/390 属性的相关说明。     

在支持目标函数多版本特性(target function multiversioning)的目标架构(如 x86)上,使用 C++ 时,你可以声明多个具有相同函数签名但目标属性值不同的函数,并由动态链接器选择正确的版本。在下例中,系统生成了两个具有不同名称修饰(mangling)的函数版本,并创建了一个 ifunc 解析器(resolver)来选择正确的版本以填充 func 符号。

int func (void) __attribute__ ((target ("arch=core2"))) { return 1; }

int func (void) __attribute__ ((target ("sse3"))) { return 2; }

在同一个多版本函数定义中,不能同时使用带有 target 注解的声明和带有 target_clones 注解的声明。

更多详情请参阅“函数多版本化”。

2.5.84  target_version (option)

此属性适用于函数。

对于支持 target_version 函数多版本特性(针对 AArch64、LoongArch 和 RISC-V 架构)的目标平台,在 C 或 C++ 代码中,你可以使用 target_version 或 target_clones 属性声明多个函数,从而定义一个函数版本集。

有关更多详细信息,请参阅“函数多版本特性”(Function Multiversioning)相关内容。

2.5.85  target_clones(options)

此属性适用于函数。

target_clones 属性用于指定将函数克隆为多个版本,并使用与命令行指定不同的目标选项进行编译。

对于 x86 和 PowerPC 目标,其支持的选项和限制与 target 属性相同。关于 LoongArch 的语法详情,请参阅“LoongArch 属性”部分。

    例如,在 x86 平台上,可以使用 target_clones("sse4.1,avx") 编译函数。GCC 会创建两个函数克隆:一个使用 -msse4.1 编译,另一个使用 -mavx 编译。

    在 PowerPC 平台上,可以使用 target_clones("cpu=power9,default") 编译函数。GCC 会创建两个函数克隆:一个使用 -mcpu=power9 编译,另一个使用默认选项编译。要使用 target_clones 属性,GCC 必须配置为使用 GLIBC 2.23 或更高版本。

    对于支持 target_version 属性的目标(如 AArch64、LoongArch 和 RISC-V),target_clones 的工作方式类似。该属性接受多个参数,并为每个参数生成一个带版本标识的克隆。一个标注了 target_clones 的函数,等同于针对参数中的每个有效版本字符串分别复制该函数,并将每个副本标注为 target_version。这意味着标注了 target_clones 的函数定义,可以与标注了 target_version 的函数定义或其他标注了 target_clones 的函数定义结合使用。

    对于这些目标,其支持的选项和限制与 target_version 属性相同。

更多详情请参阅“函数多版本化(Function Multiversioning)”部分。

2.5.86  tls_model ("tls_model")

此属性适用于变量。

tls_model 变量属性用于设置特定 __thread 变量的线程本地存储模型(参见线程本地存储),它会覆盖命令行开关 -ftls-model= 的设置,并针对每个变量单独生效。tls_model 参数应为 global-dynamic,local-dynamic,initial-exec 或 local-exec 之一。

并非所有目标都支持此属性。

2.5.87  transparent_union

该属性适用于union类型定义。

    它表明,任何具有该union类型的函数参数都会导致对该函数的调用受到特殊处理。

    首先,对应于透明union类型的实参可以是该union中包含的任意类型;无需进行类型转换。此外,如果union包含指针类型,则对应的实参可以是空指针常量或 void 指针表达式;如果union包含 void 指针类型,则对应的实参可以是任意指针表达式。如果union成员类型是指针,则必须遵守所指向类型上的限定符(如 const),这与普通指针转换的要求一致。

    其次,实参传递给函数时使用的是透明union第一个成员的调用约定,而不是union本身的调用约定。union的所有成员必须具有相同的机器表示形式;这是确保该参数传递机制正常工作的必要条件。

    透明union专为那些出于兼容性原因而拥有多个接口的库函数而设计。例如,假设 wait 函数既需要接受 int * 类型的值以符合 POSIX 标准,又需要接受 union wait * 类型的值以符合 4.1BSD 接口标准。如果 wait 的参数类型定义为 void *,虽然它能同时接受上述两种参数,但也会接受任何其他指针类型,从而削弱了参数类型检查的有效性。相比之下,<sys/wait.h> 可能会按如下方式定义该接口:

typedef union __attribute__ ((__transparent_union__))

  {

    int *__ip;

    union wait *__up;

  } wait_status_ptr_t;

pid_t wait (wait_status_ptr_t);

该接口允许传入 int * 或 union wait * 类型的参数,并采用 int * 的调用约定。程序在调用 wait 时可以使用这两种类型中的任意一种作为参数:

int w1 () { int w; return wait (&w); }

int w2 () { union wait w; return wait (&w); }

有了这个接口,wait 的实现可能如下所示:

pid_t wait (wait_status_ptr_t p)

{

  return waitpid (-1, p.__ip, 0);

}

2.5.88  unavailable , unavailable(msg)

    unavailable 属性可以应用于函数、变量、类型或枚举器。

    如果该属性所应用的实体在源文件的任何位置被使用,则会导致错误。这在识别已从接口的特定变体中移除的实体时非常有用。除了发出错误而不是警告之外,unavailable 属性的行为与 deprecated 属性相同。

2.5.89  uninitialized

此变量适用于具有自动存储的变量。

这意味着,当存在选项 -ftrivial-auto-var-init 时,编译器不应自动初始化该变量。

    启用选项 -ftrivial-auto-var-init 后,所有没有显式初始化器的自动变量都会由编译器初始化。这些额外的编译器初始化可能会增加运行时开销,有时甚至会非常显著。此属性可用于标记某些变量,使其不被自动初始化,从而减少运行时开销。

    当不存在选项 -ftrivial-auto-var-init 时,此属性无效。

2.5.90  unsequenced

此类型属性可以出现在函数声明和函数类型声明中。

它是 C23 [[unsequenced]] 属性的 GNU 对应项,用于根据 C23 定义指定指向无效果、幂等、无状态和独立函数的函数指针。

    与标准 C23 属性不同,它也可以在属于函数声明的属性中指定,并且即使在这种情况下也适用于其函数类型。

    没有指针或引用参数的未排序函数与具有 const 属性的函数类似,只不过 const 属性也需要有限性。因此,具有 const 属性的函数和具有 unsequence 属性的函数都可以通过公共子表达式消除来优化,但只有具有 const 属性的函数如果其结果未使用或仅由死代码使用,则可以通过死代码消除来优化。诊断没有指针或引用参数且返回类型为 void 的未排序函数,因为它们无法存储任何结果,也没有其他可观察到的副作用。

    具有指针或引用参数的未排序函数可以通过传递的指针或引用或对指针的引用来检查对象,或者可以通过这些指针或引用或对指针的引用来存储其他结果。

    unsequenced 属性比类似的可重现属性施加了更大的限制,并且比 const 属性施加了更少的限制,因此在优化过程中,const 优先于 unsequenced,后者优先于 reproducible。

2.5.91  unused

    该属性可应用于函数、变量、结构体成员、类型声明或标签。

    当应用于函数、变量、结构体成员或标签时,它表示该实体可能不会被使用。它会抑制 GCC 针对未使用实体发出的常规警告。

    当应用于类型(包括union或struct)时,该属性表示该类型的变量可能看起来未被使用。GCC 不会针对该类型的任何变量发出警告,即使该变量看似没有任何实际用途。这种情况常见于锁或线程类:这些类通常在定义后不再被显式引用,但其构造函数和析构函数却包含重要的管理逻辑。

    用于标签的 unused 属性主要针对程序生成的代码,因为此类代码在启用 -Wall 编译选项时可能会包含未使用的标签。通常不建议在人工编写的代码中使用该属性,但在跳转至该标签的代码位于 #ifdef 条件编译块内的情况下,它可能会很有用。

2.5.92  used

used 属性适用于函数和变量。

    当应用于函数时,该属性意味着即使函数看似未被引用,也必须为该函数生成代码。例如,当函数仅在内联汇编中被引用时,这就非常有用。

    当应用于 C++ 类模板的成员函数时,该属性还意味着如果类本身被实例化,则该函数也会被实例化。

    当应用于具有静态存储期的变量时,该属性意味着即使变量看似未被引用,也必须生成该变量。

    当应用于 C++ 类模板的静态数据成员时,该属性还意味着如果类本身被实例化,则该成员也会被实例化。

2.5.93  vector_size(bytes)

    vector_size 属性可应用于类型、变量和函数声明。

    当应用于变量声明时,它作用于该变量的类型;当应用于函数声明时,它作用于返回值类型。

    该属性指定了类型的向量大小(以字节为单位)。它所作用的类型被称为基类型。bytes 参数必须是基类型大小的正整数倍,且该倍数必须是 2 的幂。例如,以下声明使用了旧版属性语法:

typedef __attribute__ ((vector_size (32))) int int_vec32_t ;

typedef __attribute__ ((vector_size (32))) int* int_vec32_ptr_t;

typedef __attribute__ ((vector_size (32))) int int_vec32_arr3_t[3];

或者,也可以使用标准语法进行这些声明:

typedef int int_vec32_t [[gnu::vector_size (32)]];

typedef int * int_vec32_ptr_t [[gnu::vector_size (32)]];

typedef int int_vec32_arr3_t[3] [[gnu::vector_size (32)]];

两者都将 int_vec32_t 定义为由 int 大小的单元组成的 32 字节向量类型。由于

int 的大小为 4 字节,该类型定义了一个包含 8 个单元(每个单元 4 字节)的向量。int_vec32_t 类型变量的模式为 V8SI。随后,int_vec32_ptr_t 被定义为指向此类向量类型的指针,而 int_vec32_arr3_t 则被定义为包含 3 个此类向量的数组。

    以下是一个涉及函数声明的示例:

__attribute__ ((vector_size (16))) float get_flt_vec16 (void);

这段代码声明 get_flt_vec16 是一个返回 16 字节、基类型为 float 的向量的函数。

例如:

int foo [[gnu::vector_size (16)]];

此属性会导致编译器将 foo 的模式设置为 16 字节,并划分为 int 大小的单元。假设 foo 是一个 32 位 int,则其类型是一个包含四个 4 字节单元的向量,对应的模式为 V4SI。

    此属性仅适用于整数和浮点标量基类型,但允许将数组、指针和函数返回值与此结构结合使用。

    即使聚合体的大小与对应的标量相同,带有此属性的聚合体也是无效的。例如,以下声明:

struct S { int a; };

struct S  __attribute__ ((vector_size (16))) foo;

即使struct的大小与整数的大小相同,此操作也无效。

有关操作向量类型对象的详细信息,请参阅“通过内置函数使用向量指令”。

2.5.94  visibility("visibility_type")

    此属性可应用于函数、变量和类型。

    它会影响与其关联的声明的链接方式。支持四种 visibility_type 值:default,hidden,protected 和 internal visibility。

[[gnu::visibility ("protected")]]

void f () { /* Do something. */; }

int i [[gnu::visibility ("hidden")]];

visibility_type 的可能取值对应于 ELF gABI 中的visibility设置。

2.5.95  default

    对于目标文件格式而言,default visibility是常规情况。该值可用于visibility属性,以覆盖其他可能改变实体default visibility假设的选项。

    在 ELF 格式中,default visibility意味着声明对其他模块可见;对于共享库,它还意味着所声明的实体可能会被覆盖。

    在 Darwin 平台上,default visibility意味着声明对其他模块可见。

    default visibility对应于编程语言中的external linkage。

2.5.96  hidden

    hidden visibility表明所声明的实体具有一种新的链接形式,我们称之为hidden linkage。若两个具有hidden linkage的对象声明位于同一个共享对象内,则它们指向同一个对象。

2.5.97  internal

    internal visibility类似于hidden visibility,但包含特定于处理器的额外语义。除非 psABI 另有规定,GCC 将内部visibility定义为:该函数绝不会被其他模块调用。这与hidden函数形成了对比——hidden函数虽不能被其他模块直接引用,却可以通过函数指针间接引用。通过标明函数无法从模块外部被调用,GCC 可以进行某些优化(例如省略 PIC 寄存器的加载操作),因为编译器已知调用方函数已经加载了正确的值。

2.5.98  protected

    protected visibility与default visibility类似,区别在于它表明:定义该实体的模块内部的引用,会绑定到该模块内的定义上。也就是说,该声明的实体无法被其他模块重写。

    各种visibility属性在许多(但并非所有)ELF 目标平台上均受支持(前提是汇编器支持 .visibility 伪指令)。default visibility在所有平台上都受支持。hidden visibility在 Darwin 目标平台上受支持。

    visibility属性应仅应用于那些原本具有外部链接(external linkage)的声明。该属性的应用应当保持一致,即不应针对同一实体使用不同的visibility设置进行声明。

    在 C++ 中,visibility属性不仅适用于函数和对象,也适用于类型,因为 C++ 中的类型具有链接属性。类的visibility不得高于其非静态数据成员类型或基类的visibility;类成员的visibility默认与所属类相同。此外,若声明未显式指定visibility,则其visibility受限于其类型的visibility。

    在 C++ 中,你可以为类的成员函数和静态成员变量指定visibility属性。当你确定某个特定方法或静态成员变量仅供单个共享对象使用时,此功能非常有用:你可以将其标记为hidden,而将类的其余部分保持为默认visibility。使用时需注意避免违反“单一定义规则”(One Definition Rule);例如,如果未将整个类标记为hidden,通常不宜将某个内联方法单独标记为hidden。

    C++ 命名空间声明也可以具有visibility属性。

namespace nspace1 __attribute__ ((visibility ("protected")))

{ /* Do something. */; }

该属性仅适用于特定的命名空间主体,而不适用于同一命名空间的其他定义;这等同于在命名空间定义前后使用 #pragma GCC visibility(参见 Visibility Pragmas)。

    在 C++ 中,如果模板参数具有受限的可见性,则该限制会自动传递给模板实例化。否则,模板实例化和特化将默认采用其模板的可见性。

    如果模板和包含它的类都指定了显式visibility,则采用模板的可见性。

    在 C++ 中,可见性属性也可应用于class,struct,union和enum类型。与其他类型属性不同,该属性必须位于初始关键字与类型名称之间,而不能出现在类型定义体之后。

    请注意,类型可见性会应用于与该类相关的具有“模糊链接”(vague linkage)特性的实体(如虚函数表 vtable、类型信息节点 typeinfo node 等)。特别地,如果一个类在一个共享对象中被抛出作为异常,而在另一个共享对象中被捕获,则该类必须具有default visibility。否则,这两个共享对象将无法使用同一个类型信息节点,从而导致异常处理失败。

2.5.99  warn_if_not_aligned(alignment)

    此属性适用于struct字段。

    它指定字段的对齐阈值(以字节为单位)。如果struct字段的对齐方式低于此阈值,则会发出警告。

    例如,声明如下:

struct foo

{

  int i1;

  int i2;

  unsigned long long x __attribute__ ((warn_if_not_aligned (16)));

};

导致编译器针对 struct foo 发出警告,例如“warning: alignment 8 of 'struct foo' is less than 16”。当struct 成员的偏移量未对齐时,编译器也会发出警告,例如“warning: 'x' offset 8 in 'struct foo' isn't aligned to 16”:

struct __attribute__ ((aligned (16))) foo

{

  int i1;

  int i2;

  unsigned long long x __attribute__ ((warn_if_not_aligned (16)));

};

此属性还可以应用于 typedef,在这种情况下,它应用于该类型的所有结构字段。

例如,这段代码:

typedef unsigned long long __u64

  [[gnu::aligned (4), gnu::warn_if_not_aligned (8)]];

struct foo

{

  int i1;

  int i2;

  __u64 x;

};

与上述第一个例子类似。可以使用 -Wno-if-not-aligned 参数禁用此警告。

2.5.100  warn_unused_result

该属性适用于函数。

    如果调用了带有 warn_unused_result 属性的函数却未使用其返回值,编译器便会发出警告。对于那些不检查结果即意味着存在安全隐患或必然属于程序错误的函数(例如 realloc),该属性非常有用。

[[gnu::warn_unused_result]] int fn (void);

int foo (void)

{

  if (fn () < 0) return -1;

  fn ();

  return 0;

}

导致第 5 行出现警告。

2.5.101  weak

    该属性适用于函数和变量声明。

    weak 属性使得外部符号的声明被生成为weak 符号而非全局符号。这主要用于定义可在用户代码中被覆盖的库函数,不过也可用于非函数声明。用于覆盖的符号必须与weak符号具有相同的类型;此外,若涉及变量,覆盖符号还必须与弱符号具有相同的大小和对齐方式。weak符号机制受 ELF 目标格式支持,在使用 GNU 汇编器和链接器时,也支持 a.out 目标格式。

2.5.102  weakref,weakref ("target")  

该属性适用于函数或变量声明。

    weakref 属性将相应的实体声明标记为弱引用(weak reference)。若不带参数使用,该声明通常需配合alias 属性一同使用,以指定目标符号。或者,也可以直接将目标名称作为参数传递给 weakref,从而指定该别名所指向的目标定义。目标必须与声明具有相同的类型;此外,若目标为变量,其大小和对齐方式也必须与声明一致。无论采用哪种声明形式,weakref 都会隐式地将声明的符号标记为weak符号。若未向 weakref 或 alias 提供目标参数,则 weakref 等同于 weak 属性(此时声明可以是 extern 类型的)。

/* Given the declaration: */

extern int y (void);

/* the following... */

static int x (void) __attribute__ ((weakref ("y")));

/* is equivalent to... */

static int x (void) __attribute__ ((weakref, alias ("y")));

/* or, alternatively, to... */

static int x (void) __attribute__ ((weakref));

static int x (void) __attribute__ ((alias ("y")));

弱引用是指无需为目标符号定义即可使用的别名。如果目标符号仅通过弱引用被引用,则它成为一个弱未定义符号。但是,如果它被直接引用,则强引用仍然有效,并且需要为该符号定义,定义不必在同一个编译单元中。

    其效果相当于将所有对别名的引用移动到一个单独的编译单元,将别名重命名为被别名化的符号,将其声明为弱引用,编译这两个单独的编译单元,然后对它们执行可重定位输出的链接(例如,使用 ld -r 命令)。

    附加了 weakref 且与命名target关联的声明必须是static的。

2.5.103  zero_call_used_regs("choice")  

    该属性适用于函数。

    zero_call_used_regs 属性指示编译器在函数返回时将“调用者保存寄存器”(call-used registers)(注:又称易失寄存器,在调用时会修改其值,因此调用者需负责保存其值,完成后将其恢复)的一个子集清零。此举旨在提高程序安全性,既能缓解面向返回的编程(ROP)攻击,也能防止通过寄存器发生信息泄露。

    为了满足不同用户的安全需求并同时控制运行时开销,choice 参数提供了一种灵活的方式,用于选择需要清零的“调用时会被修改的寄存器”子集。choice 的四个基本取值如下:

● “skip” 不会将任何易失寄存器清零。

● “used” 仅将函数中实际使用过的易失寄存器清零。所谓“使用过”的寄存器,是指其内容在函数内已被赋值或被引用的寄存器。

● “all” 会将所有易失寄存器清零。

● “leafy” 在叶函数(leaf function)中的行为类似于 “used”,而在非叶函数中的行为则类似于 “all”。这种机制使得叶函数中的清零操作更为精简——因为在叶函数中,实际使用的寄存器集合是已知的,而针对这些寄存器进行清零往往已足以满足某些应用场景的需求。

除了这三个基本选项外,还可以按如下方式对“used”、“all”和“leafy”进行修改:

●  添加 ‘-gpr’ 可将清零操作限制在通用寄存器范围内。

●  添加 ‘-arg’ 可将清零操作限制在有时用于传递函数参数的寄存器范围内。这涵盖了平台调用约定所定义的所有参数寄存器,无论该函数实际是否使用这些寄存器来传递参数。

修饰语可以单独使用,也可以组合使用。如果组合使用,则必须按照上述顺序排列。

因此,所有选项如下:

skip —— 不会将任何易失寄存器清零。

used —— 仅将函数中使用的易失寄存器清零。

used-gpr ——仅将函数中使用的通用易失寄存器清零。

used-arg —— 仅将函数和传参使用的易失寄存器清零。

used-gpr-arg ——  仅将函数和传参使用的通用易失寄存器清零

all —— 清零所有易失寄存器。

all-gpr —— 清零所有通用易失寄存器。

all-arg —— 清楚所有传参易失寄存器。

all-gpr-arg —— 清楚所有传参通用易失寄存器。

leafy —— 同叶函数中使用的 “used”, 且同非叶函数中的“all”。

leafy-gpr —— 同叶函数中使用的 “used-gpr”, 且同非叶函数中的“all-gpr”。

leafy-arg —— 同叶函数中使用的“used-arg”, 且同非叶函数中的“all-arg”。

leafy-gpr-arg —— 同叶函数中使用的“used-gpr-arg”, 且同非叶函数中的“all-gpr-arg”。

在此列表中, ‘used-arg’, ‘used-gpr-arg’, ‘all-arg’, ‘all-gpr-arg’, ‘leafy-arg’, and ‘leafy-gpr-arg’ 主要用于缓解 ROP 攻击。

该属性默认由 -fzero-call-used-regs 控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值