__attribute__ 在多个变量的定义中
__attribute__ in definitions of multiple variables
我有一个问题最好用例子来解释。请考虑以下代码:
unsigned char a,
b;
这显然定义了两个unsigned char
类型的变量。
如果我想让变量与 16 字节边界对齐,我第一个天真的方法是这样的:
__attribute__((aligned(16))) unsigned char a,
b;
我的问题是我不确定编译器是否总是将 __attribute__((aligned(16)))
应用于两个变量。
我特别担心,因为以下所有代码都没有错误或警告地编译:
unsigned char a __attribute__((aligned(16)));
unsigned char __attribute__((aligned(16))) b;
__attribute__((aligned(16))) unsigned char c;
根据我的研究,__attribute__((aligned(16)))
对上面三行中的相应变量执行相同的操作。但是这种弱语法对于 C 来说是不寻常的,所以我有点不信任。
回到我原来的问题,我知道我可以很容易地通过类似
的方式来避免不确定性
__attribute__((aligned(16))) unsigned char a;
__attribute__((aligned(16))) unsigned char b;
或者也许
unsigned char a __attribute__((aligned(16))),
b __attribute__((aligned(16)));
但是我很想知道在声明多个变量时添加__attribute__
修饰once是否足够all 应该有属性。
当然,这个问题涉及所有属性(不仅是 aligned
属性)。
作为奖励问题,将此类属性不仅添加到变量定义中,而且添加到变量声明中(例如在头文件中)是否被认为是一种好的风格?
是;两者
__attribute__((aligned(16))) unsigned char a, b;
和
unsigned char __attribute__((aligned(16))) a, b;
将 a
和 b
对齐到 16 字节边界。 gcc 将 __attribute__
作为类型的一部分处理(如 const
和 volatile
修饰符),以便混合
char * __attribute__((__aligned__(16))) * a;
也可以。
https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html#Attribute-Syntax 说:
An attribute specifier list may appear immediately before the comma, = or semicolon terminating the declaration of an identifier other than a function definition. Such attribute specifiers apply to the declared object or function
这就是为什么
unsigned char a __attribute__((aligned(16))), b;
仅适用于 a
但不适用于 b
。
另一种情况如
unsigned char a, __attribute__((aligned(16))) b;
只有 b
对齐。这里
An attribute specifier list may appear immediately before a declarator (other than the first) in a comma-separated list of declarators ... Such attribute specifiers apply only to the identifier before whose declarator they appear
来自 适用。
为了避免所有歧义,最好创建一个新类型并使用它。例如
typedef char __attribute__((__aligned__(16))) char_aligned_t;
char_alignedt d, d1;
有了这个例子和你的
unsigned char a __attribute__((aligned(16))), a1;
unsigned char __attribute__((aligned(16))) b, b1;
__attribute__((aligned(16))) unsigned char c, c1;
gcc 创建 (gcc -c
) 并且 readelf
显示描述的对齐方式
8: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM a
9: 0000000000000001 1 OBJECT GLOBAL DEFAULT COM a1 <<< not aligned!
10: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM b
11: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM b1
12: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM c
13: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM c1
14: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM d
15: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM d1
所有学分归于@ensc,因为 1) 他的回答是正确的,因为 2) 他让我在文档方面走上了正确的轨道。
但是,他给出的引文指出属性 未 应用于整个声明,而仅应用于相应的声明者。然后他给出了一些将属性应用于整个声明的示例。
起初我不明白为什么会这样,什么时候会这样,但现在在文档中找到了相应的说明。很难找到,因为它所在的段落很长并且有很多分散注意力的附加信息。
请考虑 GCC 文档 this page 中的“所有其他属性”部分。它包含以下段落(缩短并强调我的):
Any list of specifiers and qualifiers at the start of a
declaration may contain attribute specifiers, whether or not such a
list may in that context contain storage class specifiers. [...] All
attribute specifiers in this place relate to the declaration as a
whole. [...]
把上面的引文和@ensc 的回答中的引文放在一起,情况出奇地简单:
如果__attribute__
出现在声明的开头,则它适用于整个声明,即适用于所有声明符/声明的对象。
在所有其他情况下,它仅适用于它所在的特定声明符,即仅适用于相应的标识符或对象。
在上面的引文中唯一仍然可能产生误导的是术语“声明的开始”。 GCC 手册没有解释声明的开始到底是什么。
可能这个词是从众多C及相关规范中借用的,但我还没有找到一个简洁的定义。
根据测试结果,在
__attribute__((aligned(16))) unsigned char a,
b;
和
unsigned char __attribute__((aligned(16))) a,
b;
属性被认为是声明开头的说明符和限定符列表的一部分。
相比之下,在
unsigned char a __attribute__((aligned(16))),
b;
该属性显然(根据测试结果)不被认为是声明开头的说明符和限定符列表的一部分。
作为一个 non-native 说英语的我,这非常令人担忧:
我会将上述每个示例中的第一行视为声明的开头。值得注意的是,我会将第三个示例第一行中的说明符和限定符列表视为声明开头的一部分,尽管此列表(在本例中仅包含 __attribute__
部分)位于后面标识符名称。显然,我这样做是错误的。
请不要将此视为附加问题 - 它更多的是作为此答案的附加方面。也许 GNU 的人有一天正在阅读这篇文章并澄清文档:-)
我有一个问题最好用例子来解释。请考虑以下代码:
unsigned char a,
b;
这显然定义了两个unsigned char
类型的变量。
如果我想让变量与 16 字节边界对齐,我第一个天真的方法是这样的:
__attribute__((aligned(16))) unsigned char a,
b;
我的问题是我不确定编译器是否总是将 __attribute__((aligned(16)))
应用于两个变量。
我特别担心,因为以下所有代码都没有错误或警告地编译:
unsigned char a __attribute__((aligned(16)));
unsigned char __attribute__((aligned(16))) b;
__attribute__((aligned(16))) unsigned char c;
根据我的研究,__attribute__((aligned(16)))
对上面三行中的相应变量执行相同的操作。但是这种弱语法对于 C 来说是不寻常的,所以我有点不信任。
回到我原来的问题,我知道我可以很容易地通过类似
的方式来避免不确定性 __attribute__((aligned(16))) unsigned char a;
__attribute__((aligned(16))) unsigned char b;
或者也许
unsigned char a __attribute__((aligned(16))),
b __attribute__((aligned(16)));
但是我很想知道在声明多个变量时添加__attribute__
修饰once是否足够all 应该有属性。
当然,这个问题涉及所有属性(不仅是 aligned
属性)。
作为奖励问题,将此类属性不仅添加到变量定义中,而且添加到变量声明中(例如在头文件中)是否被认为是一种好的风格?
是;两者
__attribute__((aligned(16))) unsigned char a, b;
和
unsigned char __attribute__((aligned(16))) a, b;
将 a
和 b
对齐到 16 字节边界。 gcc 将 __attribute__
作为类型的一部分处理(如 const
和 volatile
修饰符),以便混合
char * __attribute__((__aligned__(16))) * a;
也可以。
https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html#Attribute-Syntax 说:
An attribute specifier list may appear immediately before the comma, = or semicolon terminating the declaration of an identifier other than a function definition. Such attribute specifiers apply to the declared object or function
这就是为什么
unsigned char a __attribute__((aligned(16))), b;
仅适用于 a
但不适用于 b
。
另一种情况如
unsigned char a, __attribute__((aligned(16))) b;
只有 b
对齐。这里
An attribute specifier list may appear immediately before a declarator (other than the first) in a comma-separated list of declarators ... Such attribute specifiers apply only to the identifier before whose declarator they appear
来自
为了避免所有歧义,最好创建一个新类型并使用它。例如
typedef char __attribute__((__aligned__(16))) char_aligned_t;
char_alignedt d, d1;
有了这个例子和你的
unsigned char a __attribute__((aligned(16))), a1;
unsigned char __attribute__((aligned(16))) b, b1;
__attribute__((aligned(16))) unsigned char c, c1;
gcc 创建 (gcc -c
) 并且 readelf
显示描述的对齐方式
8: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM a
9: 0000000000000001 1 OBJECT GLOBAL DEFAULT COM a1 <<< not aligned!
10: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM b
11: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM b1
12: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM c
13: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM c1
14: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM d
15: 0000000000000010 1 OBJECT GLOBAL DEFAULT COM d1
所有学分归于@ensc,因为 1) 他的回答是正确的,因为 2) 他让我在文档方面走上了正确的轨道。
但是,他给出的引文指出属性 未 应用于整个声明,而仅应用于相应的声明者。然后他给出了一些将属性应用于整个声明的示例。
起初我不明白为什么会这样,什么时候会这样,但现在在文档中找到了相应的说明。很难找到,因为它所在的段落很长并且有很多分散注意力的附加信息。
请考虑 GCC 文档 this page 中的“所有其他属性”部分。它包含以下段落(缩短并强调我的):
Any list of specifiers and qualifiers at the start of a declaration may contain attribute specifiers, whether or not such a list may in that context contain storage class specifiers. [...] All attribute specifiers in this place relate to the declaration as a whole. [...]
把上面的引文和@ensc 的回答中的引文放在一起,情况出奇地简单:
如果
__attribute__
出现在声明的开头,则它适用于整个声明,即适用于所有声明符/声明的对象。在所有其他情况下,它仅适用于它所在的特定声明符,即仅适用于相应的标识符或对象。
在上面的引文中唯一仍然可能产生误导的是术语“声明的开始”。 GCC 手册没有解释声明的开始到底是什么。
可能这个词是从众多C及相关规范中借用的,但我还没有找到一个简洁的定义。
根据测试结果,在
__attribute__((aligned(16))) unsigned char a,
b;
和
unsigned char __attribute__((aligned(16))) a,
b;
属性被认为是声明开头的说明符和限定符列表的一部分。
相比之下,在
unsigned char a __attribute__((aligned(16))),
b;
该属性显然(根据测试结果)不被认为是声明开头的说明符和限定符列表的一部分。
作为一个 non-native 说英语的我,这非常令人担忧:
我会将上述每个示例中的第一行视为声明的开头。值得注意的是,我会将第三个示例第一行中的说明符和限定符列表视为声明开头的一部分,尽管此列表(在本例中仅包含 __attribute__
部分)位于后面标识符名称。显然,我这样做是错误的。
请不要将此视为附加问题 - 它更多的是作为此答案的附加方面。也许 GNU 的人有一天正在阅读这篇文章并澄清文档:-)