R..*_*R.. 14 c strict-aliasing language-lawyer
例如,此代码是有效的,还是通过违反别名规则来调用未定义的行为?
int x;
struct s { int i; } y;
x = 1;
y = *(struct s *)&x;
printf("%d\n", y.i);
Run Code Online (Sandbox Code Playgroud)
我的兴趣在于使用基于此的技术来开发用于执行别名读取的可移植方法.
更新:这是预期的用例,有点不同,但当且仅当上述内容有效时才有效:
static inline uint32_t read32(const unsigned char *p)
{
struct a { char r[4]; };
union b { struct a r; uint32_t x; } tmp;
tmp.r = *(struct a *)p;
return tmp.x;
}
Run Code Online (Sandbox Code Playgroud)
GCC根据需要将其编译为单个32位负载,并且它似乎避免了如果p
实际指向除其他类型之外可能发生的混叠问题char
.换句话说,它似乎是GNU C __attribute__((__may_alias__))
属性的可移植替代品.但我不确定它是否真的定义明确......
我相信这仍然会违反有效的打字规则.您希望访问未明确声明的内存位置(或在动态分配的情况下通过存储隐式声明),因为它包含struct a
通过该类型的表达式.
其他答案中引用的部分都不能用于逃避此基本限制.
但是,我相信您的问题有一个解决方案:使用__builtin_memcpy()
,即使在独立环境中也可以使用(请参阅手册条目-fno-builtin
).
请注意,这个问题不像我说的那样清晰.C11第6.5节第7节告诉我们,通过左值表达式访问对象是可以的,该表达式具有聚合或联合类型,其中包含其成员中的上述类型之一.
C99基本原理清楚地说明了这个限制,因此指向聚合的指针和指向其成员之一的指针可能是别名.
我认为以第一个例子的方式使用这个漏洞的能力(但不是第二个例子,假设p
没有指向实际的char [4]
)是一个意想不到的结果,标准只是由于措辞不精确而无法拒绝.
另请注意,如果第一个示例有效,我们基本上可以将结构类型转换为其他名义上键入的语言.除了共同的初始子序列的联合中的结构(即使这样,成员名称也很重要),相同的内存布局也不足以使类型兼容.我相信同样的道理也适用于此.