示例代码:
#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
将所有填充初始化为零字节。
答案 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
代码确实显示未定义的行为,尝试访问结构填充,这是不允许的。
因此,断言不太可能触发,但由于你的代码表现出不明确的行为,它可能会发生而且你没有追索权。