使用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位存储更有效。该标准的作者不希望禁止在有用的平台上进行此类处理。