我以为我很了解C,但是我被下面的代码弄糊涂了:
typedef struct {
int type;
} cmd_t;
typedef struct {
int size;
char data[];
} pkt_t;
int func(pkt_t *b)
{
int *typep;
char *ptr;
/* #1: Generates warning */
typep = &((cmd_t*)(&(b->data[0])))->type;
/* #2: Doesn't generate warning */
ptr = &b->data[0];
typep = &((cmd_t*)ptr)->type;
return *typep;
}
当我使用 GCC 编译时,我收到“解引用类型双关指针将破坏严格别名规则”警告。
为什么我会收到此警告?我在 char 数组中取消引用。将
char *
转换为任何内容都是合法的。这是数组与指针不完全相同的情况之一吗?为什么两个分配都没有生成警告?第二个任务等同于第一个,不是吗?
最佳答案
当启用严格别名时,允许编译器假设两个不同类型的指针(char*
vs cmt_t*
在这个例子中)不会指向相同的内存位置。这允许更大范围的优化,如果它们确实指向相同的内存位置,则您不希望应用这些优化。在此 question 中可以找到各种示例/恐怖故事.
这就是为什么在严格别名下,您必须小心处理类型双关语的原因。我相信该标准不允许任何类型的调整(不要引用我的话)但是大多数编译器都对 union 有豁免(我的 google-fu 未能打开相关的手册页) :
union float_to_int {
double d;
uint64_t i;
};
union float_to_int ftoi;
ftoi.d = 1.0;
... = ftoi.i;
不幸的是,这对您的情况不太适用,因为您必须将数组的内容 memcpy
放入并集,这不太理想。一种更简单的方法是通过 -fno-strict-aliasing
开关简单地关闭严格别名。这将确保您的代码是正确的,并且不太可能对性能产生重大影响(如果性能很重要,请进行测量)。
至于断线时为什么不出现警告,我就不知道了。很有可能对源代码的修改设法混淆了编译器的静态分析过程,以至于它看不到类型双关。请注意,负责检测类型双关的静态分析过程是无关的,并且不会与假定严格别名的各种优化过程对话。您可以将编译器完成的任何静态分析(除非另有说明)视为尽力而为的事情。换句话说,没有警告并不意味着没有错误,这意味着简单地拆分行并不能神奇地使您的类型双关安全。
关于c - 严格的别名和灵活的数组成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32833619/