这一段说明了在C语言中,使用到__attribute__
的语法和属性说明符绑定的概念。一些细节也许对C++和Objective C有所不同。由于对属性不合适的语法,这里描述的一些格式可能不能在所有情况下成功解析。
上一篇,声明函数的属性,了解施加于函数的属性语义的细节。说明变量属性一篇,了解施加于变量的属性语义的细节。指定类型属性一篇,了解施加与结构体,共用体,和枚举类型的属性语义的细节。
属性说明符是的格式是:__attribute__
((属性列表))。属性列表是一个可为空的由逗号分隔的属性序列,其中的属性类型如下:
- 空。空属性会被忽略。
- 字(可能是一个标识符如unused,或者一个保留字如const)。
- 在跟在后边的圆括号中有属性的参数的字。这些参数有如下的格式:
- 标识符。比如,模式属性使用这个格式。
- 跟有逗号或非空的由逗号分隔的表达式表。比如,格式化属性使用这个格式。
- 可为空的由逗号分隔的表达式表。比如,格式化参数使用这个格式和一个单独整型常量表达式列表,并且别名属性使用这个格式和一个单独的字符串常量列表。
属性说明符列表是一个由一个或多个属性说明符组成的序列,不被任何其它标志分开。
属性说明符列表可能跟在一个标签后的冒号出现,除了
case
或default
中的标签。唯一的属性使用在一个未使用的标签后是合理的。这个特征被设计成代码被包含而未使用的标签但是在编译是使用了"-Wall
"参数。它也许不常用与人工编写的代码中,虽然它应该在代码需要跳到一个包含在一段有#ifdef
说明的条件编译的程序段中很有用。属性说明符列表可以作为结构体、共用体或枚举说明符的一部分出现。它既可以直接跟在结构体、共用体或枚举说明符后,也可以紧贴大括号之后。如果结构体、共用体或枚举类型没有在使用属性说明符列表中用到的说明符定义,也就是说在如下这样的用法中
struct __attribute__((foo))
没有跟空的大括号,它会被忽略。在用到属性说明符的地方,跟它靠近的大括号,它们会被认为和结构体、共用体或被定义的枚举类型有联系,不同任何出现在包含声明的类型说明符,并且类型直到属性说明符之后才结束。否则,属性说明符作为声明的一部分出现,计算未命名参数和类型明声明,并且和这个声明相关(有可能内嵌在另外一个声明中,例如在参数声明的时候)。 将来属性说明符在一些地方也任何用于特殊声明符不超出代替声明;这些情况将在一下提到。在属性说明符被用于在函数或数组中说明参数的地方,它也许用于函数 或数组而不是指向一个会被隐含转换的参数的指针,但这不仅是正确的工具。
任何在可能包含属性说明符的声明开始处的说明符与修饰符列表,抑或没有这样一个列表也许在上下文包含存储类说明符。(一些属性尽管本质自然是类型说 明符,并且仅在需要使用存储类说明符的地方是合理的,例如section。)对这个语法有一个必要的限制:第一,在函数定义中的老式风格的参数声明无法有 属性说明符开头,这种情况尚不提供)。在一些其它情况下,属性说明符被允许使用这种语法但不被编译器所支持。所有的属性说明符在这里被当做正割声明的一部 分。在逐步被废弃的在int类型默认省略了类型说明符的用法的地方,这样的说明符和修饰符列表可能是没有任何其它说明符或修饰符的属性说明符列表。
在不止一个标识符使用单独的说明符或修饰符的声明中的由逗号分隔的说明符列表中,属性说明符列表可能直接在一个说明符之前出现(第一个除外)。目 前,这样的属性说明符不仅适用于被出现在自己的声明中的标识符,也可以用于在此声明明中此后声明的所有标识符,但是将来它们可能只能用于那样的单独的标识 符。例如:
__attribute__((noreturn)) void d0 (void), __attribute__((format(printf, 1, 2))) d1 (const char *, ...), d2 (void)
无返回值属性用于所有的函数声明中;格式化属性会只用于d1,但是目前也用于d2(并且由此造成错误)。
属性说明符列表可能直接出现在逗号、等号或分号之前,终止标识符的说明除过函数定义。目前,这样的属性说明符用于已声明的对象或函数,但是将来它们将附属与相邻的最远的说明符。在简单情况下没有区别,但是例如在
void (****f)(void) __attribute__((noreturn))
这 样的声明中,当前无返回值的属性用于f,这就会造成从f起不是一个函数的警告,但是将来它也许用于函数****f。在这种情况中的属性的明确的语言符号将 不用于定义。在对象或函数的汇编名称处被说明(看5.37节控制用于汇编代码的名称),当前,属性必须跟随与asm说明之后;将来,在asm说明之前的属 性可能用于邻近的声明符,并且它那些在它之的被用于已声明的对象或函数。将来,属性说明符可能被允许在函数定义的声明符后出现(在任意老式风格参数声明或函数题之前)。
属性说明符列表可能出现在内嵌声明符的开始。目前,这种用法有一些限制:属性用于在这个声明中声明过的标识符和所有之后的声明过的标识符(如果它包 括一个逗号分隔的声明符列表),而不仅是用于特定的声明符。当属性说明符跟在指针声明符“*”之后时,它们应该出现在任意一种修饰符序列之后,并且不能和 它们混在一起。接下来的陈述预期将来的语言符号仅使这种语法更有用。如果你对标准ISO C的声明符的说明格式熟悉的话,它将更好理解。
考虑
T D1
这样的声明(像C99标准6.7.5第四段中的子句),T包含声明说明符的地方说明一个Type类型(比如int
)并且D1
是一个包含标识符的标志的声明符。类型位标志被说明用来导出类型不包括在属性说明符中的声明符就像标准ISO C那样。如果
D1
有(属性说明符列表D
)这样的格式,并且T D
这样的声明为标志说明了“导出声明符类型列表 类型”的类型,这样T D1
为标志说明了“导出声明符类型列表 属性说明符列表 类型”的类型。如果
D1
有 * 类型修饰符和属性说明符列表 D这样的格式,并且T D
这样的声明为标志说明“导出声明符类型列表 类型”的类型,则T D1为标志说明“导出声明符列表 类型修饰符和属性说明符列表 类型”的类型。例如,
void (__attribute__((noreturn)) ****f)()
;说明“指向返回空的无返回值函数的指针的指针的指针”的类型。作为另外的例子,char *__attribute__((aligned(8))) *f
;说明“指向8位宽度的指向char
型数据的指针的指针”的类型。再次注意这个陈述是被期待将来的语法符号,不是当前实现的。