使用void*键入punning,而不破坏C99中的严格别名规则
我最近遇到了严格的别名规则,但我很难理解如何使用使用void*键入punning,而不破坏C99中的严格别名规则,c,c99,void-pointers,strict-aliasing,type-punning,C,C99,Void Pointers,Strict Aliasing,Type Punning,我最近遇到了严格的别名规则,但我很难理解如何使用void*在不违反规则的情况下执行类型双关 我知道这违反了规则: int x = 0xDEADBEEF; short *y = (short *)&x; *y = 42; int z = x; 我知道我可以安全地使用C99中的union进行类型双关: union{ int x; short y; } data; data.x = 0xDEADBEEF; data.y = 42; int z = data.x; 但
void*
在不违反规则的情况下执行类型双关
我知道这违反了规则:
int x = 0xDEADBEEF;
short *y = (short *)&x;
*y = 42;
int z = x;
我知道我可以安全地使用C99中的union进行类型双关:
union{
int x;
short y;
} data;
data.x = 0xDEADBEEF;
data.y = 42;
int z = data.x;
但是如何使用void*
在C99中安全地执行类型双关?以下各项是否正确:
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\u create
)。在这种情况下,编译器无法强制执行类型检查;编写调用方和回调时,您有责任确保回调使用正确的类型访问对象void
的指针也在一些地方使用(如memcpy
),它们实际上在对象上作为对象的覆盖无符号字符[]
表示进行操作。这可以看作是类型双关,但它不是一个别名冲突,因为char
类型可以对任何内容进行别名,以访问其表示形式。在这种情况下,unsigned char*
也可以工作,但是void*
具有指针自动转换为void*
的优点
在您的示例中,由于原始类型是
int
而不是联合,因此没有合法的方法键入pun并以short
的形式访问它。您可以将x
的值复制到联合体,在那里执行定义良好的类型双关,然后将其复制回来。一个好的编译器应该完全省略副本。或者,您可以将写操作分解为char
写操作,然后使用合法别名。哦,我明白了,所以在使用void*
的情况下,程序员应该已经知道要转换的正确类型;由于使用了原始数据类型(即int*
->void*
->int*
),因此不会发生类型双关。我可能错了,但不是只有char
才允许对任何内容进行别名,而不是无符号char
。@Arnout:不,这是任何字符类型(有三种:char
、signed char
和unsigned char
)。表示法是根据无符号字符
定义的,但事实上,无符号字符
是用于可移植访问它的表示法,因为有符号类型有表示法唯一性和/或完整性问题,除非有符号表示法是两个补码。@R“您可以在union中进行类型双关”。但我在C99规范:6.2.6.1 General“7中发现。当值存储在union类型的对象的成员中时,对象表示形式中不对应于该成员但却对应于其他成员的字节采用未指定的值。。换句话说,如果您存储union.short=value;然后将其读取为union.int,则union的.int部分未指定,并且远离“定义良好的类型双关”是吗?还是我遗漏了什么?@VitBernatik:你引用的语言之所以存在,是因为有些平台的大型存储有时比小型存储便宜。例如,如果一个实现支持8位和32位存储,但不支持16位存储,并且一个程序包含一个结合了16位short
和32位的并集float
,使用32位存储处理对short
成员的写入可能比使用两个8位存储更有效。该标准的作者不希望禁止在有用的平台上进行此类处理。