3

在 ARM 信息中心网站上,他们提出了使用结构将变量映射到内存地址的建议。

#define PORTBASE 0x40000000

typedef struct Port
{
    uint32_t reg0;
    uint32_t reg1;
    uint32_t reg2;
} Port;

volatile struct Port * const reg_p = (struct Port *)PORTBASE;

但是,我看到其他人建议编译器可以在 struct 对象的成员之间添加填充,并且确保不会发生这种情况的唯一方法是使用 packed 属性,例如在 GCC__attribute__((__packed__))中。

在我看来,填充只会由编译器引入以对齐成员边界,但我在 C99 标准中没有看到它明确指出在其他情况下不应该发生这种情况。事实上,它似乎说它可能会发生。

从 C99 第 6.7.2.1 节开始,

12

结构或联合对象的每个非位域成员都以适合其类型的实现定义的方式对齐。

13

在结构对象中,非位域成员和位域所在的单元的地址按声明顺序递增。一个指向结构对象的指针,经过适当的转换,指向它的初始成员(或者如果该成员是位域,则指向它所在的单元),反之亦然。结构对象中可能有未命名的填充,但不是在其开头。

15

在结构或联合的末尾可能有未命名的填充。

给定上面的例子,在不告诉编译器不要添加填充的情况下,是否保证 reg1 与 reg0 正好偏移 32 位?

4

1 回答 1

1

添加了成员​​之间的填充,以便值与 32 位对齐(取决于体系结构),例如 uint32_t 应该从对齐的偏移量 4*2^x 开始,以便更快地访问,因为可以使用普通的 32 位指针,因为您可以使用 struct仅由 32 位值组成,如果 PORTBASE 是对齐地址,则自动为真。

因此,编译器在这种情况下不应添加填充,但您始终可以添加

__attribute__((__packed__))

为了确定。

编译器会在以下情况下添加填充:

struct
{
    uint8_t  a;
    uint32_t b;
}

其中 b 最终会出现在未对齐的地址上。

问题是对于 arm,您可能会得到不完全兼容 C99 的编译器。

于 2013-10-26T11:10:52.143 回答