c - 严格的别名和灵活的数组成员

标签 c arrays strict-aliasing

我以为我很了解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 编译时,我收到“解引用类型双关指针将破坏严格别名规则”警告。

  1. 为什么我会收到此警告?我在 char 数组中取消引用。将 char * 转换为任何内容都是合法的。这是数组与指针不完全相同的情况之一吗?

  2. 为什么两个分配都没有生成警告?第二个任务等同于第一个,不是吗?

最佳答案

当启用严格别名时,允许编译器假设两个不同类型的指针(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/

相关文章:

c - 反向单链表

java - 将 java.awt.Image 对象转换为字节数组 (`byte[]` )

arrays - 默认值分配中的 Bash 数组值

c - 与聚合或 union 类型相关的严格别名

c - 需要帮助解决警告 : dereferencing type-punned pointer will break strict-aliasing rules

c - C11限制的正式定义与实现是否一致?

c - Typedef 指向二维数组的指针

c - tmpfile 中的安全漏洞是什么,tmpfile_s 是如何解决的?

arrays - 在 C 中打印指针本身

c - 在 C 中具有严格别名和严格对齐的面向对象模式的最佳实践