【问题标题】:Aliasing array with pointer-to-struct without violating the standard在不违反标准的情况下使用指向结构的指针对数组进行别名
【发布时间】:2013-06-09 06:45:10
【问题描述】:

阅读this 我了解到,如果结构具有兼容的成员,即给定以下结构,您可以对结构进行别名(即不违反标准):

typedef struct {
    uint32_t a;
    uint32_t b;
} Frizzly;

以下内容会破坏别名规则:

uint32_t foo(uint16_t *i) {
    Frizzly *f = (Frizzly *)i;
    return f->a;
}

但以下不会:

uint32_t foo(uint32_t *i) {
    Frizzly *f = (Frizzly *)i;
    return f->b;
}

因为所讨论的“聚合类型”包含与我们要转换为它的指针兼容的类型,即指向类型uint32_t 的指针可以转换为包含类型成员(或成员)的结构uint32_t 不破坏别名规则。

首先,我是否理解正确?

其次,结构中(其他)变量的顺序和类型是否重要?假设Frizzly 定义如下:

typedef struct {
    uint16_t b[2];
    uint32_t a;
}

在第二个示例中的强制转换之后,b 现在由不兼容 (uint32_t) 类型的内存支持。转换是否仍然有效(或者更确切地说,通过转换的指针访问值)? a 的任一元素的更改是否会改变 i 的第一个元素的值(反之亦然),就像禁用了严格别名一样?

另外,如果上述内容有效,如果我有一个这样的结构怎么办:

typedef struct {
    void *m;
    uint16_t hooah[4];
} Bar;

如果我没记错的话,下面的演员表会破坏别名规则:

void test(char *boo, size_t dee) {
    Bar *bar = (Bar *)(boo + dee);
    do_other_stuff(bar);
}

我可以通过将单个unsigned char 成员添加到结构中来使强制转换有效吗?换句话说,转换不兼容类型的指针通常会破坏别名规则,但是由于从指向包含X 类型成员的结构的指针转换为指向X 的指针是一个例外,因此任何从指针到的转换都可以-X 到聚合-Y 只需将 X 类型的(可能是虚拟的)成员添加到 Y 中就有效?

(我实际上并没有在编译器中测试上面的代码sn-ps。)

编辑:

我知道我的措辞和示例可能相当糟糕,所以我将尝试重新表述这个问题:如果我理解正确,指向结构的指针对类型为“X”的元素数组进行别名是合法的只要结构包含“X”类型的成员。现在,当取消引用结构的成员时,该成员是否必须是“X”类型,或者是为结构的所有成员制定的严格别名规则的例外,无论它们的类型如何只要有一个适当类型的成员?

【问题讨论】:

  • 我总是看到人们将两者结合起来并使用它。这样一来,您总能得到两者的最严格版本。
  • 如果它编译,它不会违反标准。没有?
  • @basin - 不正确,在任何情况下,技术上的别名要求都与通过指针的访问有关,因此只能在运行时违反(尽管足够聪明的编译器 可能 捕获 一些编译过程中出现这样的问题。)

标签: c alias strict-aliasing


【解决方案1】:

根据ISO/IEC9899/TC2 第 6.7.2.1 节第 13 段:

一个指向结构对象的指针,经过适当转换,指向它的初始成员(或者如果该成员是位域,则指向它所在的单元),反之亦然

因此,只要您将结构指针转换为第一个成员的指针类型,它就不应违反严格别名(在第 6.5 节第 7 节中指定),也可以通过以下方式访问元素

在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含联合的成员)

但这只能在另一个方向上起作用(通过结构指针访问成员,而不是通过成员指针访问结构)

【讨论】:

  • 所以第二个引用的意思是说如果我通过结构指针访问成员a,只要结构指针别名与 any 兼容的位置,它就有效i> 结构的成员,即使a 是不兼容的类型?
  • 没有。据我了解,它实际上只说明如果您写入结构指针,您可以影响任何指向任何成员类型的指针所指向的存储。但是并不暗示相反,因此您可以通过 uint32 指针影响任何 uint32 成员,但不能影响其他任何成员。由于第一个引号,我相信如果你的结构有一个 uint32 作为第一个成员,那么会有一个例外。
【解决方案2】:

您的任何示例都没有违反规则,甚至将 char 数组转换为结构指针。
您只需要关心:

  • 数组足够大
  • 成员对齐

【讨论】:

  • 我认为这根本不是真的。在什么情况下应该我关心别名(如果不在任何示例中)?
  • 我试过你的例子。 gcc 不会警告/没有意外结果。即使您发布的 URL 中的示例也适用于 gcc4。 gcc3 确实对来自 URL 的示例有问题,但不是您的示例。我认为,因为它们是函数参数,而不是局部变量
  • 您使用了哪些优化/编译器标志? gcc 并没有捕捉到所有的别名问题(没有编译器能捕捉到),如果像我这样的简单示例在实践中有效,尽管违反了标准(据我所知),我不会感到惊讶。但是,我绝对会不想依赖于上述适用于任何一种编译器的观察结果。
  • @jaymmer:在编译器避免执行标准未强制执行的任何操作成为时尚之前,将 int* 强制转换为 Frizzly* 的行为将防止在一种类型写入的情况下出现别名问题在演员表之前,其他类型的读取在它之后。在编译器没有理由期望它的情况下,人们需要担心别名,但在它显而易见的原因中则不需要。不幸的是,gcc 和 clang 等流行的编译器已被不关心语言是否可用的人接管。
  • 你的例子都没有违反规则完全,完全,而且在所有方面都是完全错误的。为了将来偶然发现这个明显错误的答案的任何人的利益,访问char 数组作为char 数组以外的任何东西都违反了strict aliasing ...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-25
  • 2017-06-03
  • 2015-12-13
  • 2013-06-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多