【问题标题】:Is cast of pointer to anonymous union valid in C11?指向匿名联合的指针在 C11 中有效吗?
【发布时间】:2018-07-08 11:13:31
【问题描述】:
const Boo *constBoo;
Boo *nonConstBoo;

nonConstBoo = ((union {const Boo *_q; Boo *_nq;})constBoo)._nq;

上述构造在 C11 中是否有效,还是只有 GCC/clang 扩展才能以这种方式将指针强制转换为匿名联合?如果无效,有没有其他方法可以在有效的C11代码中写出等价的表达式?

其目的是模拟与 C11 兼容的 C++ const_cast 并提供一些基本的类型安全性。从 const 到非 const 指针的显式转换将触发带有 -Wcast-qual 选项的警告,这是不可取的。

【问题讨论】:

  • 你有什么理由不想直接做演员?
  • 它不能用 -Wcast-qual 编译。这基本上是一种在 C 中从 C++ 编写 const_cast 而不会触发警告的显式方式。不幸的是,一些外部库没有正确定义函数原型,因此进行强制转换是不可避免的,但同时为其余代码启用警告是合理的。
  • GCC 将发出 -pedantic-errors 的错误:“错误:ISO C 禁止强制转换为联合类型 [-Wpedantic]” 还建议改写:我读到您的问题是询问指向联合的指针。 :)
  • 嗯,有趣,我想我错过了那个警告。让我重新表述一下这个问题:-) 您是否还指定了 -std=c11?什么 GCC 版本?
  • @FilipNavara 是的,这是 GCC 8.1 的 -std=c11 -pedantic-errors,但与 GCC 4.7 的结果相同,第一个识别 -std=c11

标签: c c11


【解决方案1】:

Cast to a union 是一个 GNU C 扩展。 C 标准仅定义标量类型之间的强制转换(即整数、浮点数和指针;参见 6.5.4p2)。但是,您可以做的是当场复制创建联合(而不是强制转换),然后获取适当的成员:

typedef struct Boo Boo;
const Boo *constBoo;
Boo *nonConstBoo;

int main()
{
    nonConstBoo = (union {const Boo *_q; Boo *_nq;}){._q=constBoo}._nq;
}

以上应该可以工作(在 C 中,但在 C++ 中,您只需要访问联合的最后使用的成员),因为合格和非合格对象必须具有相同的表示和对齐要求,并且同样适用于指向兼容类型的合格和不合格版本的指针 (6.2.5p28)。

memcpy(&nonConstBoo,&constBoo,sizeof constBoo);

应该使用任何一种语言。

【讨论】:

  • @StoryTeller 之前曾建议使用复合文字而不是强制转换,但答案已被删除,因为他在标准中发现了一些阻止它有效的东西。不过,它似乎可以同时使用 GCC 和 clang 进行编译。
  • 对象可能需要具有相同的表示,但指向它们的指针不需要。演员表不保值并非不可行,因此双关语将不等同于演员表。无论如何,标准方面。
  • @StoryTeller 谢谢。里奇是对的。预选赛很烂。 :D
  • @StoryTeller 我收回了。该标准说“同样,指向兼容类型的合格或非合格版本的指针应具有相同的表示和对齐要求。” (port70.net/~nsz/c/c11/n1570.html#6.2.5p28)。联合方式应该没问题。
  • @PSkocik - 你还没有证明 const BB 是兼容的类型。剧透:他们不是port70.net/~nsz/c/c11/n1570.html#6.7.3p10
【解决方案2】:

不,这是不合法的,原因很简单,转换只允许用于标量类型,C11 6.5.4“转换运算符”:

除非类型名称指定了 void 类型,否则类型名称应 指定原子的、合格的或不合格的标量类型,以及操作数 应该是标量类型。

您的类型是 union 类型,因此这是违反约束的,任何 C 编译器都不应该接受。

如果你只是想抛弃constness,就这样做,那就是使用(Boo*)constBoo。但请注意,这样做需要您自担风险,施放咒语会告诉编译器您假装知道自己在做什么。

在大多数情况下,当您使用这种类型转换的指针时,程序的行为是不确定的,可能会发生非常糟糕的事情。

【讨论】:

  • “您的类型是联合类型,所以这是违反约束的”——正确。 “而且任何 C 编译器都不应该接受这一点。” - 错误的。该标准不强制要求违反约束的错误,它只需要诊断。允许编译器发出警告并将其作为扩展接受。
  • 感谢您参考禁止案例的标准部分。不幸的是,“(Boo*)constBoo”在 GCC 编译器上不能与 -Wcast-qual 一起使用,这就是我首先提出这个结构的原因。我更喜欢尽可能明确地说明强制转换,最好确保类似于 const_cast C++ 运算符的类型安全。在理想世界中我不需要它,但是一些系统 API 定义了错误的原型,这使得某种形式的转换成为必要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-11
  • 1970-01-01
  • 2012-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多