【问题标题】:__attribute__ in definitions of multiple variables__attribute__ 在多个变量的定义中
【发布时间】:2020-08-04 11:10:02
【问题描述】:

我有一个问题,最好用例子来解释。请考虑以下代码:

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)));

但我真的很想知道在声明 all 应该具有属性的多个变量时添加__attribute__ 装饰一次 是否足够。

当然,这个问题涉及所有属性(不仅仅是aligned 属性)。

作为一个额外的问题,将这些属性不仅添加到变量定义中,还添加到变量声明中(例如在头文件中)是否被认为是一种好的样式?

【问题讨论】:

  • 最好每行声明一个变量
  • 这个问题在这里得到解答:stackoverflow.com/a/31067623/5639126
  • @secretsquirrel 我认为这个答案是错误的(通过解释 gcc 文档和使用 gcc 10 对其进行测试)
  • @secretsquirrel 我也做了额外的研究并确认 ensc 是正确的。但是,他的回答仅引用了说明属性在何种情况下适用于特定声明对象的段落,而不是声明中的所有对象,因此这可能仍然令人担忧。解决方案在他链接的文档中:GNU 文档确实说明了该属性在何种情况下适用于整个声明,即声明中的所有声明对象。不过,相应的句子很难找到。
  • @secretsquirrel 如果我会在相应的引文中写一个额外的答案是否合适?

标签: c gcc gcc-attribute


【解决方案1】:

是的;两者都有

__attribute__((aligned(16))) unsigned char   a, b;

unsigned char __attribute__((aligned(16)))    a, b;

ab 对齐到 16 字节边界。 gcc 将 __attribute__ 处理为类型的一部分(如 constvolatile 修饰符),以便混合诸如

char * __attribute__((__aligned__(16))) *  a;

也可以。

https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html#Attribute-Syntax 说:

属性说明符列表可能紧接在逗号、= 或分号之前,以终止函数定义以外的标识符的声明。此类属性说明符适用于声明的对象或函数

这就是为什么

unsigned char   a __attribute__((aligned(16))), b;

仅适用于a,但不适用于b

另外一种情况

unsigned char   a, __attribute__((aligned(16))) b;

只有b 对齐。这里

一个属性说明符列表可能会出现在一个以逗号分隔的声明符列表中的声明符之前(除了第一个)......这样的属性说明符仅适用于它们出现在其声明符之前的标识符

来自https://stackoverflow.com/a/31067623/5639126 申请。

为了避免所有的歧义,最好创建一个新类型并使用它。例如

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

【讨论】:

  • 非常感谢您提供这个经过深思熟虑的答案和引用。我查看了文档的错误位置,即描述了aligned 属性的位置(gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html)。我刚刚看到那里有一个链接可以解释语法;我第一次没有注意到那个链接,可能是因为我太专注于特定的属性。
  • 非常感谢您的回答。属性语法的 GNU 文档不容易阅读……对我来说,这几乎就像我在阅读比赛中的混淆代码一样。你的解释真的很清楚。
【解决方案2】:

所有功劳都归于@ensc,因为 1) 他的回答是正确的,并且因为 2) 他让我在文档方面走上了正轨。

但是,当属性为 时,他给出的引用状态适用于整个声明,而仅适用于相应的声明符。然后他给出了一些将属性应用于整个声明的示例。

我最初不明白为什么以及何时会出现这种情况,但现在在文档中找到了相应的声明。很难找到,因为它所在的段落很长,并且有很多分散注意力的附加信息。

请考虑 GCC 文档的this page 中的“所有其他属性”部分。它包含以下段落(缩短和强调我的):

任何说明符和限定符列表在一个开头 声明可以包含属性说明符,无论是否 list 可能在该上下文中包含存储类说明符。 [...] 全部 这里的属性说明符与声明相关 整个。 [...]

将上述引文和@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;

该属性显然(根据测试结果)被认为是声明开头的说明符和限定符列表的一部分。

对于非英语母语的我来说,这非常令人担忧:

我会认为上面每个示例中的第一行是声明的开始。值得注意的是,我会认为第三个示例的第一行中的说明符和限定符列表是声明开头的一部分,尽管此列表(在本例中仅包含 __attribute__ 部分)位于标识符之后姓名。显然,我这样做是错误的。

请不要将此视为附加问题 - 它更多地意味着此答案的附加方面。也许 GNU 的人有一天会阅读这篇文章并澄清文档:-)

【讨论】:

    猜你喜欢
    • 2016-06-03
    • 1970-01-01
    • 2019-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-19
    • 1970-01-01
    • 2022-08-20
    相关资源
    最近更新 更多