结构填充字节是否由成员赋值保留?

时间:2016-10-14 04:45:17

标签: c struct language-lawyer padding

示例代码:

#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将所有填充初始化为零字节。

2 个答案:

答案 0 :(得分:3)

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

在赋值s.ch = 257;之后,所有填充位的值采用未指定的值 1 ,这意味着,如果结构的第二个字节是填充字节,则它采用未指定的值并且未指定比较结果为零。它可能触发与否。

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

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

答案 1 :(得分:0)

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

  

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

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

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

但是,您的assert代码确实显示未定义的行为,尝试访问结构填充,这是不允许的。

因此,断言不太可能触发,但由于你的代码表现出不明确的行为,它可能会发生而且你没有追索权。