我以为我很了解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 *
投射到任何内容都是合法的。这是一个数组与指针不完全相同的情况吗?
为什么两个作业都不会产生警告?第二个任务相当于第一个,不是吗?
答案 0 :(得分:0)
当打开严格别名时,允许编译器假设两个不同类型的指针(在此实例中为char*
vs cmt_t*
)不会指向相同的内存位置。这允许更大范围的优化,如果它们确实指向相同的内存位置,则不希望应用这些优化。可以在question中找到各种示例/恐怖故事。
这就是为什么,在严格别名下,你必须小心你如何打字。我相信这个标准不允许任何类型的惩罚(不要引用我),但大多数编译器都对工会有豁免(我的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
开关关闭严格别名。这将确保您的代码是正确的,并且不太可能产生重大的性能影响(如果性能很重要,请进行衡量)。
至于为什么当线路被打破时警告没有出现,我不知道。有可能对源代码的修改设法混淆了编译器的静态分析通过,以至于它没有看到类型惩罚。请注意,负责检测类型 - 双关语的静态分析通道是不相关的,并且不会与假定严格别名的各种优化通道进行对话。您可以将编译器完成的任何静态分析(除非另有说明)视为尽力而为的类型。换句话说,没有警告并不意味着没有错误,这意味着简单地分解线并不会神奇地使你的打字安全。