为什么即使使用 `char *` 也会收到 "type-punned" 警告?

Why I get a "type-punned" warning even when using a `char *`?

gcc (6.3.1 20170109)编译时如下程序

#include <stdio.h>

int main(int argc, const char *argv[]) {
    unsigned char x[] = {0x66, 0x19};
    printf("%i\n", ((short *)((char *)&x[0]))[0]);
    return 0;
}

生成警告:

pun.c: In function ‘main’: pun.c:5:5: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

使用 char 指针时不应允许类型别名?

以下是 C11(或至少是自由草案 N1570)关于别名的说法:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.

字符类型例外意味着你可以通过char*unsigned char*访问任何类型,但并不意味着你可以通过任何类型访问char*short* 不符合此处为 char* 列出的其他条件,因此此使用是未定义的行为。

此外,如果无条件允许,您可以打破对齐要求:

short x[] = {1, 2};
char* alias = x;
printf("%i\n", *(short*)&alias[1]);