【问题标题】:Are struct padding bytes preserved by member assignment?成员分配是否保留了结构填充字节?
【发布时间】:2017-02-23 09:48:46
【问题描述】:

示例代码:

#include <assert.h>

struct S
{
    unsigned char ch;
    int i;
};

int main()
{
    struct S s;

    memset(&s, 0, sizeof s);

    s.ch = 257; 

    assert( 0 == ((unsigned char *)&s)[1] );
}

断言会失败吗?

这个问题的动机是小端系统上的编译器是否可以决定使用 4 字节存储来实现s.ch = 257;。显然,没有人会像我在示例中那样编写代码,但如果在程序中以各种方式分配 ch,然后继续使用 memcmp 检查结构是否相等,则可能会发生类似的事情。

例如,如果代码使用--s.ch 而不是s.ch = 257 - 编译器可以发出字长递减指令吗?

我认为围绕DR 451 的讨论无关紧要,因为这只适用于未初始化的填充;但是memset 将所有填充初始化为零字节。

【问题讨论】:

    标签: c struct language-lawyer padding


    【解决方案1】:

    ISO/IEC 9899:2011 §6.2.6.1 (类型的表示)一般说:

    ¶6 当值存储在结构或联合类型的对象中时,包括在成员对象中,对应于任何填充字节的对象表示的字节采用未指定的值。51)结构或联合对象的值永远不是陷阱表示,即使结构或联合对象的成员的值可能是陷阱表示。

    51) 因此,例如,结构赋值不需要复制任何填充位。

    但是,您的示例没有进行结构分配,因此可能不适用。我相信没有理由认为分配给结构的简单类型成员会修改数据。

    但是,您的 assert 代码确实表现出未定义的行为,试图访问结构填充,这是根本不允许的。

    因此,断言不太可能触发,但由于您的代码表现出未定义的行为,它可能会发生并且您没有追索权。

    【讨论】:

    • 我在您的引文中忽略了“包括在成员对象中”。这似乎回答了我的问题。但我不明白为什么访问结构填充(未初始化)会是未定义的行为,你能提供一些支持吗?
    • 我相信“访问填充”是未定义的行为;它的初始化状态无关紧要,因为您无法访问它。但是,我可能需要一段时间才能找到标准的相关部分……
    • 做笔记——这可能是也可能不是我答案的最后补充:§7.14.4.1 memcmp 函数 memcmp 函数比较前 n 个字符s1 指向的对象的前 n 个字符到 s2.310) 指向的对象的前 n 个字符) — 310) 用作结构对象内对齐目的的填充的 ''holes'' 的内容是不确定的。比其分配空间短的字符串和联合也可能导致比较问题。
    • 更多注释 — §6.7.9 初始化 ¶10 … 如果具有静态或线程存储持续时间的对象未显式初始化,则: … — 如果它是聚合,每个成员都根据这些规则进行初始化(递归),并且任何填充都被初始化为零位; — 如果是联合,则根据这些规则(递归)初始化第一个命名成员,并将任何填充初始化为零位; ——这可能不是密切相关的。
    • 更多注释 — §6.7.2.1 结构和联合说明符 ¶15 ...结构对象中可能有未命名的填充,但不是在其开头。 …¶17 在结构或联合的末尾可能有未命名的填充。 - 部分相关,但没有说明对填充的访问是禁止的(尽管没有引用填充的有效方法,所以我仍然看不到如何定义访问它的行为)。如果您想知道,我在标准 PDF 中使用的搜索词是“填充”。不过,我可能不得不使用替代术语来找到我正在寻找的答案。
    【解决方案2】:

    是的,它可能会失败。行为未指定,但并非未定义。

    赋值s.ch = 257;后,所有填充位的值都取未指定值1,也就是说,如果结构的第二个字节是填充字节,则取未指定值,未指定与零的比较结果。它可能触发或不触发。

    断言中的读取值不能是陷阱表示,因为 unsigned char 没有陷阱表示,并且因为该值是未指定的,不是不确定的。


    1(引自:ISO/IEC 9899:201x 6.2.6.1 General 6):
    当值存储在结构或联合类型的对象中时,包括在成员中 对象,对应于任何填充字节的对象表示的字节采用 未指定的值。

    【讨论】:

      猜你喜欢
      • 2012-10-27
      • 2011-09-08
      • 2018-05-06
      • 2017-11-17
      • 2016-09-18
      • 1970-01-01
      • 2017-11-07
      • 2021-08-25
      • 2021-03-25
      相关资源
      最近更新 更多