转换为 (void**) 是否定义明确?

use*_*624 7 c c++ pointers casting pointer-conversion

假设 A 是一个结构体,并且我有一个分配内存的函数

f(size_t s, void **x)
Run Code Online (Sandbox Code Playgroud)

我调用f分配内存如下。

struct A* p;
f(sizeof(struct A), (void**)&p);
Run Code Online (Sandbox Code Playgroud)

我想知道(void**)&p这里是否有一个明确定义的铸造。我知道在 C 中,将指针强制转换为明确定义的,void*反之亦然。不过,我不确定 的情况void**。我发现以下文档指出我们不应强制转换为具有更严格对齐要求的指针。是否void**有更严格或更宽松的对齐要求?

Eri*_*hil 9

这种转换不是由 C 标准定义的,即使是这样,f通过类型分配给它的代码void **也不会由 C 标准定义。

\n

C 2018 6.3.2.3 7 表示指向对象类型的指针可以转换为指向不同对象类型的指针。这涵盖了(void **) &p,因为&p是指向对象的指针p,并且void **是指向对象类型的指针void *。然而,本段仅告诉我们可以执行转换。它没有完全定义结果是什么。它说:

\n
    \n
  • \xe2\x80\x9c如果生成的指针未针对引用类型正确对齐,则行为未定义。\xe2\x80\x9d 这通常不是问题;void *在常见的 C 实现中,和 的对齐要求struct A *是相同的,并且这很容易检查。

    \n
  • \n
  • \xe2\x80\x9c否则,当再次转换回来时,结果将与原始指针相等。\xe2\x80\x9d 这就是该段落告诉我们有关转换结果的全部内容: 这是一个可以转换回来的指针tostruct A *获取原始指针或其等效指针。它没有告诉我们指针在类型中时可以用于其他任何用途void **

    \n
  • \n
  • \xe2\x80\x9c当指向对象的指针转换为指向字符类型的指针时,\xe2\x80\xa6\xe2\x80\x9d 这部分内容不适用,因为我们没有转换为指针到字符类型。

    \n
  • \n
\n

因此,假设该函数f有一些使用其参数的代码,x如下所示:

\n
*x = malloc(\xe2\x80\xa6);\n
Run Code Online (Sandbox Code Playgroud)\n

因为该标准没有定义如果将x用作 avoid **除了将其转换回 之外的任何目的时会发生什么struct A *,所以我们不知道*x会发生什么。

\n

典型的期望是*x访问相同的内存p,但它将作为 avoid *而不是 a进行访问struct A *。这里的一个技术问题是 C 标准不保证 a 在内存中的表示方式与 a在内存中的表示void *方式相同。struct A *就标准而言,void *可以使用八个字节而struct A *使用四个字节,或者void *可以使用平面字节地址而struct A *使用段和偏移地址方案。然而,与对齐一样,在常见的 C 实现中,不同类型的指针在内存中具有相同的表示形式,并且可以检查这一点。

\n

但随后我们就得出了别名规则。即使void *struct A *在内存中具有相同的表示形式,C 2018 6.5 7 说:

\n
\n

对象的存储值只能由具有以下类型之一的左值表达式访问:

\n

\xe2\x80\x94 与对象的有效类型兼容的类型,

\n

\xe2\x80\xa6

\n
\n

该列表还包含其他几个类别的类型,但没有一个与struct A *的类型匹配p。也就是说,标准中的这一段告诉我们,对象p只能通过具有以下之一的表达式来访问其存储值(C 标准中的 \xe2\x80\x9caccessed\xe2\x80\x9d 包括读取和写入)列出的类型。p用于访问in 的表达式*x = malloc(\xe2\x80\xa6);*x,其类型为void *,并且与void *不兼容struct A *,并且void *也不是该段落中列出的任何其他类型。

\n

所以代码*x = malloc(\xe2\x80\xa6);打破了这个规则。违反 \xe2\x80\x9cshall\xe2\x80\x9d 规则意味着代码的行为不是由 C 标准定义的。

\n

当使用开关要求它们通过不同类型支持别名对象时,某些编译器支持打破此规则。使用这样的开关会阻止编译器进行某些优化。特别是,给定两个指针x并且y指向不匹配别名规则的不同类型,则编译器可能会假设它们指向不同的对象,因此它可以以任何有效的方式重新排序对 和 的访问,*x因为*y存储到一个不能更改中的值另一个。

\n

因此,如果您验证了这一点void *struct A *具有相同的表示和对齐要求,并且您的编译器支持别名,则将为您检查的特定 C 实现定义该行为。然而,C 标准一般没有定义它。

\n