【发布时间】:2013-11-05 02:31:41
【问题描述】:
在 ARM 信息中心网站上,他们有 this 建议使用结构将变量映射到内存地址。
#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;
但是,我看到其他人建议编译器可以在结构对象的成员之间添加填充,并且确保不会发生这种情况的唯一方法是使用打包属性,例如在 GCC __attribute__((__packed__)) 中。
在我看来,填充只会由编译器引入以对齐成员边界,但我在 C99 标准中没有看到它明确指出在其他情况下不应该发生这种情况。事实上,它似乎说它可能会发生。
来自 C99 第 6.7.2.1 节,
12
结构或联合对象的每个非位域成员都是对齐的 以适合其类型的实现定义的方式。
13
在结构对象中,非位域成员和单元 位域驻留的地址按顺序增加 他们被宣布。指向结构对象的指针,适当地 转换后,指向其初始成员(或者如果该成员是 位域,然后到它所在的单元),反之亦然。 结构对象内可能有未命名的填充,但在其 开始。
15
结构或联合的末尾可能有未命名的填充。
鉴于上面的例子,在不告诉编译器不要添加填充的情况下,是否保证 reg1 与 reg0 的偏移量正好是 32 位?
【问题讨论】:
-
您始终可以使用
uint32_t[3]并确保安全。 -
立即+1 以获得不错的选择。不过,我仍然想知道这件事。如果这是一个坏主意,我想从我以后做的任何事情中消除它。
-
@KerrekSB 我确实发现了一些我以前没有真正考虑过的东西。虽然 uint32_t[3] 的大小几乎可以保证等于其所有元素组合的大小,但像 Port[3] 这样的东西却不是。
-
如果你使用这样的地址,那么无论如何你都会被绑定到一些特定的架构上。此代码 AFAIK 的可移植性不大。因此,检查汇编输出是否为您的机器创建了正确的代码,然后它就可以工作了。如果您打算便携,那么查看标准最有兴趣,在这种情况下我并没有真正看到。
-
不完全是:
Port[3]的大小也是Port的三倍,但不一定一定是uint32_t的九倍。
标签: c