【问题标题】:Is it safe to use struct members as references to memory offsets?使用结构成员作为对内存偏移量的引用是否安全?
【发布时间】: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


【解决方案1】:

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

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

__attribute__((__packed__))

确定。

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

struct
{
    uint8_t  a;
    uint32_t b;
}

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-11
    • 2015-11-02
    • 2014-07-22
    • 2019-06-23
    • 1970-01-01
    • 2020-06-22
    • 1970-01-01
    相关资源
    最近更新 更多