使用struct成员作为内存偏移的引用是否安全?

时间:2013-10-25 20:27:17

标签: c

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

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

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

来自C99第6.7.2.1节,

  

12

     

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

     

13

     

在结构对象中,非位字段成员和其中的单位   位字段驻留的地址按顺序增加   他们被宣布。适当地指向结构对象的指针   转换后,指向其初始成员(或者如果该成员是   位字段,然后到它所在的单元,反之亦然。   结构对象中可能有未命名的填充,但不在其中   开始。

     

15

     

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

鉴于上面的例子,是否保证reg1与reg0完全相差32位而不告诉编译器不添加填充?

1 个答案:

答案 0 :(得分:1)

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

因此编译器在这种情况下不应添加填充, 但你可以随时添加

__attribute__((__packed__))

可以肯定。

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

struct
{
    uint8_t  a;
    uint32_t b;
}

其中b将以未对齐的地址结束。

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