【问题标题】:Do C struct members inherit compiler attributes?C struct 成员是否继承编译器属性?
【发布时间】:2021-02-15 14:03:13
【问题描述】:

对于大多数 C 编译器,可以在结构上指定编译器属性,以定义该结构的成员如何在内存中对齐。例如:

typedef struct{
    char a;
    char b;
} __attribute__((aligned(2))) TwoChars;

如果char a 最终位于地址 0xA(为简单起见),那么 char b 将不会位于地址 0xB,而是 0xC,因为它与 2 个字节对齐。

我的问题是:这个属性是由结构成员继承的吗?例如:

typedef struct{
    char a;
    char b;
} TwoChars;

typedef struct {
    TwoChars tc;
    char c;
} __attribute__((aligned(1))) ThreeChars;

这在记忆中最终会是什么样子? } __attribute__((aligned(2))) TwoChars 怎么样?

【问题讨论】:

  • 这可以用你最喜欢的编译器轻松测试...这些属性无论如何都是编译器扩展
  • 如果它是“继承的”,我会感到惊讶。出于一个简单的原因,可以分别编译两个不同的翻译单元,一个具有“普通”TwoChars 变量,一个具有ThreeChars。这些应该是兼容的。但是如果它是第二个TU的继承者,那么他们不会。
  • 是的,我想您的问题更适用于 packed 属性,如下面的答案所指出的那样。
  • 我用 GCC 标记了这个,因为它是一个非标准的编译器特定的扩展。让我知道是否可以。

标签: c gcc struct memory-alignment


【解决方案1】:

注意:这是一个非标准的编译器专用扩展。我在回答中提到的可能适用于 GCC 和 Clang,我认为这就是您所说的“大多数 C 编译器”。


可以在结构上指定编译器属性,定义该结构的成员如何在内存中对齐

不,__attribute__((aligned(...))) 不是这样工作的。 aligned 属性仅适用于结构本身,而不适用于其字段。见亲戚documentation page

如果要控制结构字段的对齐方式,则必须使用它们的属性。

这个:

struct {
    char a;
    char b;
} __attribute__((aligned(8))) foo = { 1, 2 };

将编译为:

foo:
    .byte   1
    .byte   2
    .zero   6

此时:

struct {
    char a __attribute__((aligned(2)));
    char b __attribute__((aligned(2)));
} bar = { 1, 2 };

会做你想做的:

bar:
    .byte   1
    .zero   1
    .byte   2
    .zero   1

因此:

此属性是否由结构成员继承?

没有。对齐不是结构成员继承的东西。


有趣的是,packed 属性是否被应用它的结构内的嵌套结构的成员继承,因为packed 专门用于由结构成员继承。文档没有明确说明是否是这种情况,但我们可以轻松检查。

以下代码:

struct foo {
    char a;
    int b;
    char c;
};

struct bar {
    struct foo d;
    char e;
    int f;
} __attribute__((packed));

struct bar x = {{101, 102, 103}, 104, 105};

编译为:

x:
    .byte   101
    .zero   3
    .long   102
    .byte   103
    .zero   3
    .byte   104
    .long   105        

从上面我们可以看出packed只适用于bar的成员,不会被foo的成员继承,而将__attribute__((packed))添加到foo会产生以下结果:

x:
    .byte   101
    .long   102
    .byte   103
    .byte   104
    .long   105

如果您想知道,这也适用于在其他结构中定义的结构:

struct bar {
    struct {
        char a;
        int b;
        char c;
    } d;
    char e;
    int c;
} __attribute__((packed));

struct bar x = {{101, 102, 103}, 104, 105};

生产:

x:
    .byte   101
    .zero   3
    .long   102
    .byte   103
    .zero   3
    .byte   104
    .long   105        

而将__attribute__((packed)) 添加到d 会产生:

x:
    .byte   101
    .long   102
    .byte   103
    .byte   104
    .long   105

【讨论】:

  • @EugeneSh。怀疑,packed 不接受任何争论。
  • 是的,不是字面意思。我的意思是,如果问到packed,这个问题是合法的
  • 可能aligned 不适用于成员,但将packed 应用于结构也会导致成员被打包。所以答案并不是说成员不继承父结构那么简单。成员是否有其他属性可以继承?
  • @EricPostpischil 展开。
【解决方案2】:

这样可以使 ThreeChars 至少与 2 对齐。

你可以自己测试一下:

typedef struct{
    char a;
    char b;
} __attribute__((aligned(2))) TwoChars;


typedef struct {
    TwoChars tc;
    char c;
} __attribute__((aligned(1))) ThreeChars;

_Static_assert(_Alignof(ThreeChars) == 2,"");

https://gcc.godbolt.org/z/9bTzbs

或者您可以在 https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html 阅读对齐属性的文档。

(不幸的是,标准化的_Alignas 使用起来不太方便,因为指定较低的对齐方式将违反约束。)

注意这里只指定了整个结构的对齐方式,所以虽然TwoChars tc; 将是双对齐的,因此会影响ThreeChars 的对齐方式,但后续成员的对齐方式不会高于自然对齐方式。

后一个事实可以通过_Static_asserting 来验证,即 be 的偏移量是 1 (_Static_assert(offsetof(TwoChars,b)==1,"");),正如自然对齐所暗示的那样,因此它不能是 2 对齐的。

【讨论】:

  • @MarcoBonelli 好吧,谢谢,但我相信只有最初的“是”被误导并且完全错误(现在已删除)。其余的,我相信,检查。
  • 是的,当我看到你的编辑时删除了我的评论。虽然如果你问我这不是很清楚。似乎您的第一行回答的问题与 OP 提出的问题不同。
猜你喜欢
  • 2011-02-26
  • 1970-01-01
  • 1970-01-01
  • 2017-01-15
  • 2010-11-10
  • 1970-01-01
  • 2015-08-18
  • 2012-02-13
  • 2016-11-13
相关资源
最近更新 更多