使用void *键入punning而不破坏C99中的严格别名规则
我最近遇到了严格的别名规则,但是我无法理解如何使用void *
来执行类型惩罚而不违反规则。
我知道这违反了规则:
int x = 0xDEADBEEF; short *y = (short *)&x; *y = 42; int z = x;
而且我知道我可以安全地使用C99中的联合进行类型惩罚:
union{ int x; short y; } data; data.x = 0xDEADBEEF; data.y = 42; int z = data.x;
但是如何在C99中使用void *
来安全地执行类型惩罚呢? 以下是正确的:
int x = 0xDEADBEEF; void * helper = (void *)&x; short *y = (short *)helper; *y = 42; int z = x;
我怀疑代码仍然会破坏严格的别名规则,因为变量x
地址的内存可以被x
和解引用的y
。
如果通过void *
未定义类型 – 双关语,那么C99中void *
的目的是什么?
void *
与类型惩罚无关。 其主要目的是:
-
允许通用分配和释放操作,这些操作不关心调用者在那里存储的对象的类型(例如
malloc
和free
)。 -
允许调用者通过函数将指针传递给任意类型,该函数将通过回调传递回来(例如
qsort
和pthread_create
)。 在这种情况下,编译器不能强制执行类型检查; 在编写调用者和回调时, 您有责任确保回调访问具有正确类型的对象。
指向void
指针也用于实际操作对象的一些地方(如memcpy
),作为对象的重叠unsigned char []
表示。 这可以看作是类型惩罚,但它不是别名冲突,因为允许使用char
类别来访问其表示的任何内容。 在这种情况下, unsigned char *
也可以工作,但void *
的优点是指针会自动转换为void *
。
在您的示例中,由于原始类型是int
而不是union,因此没有合法的方式来输入pun并将其作为short
访问。 您可以将x
的值复制到union,在那里执行明确定义的类型,然后将其复制回来。 一个好的编译器应该完全省略副本。 或者,您可以将写入分解为char
写入,然后它将是合法别名。