【问题标题】:C99 strict aliasing rules in C++ (GCC)C++ (GCC) 中的 C99 严格别名规则
【发布时间】:2026-02-24 04:30:01
【问题描述】:

据我了解,GCC 在 C++ 中支持其所有 C99 功能。但是 C++ 代码中如何处理 C99 严格别名?

我知道在不相关的类型之间使用 C 进行强制转换不是严格混叠安全的,并且可能会生成不正确的代码,但是 C++ 呢?由于严格别名不是 C++ 标准的一部分(对吗?),GCC 必须自己指定语义。

我认为const_caststatic_cast 在相关类型之间进行转换,因此它们是安全的,而reinterpret_cast 可以打破严格的别名规则。

这是正确的理解吗?

【问题讨论】:

    标签: c++ c gcc strict-aliasing


    【解决方案1】:

    不,你可能混合了不同的东西。

    严格的别名规则与 C99 标准完全无关。严格的别名规则植根于自 [标准化] 时代开始以来存在于 C 和 C++ 中的部分标准。在 C89/90 (6.3) 和 C++98 (3.10/15) 中存在禁止通过另一种类型的左值访问一种类型的对象的子句)。这就是严格别名的全部意义,不多也不少。只是并非所有编译器都想要(或敢于)强制执行或依赖它。 C 和 C++ 语言有时都被用作“高级汇编”语言,严格的别名规则经常会干扰这种使用。正是 GCC 做出了这一大胆的举动,并决定开始在优化中依赖严格的别名规则,这经常会招致那些“汇编”类型的抱怨。

    确实,在 C++ 中打破严格别名规则的最直接的方法是 reinterpret_cast(当然还有 C 风格的强制转换)。但是,static_cast 也可以用于该目的,因为它允许通过使用 void * 作为“链式”强制转换中的中间类型来打破严格的别名

    int *pi;
    ...
    double *pd = static_cast<double *>(static_cast<void *>(pi));
    

    const_cast 不能在兼容的编译器中破坏严格的别名。

    至于 C99...C99 引入的是 restrict 限定符。这与别名直接相关,但它本身并不是所谓的严格别名。

    【讨论】:

    • 你说得对,我在想 C99 restrict 关键字,而不是严格的别名。由于某种原因,它以这种方式在我的脑海中根深蒂固(“C99”+“严格别名”)。
    • 我仍然不确定是否可以在不违反严格的别名规则的情况下使用 BSD 套接字 API 的 sockaddr 相关部分。
    • @Maister:不,使用联合在类型之间进行强制转换是未定义的行为。该标准仅允许从上次写入的联合的同一变量中读取。
    • @jons34yp - 另外,如果有两个结构的联合,那么标准还允许访问两个结构中的初始公共字段子序列,IIRC。
    • @caf:我相信 BSD 套接字 API 的 sockaddr 相关部分(以及在 C 中执行多态性的任何其他 API)实际上并没有违反严格的别名,因为您可以通过“an在其成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的成员)”,因此似乎允许通过两个不同类型的结构指针访问 sa_family_t 类型的第一个字段。
    【解决方案2】:

    static_cast 也可以破坏别名规则,因为编译器信任您确保目标类型与对象的实际运行时类型相关。考虑:

    extern void f(double*, int*); // compiler may optimize assuming that arguments don't overlap
    double d;
    void* pv = &d;
    int* pi = static_cast<int*>(pv);
    f(&d, pi); // assumption is violated
    

    【讨论】:

      【解决方案3】:

      这个概念在Cpp中是一样的;因为您可以使用 C 样式转换来指导您完成被认为是安全的严格别名。

      简而言之:不,使用 Cpp 强制转换的方法(您已概述)不会安全地涵盖所有情况。打破规则的一种常见方法是使用 static_cast 来转换指针。

      只需打开编译器警告——它会(或者,应该)告诉你什么是不安全的。

      【讨论】: