问题描述
回答另一个问题时,我想到了以下示例:
void *p; unsigned x = 17; assert(sizeof(void*) >= sizeof(unsigned)); *(unsigned*)&p = 17; // (1) memcpy(&p, &x, sizeof(x)); // (2)
第1行打破混叠规则.但是,第2行是可以的.混叠规则.问题是:为什么?编译器是否具有有关Memcpy等功能的特殊内置知识,或者还有其他一些使Memcpy可以的规则可以吗?是否有一种方法可以在标准C中实现类似memcpy的功能而不打破混叠规则?
推荐答案
c标准在上面很清楚. p命名的对象的有效类型为void*,因为它具有声明的类型,请参见6.5/6. C99中的混叠规则适用于读取和的写入,并且根据(1)中的unsigned lvalue写入void*是根据6.5/7的不确定行为.
相反,(2)的memcpy是可以的,因为unsigned char*可以别名任何对象(6.5/7).标准定义memcpy at 7.21.2/1为
对于本款中的所有函数,每个字符应被解释为具有unsigned char的类型(因此,每个可能的对象表示都是有效的,并且具有不同的值).
memcpy函数将n个字符从s2指向的对象复制到s1指向的对象.如果在重叠的对象之间进行复制,则该行为是未确定的.
然而,如果之后存在p,则可能会导致不确定的行为,具体取决于bitpattern.如果没有发生这样的用法,则该代码在C中很好.
根据 c ++标准,我认为这在这个问题上远不清楚,我认为以下内容存在.请不要将这种解释视为唯一的可能 - 模糊/不完整的规范为猜测留出了很多空间.
行(1)是有问题的,因为&p的对齐方式可能不正确.它将存储在p中的对象的类型更改为unsigned int.只要您以后不访问该对象,通过p,混杂规则不会破坏,但是对齐要求仍然可能存在.
line (2)但是没有对齐问题,因此只要您不访问p之后是void*,这可能会导致不确定的行为,这取决于void* void*的方式解释如何解释存储的BitPattern.我认为对象的类型不会因此而更改.
有一个长 gcc bugreport 通过这样的演员产生的指针写作以及与安置新的区别是什么(该列表上的人并不同意它是什么).