在C中取消引用对齐的空结构?

问题描述 投票:0回答:1

我在SameBoy模拟器(v0.13)中遇到了一组奇怪的宏,该宏似乎使用空结构来寻址数据。看起来像这样:

#define GB_PADDING(type, old_usage) type old_usage##__do_not_use

#define GB_SECTION(name, ...)     \
        __attribute__ ((aligned (8))) struct {} name##_section_start;  \
        __VA_ARGS__;   \
        struct {} name##_section_end
#define GB_SECTION_OFFSET(name)   \
        (offsetof(GB_gameboy_t, name##_section_start))
#define GB_SECTION_SIZE(name)     \ 
        (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start))
#define GB_GET_SECTION(gb, name)  \
        ((void*)&((gb)->name##_section_start))

似乎GB_gameboy_t是某种类型的(可能是对于GameBoy内部结构)。但是,令我困扰的部分是GB_SECTIONGB_GET_SECTION宏。显然,这些宏的目的是对齐数据。但是,我对空结构(标记为name##_section_start)扩展到的内容迷失了。 它是否扩展为空(即0字节)?如果是这样,则GB_GET_SECTION将指向任何__VA_ARGS__。但是,__attribute__ ((aligned (8)))限定词的意义何在?或空结构是否扩展为某些垃圾填充字节?如果这样做,则GB_GET_SECTION将指向垃圾数据。

那是哪一个?

c gcc struct padding memory-alignment
1个回答
0
投票

标准C不允许使用空结构,但gcc提供了extension。它们完全看起来像,是一个大小为0的对象,并且完全按照您的期望执行操作,实际上什么也没做。他们没有成员可以访问。您可以将一个分配给另一个,但是这是一项禁止操作的操作。在这种情况下,它们最可用作占位符。

__attribute__((aligned (8)))会执行与通常相同的操作:保证具有此属性的对象在8字节边界上对齐。换句话说,其地址将是8的倍数。

在此程序中,宏用于将大型结构的成员划分为“节”,每个节均以8字节边界开始,并创建零字节的空结构成员以标记结构的开始和结束。每个部分。该代码看起来像:

struct GB_gameboy_s {
    GB_SECTION(foo, int a; short b;);
    GB_SECTION(bar, char c; char d;);
};

typedef struct GB_gameboy_s GB_gameboy_t;

展开为

struct GB_gameboy_s {
    __attribute__ ((aligned (8))) struct {} foo_section_start;
    int a;
    short b;
    struct {} foo_section_end;
    __attribute__ ((aligned (8))) struct {} bar_section_start;
    short c;
    char d;
    struct {} bar_section_end;
};

因此结构的布局类似于:

  • [foo_section_start:偏移量0,大小0
  • [a:偏移量0,大小4
  • [b:偏移量4,大小2
  • foo_section_end:偏移量6,大小为0
  • bar_section_start:偏移量8,大小为0
  • [c:偏移量8,大小2
  • [d:偏移10,大小1
  • [bar_section_end:偏移量11,大小0

注意aligned属性已确保bar_section_start以及c放置在偏移8处,而不是放置在偏移6处。在结构的字节7和8中有填充,但是请注意,此填充<< before >> bar_section_start,因为为了使对齐有意义,必须填充。 bar_section_start指向填充的第一个字节after,而不是填充本身。现在,可以使用offsetof查找这些成员的偏移量,并像GB_SECTION_SIZE一样使用它来计算每个节的大小。例如,here您可以看到他们将各种成员集写入文件,从而使用[]这样的代码来保存虚拟机状态的一部分。

fwrite(GB_GET_SECTION(bar), GB_SECTION_SIZE(bar), 1, fd)

这具有写入结构的字节8到10的效果,即cd成员。这比一个接一个地写出所需的成员要方便得多,特别是因为在实际代码中有两个以上的成员。

尚不清楚为什么需要对齐,但是如果写入转储文件的所有内容都是8字节的倍数,则可能更方便。到处复制对齐的缓冲区也可能更有效率。

他们本可以为char成员使用start/end或某些其他标准类型,但是结构会不必要地变大。例如,在那种情况下,a不能放置在偏移量0处,因此将放置在偏移量4处,以便为它提供与int一样的4字节对齐方式。 b将位于偏移量8,而bar_section_start将位于偏移量16。这意味着foo节将使用16个字节而不是8个字节,这浪费了一定数量的内存和磁盘空间(尽管实际上这不太可能非常重要)

© www.soinside.com 2019 - 2024. All rights reserved.