【问题标题】:Strict aliasing rule in C++11C++11 中的严格别名规则
【发布时间】:2015-05-31 15:39:59
【问题描述】:

我在我的 C++11 代码中使用了以下 C 结构(代码来自 PostGis 的 liblwgeom,但这不是问题的核心)。代码使用 g++-4.8 使用以下选项编译:

-std=c++11 -Wall -Wextra -pedantic-errors -pedantic -Werror

并且我在编译期间没有收到任何错误(或警告)(我应该收到任何错误吗?)

问题

在接受LWGEOM 并且不修改void *data; 成员的函数中使用LWPOLY(实际上是LWGEOM*)是安全的。我知道这是穷人的遗产,但这是我需要处理的。

详情

多边形:

typedef struct
{
        uint8_t type; /* POLYGONTYPE */
        uint8_t flags;
        GBOX *bbox;
        int32_t srid;
        int nrings;   /* how many rings we are currently storing */
        int maxrings; /* how many rings we have space for in **rings */
        POINTARRAY **rings; /* list of rings (list of points) */
}
LWPOLY; /* "light-weight polygon" */

LWGEOM:

typedef struct
{
        uint8_t type;
        uint8_t flags;
        GBOX *bbox;
        int32_t srid;
        void *data;
}
LWGEOM;

点阵:

typedef struct
{
        /* Array of POINT 2D, 3D or 4D, possibly missaligned. */
        uint8_t *serialized_pointlist;

        /* Use FLAGS_* macros to handle */
        uint8_t  flags;

        int npoints;   /* how many points we are currently storing */
        int maxpoints; /* how many points we have space for in serialized_pointlist */
}
POINTARRAY;

GBOX:

typedef struct
{
        uint8_t flags;
        double xmin;
        double xmax;
        double ymin;
        double ymax;
        double zmin;
        double zmax;
        double mmin;
        double mmax;
} GBOX;

当我做类似的事情时,我是否违反了严格的别名规则?

const LWGEOM* lwgeom;
...
const LWPOLY* lwpoly = reinterpret_cast<const LWPOLY*>(lwgeom);

我知道 PostGis 中的类型是专门设计为“兼容”的,但是我想知道这样做是否违反了标准。

另外,我注意到 PostGis 编译时默认禁用严格别名(至少版本 2.1.5)。

解决方案

我的同事帮助我对其进行了调查,似乎答案是否定的,它不违反严格的别名,但仅在我们访问与 LWPOLY 的类型相同且布局在结构连续。原因如下(引用标准):

3.10.10 表示您可以通过指向“聚合或联合”的指针访问成员。

8.5.1 定义聚合(C 结构是聚合): 聚合是一个数组或一个类(第 9 条),没有用户提供的构造函数(12.1),没有私有或 受保护的非静态数据成员(第 11 条),无基类(第 10 条),无虚函数(10.3)。

9.2.19 表示指向结构的指针与指向标准布局类的第一个成员的指针相同(C 结构是标准布局)。

这是否是一种安全的编码方式是另一个问题。

【问题讨论】:

  • 是的,这肯定会破坏字符串别名规则。
  • 我个人的看法是,不应该在不同的结构之间进行转换,而应该解析它们。
  • 这一行出现在标准中:“一种聚合或联合类型,在其元素或非静态数据成员(递归地包括元素或非静态数据)中包含上述类型之一子聚合或包含联合的成员)”,但我不确定我能否正确解释它
  • 这可能是穷人的遗产。 (多边形是一种几何形状。)你能用你自己的类包装它们,从 LWGEOM 显式继承 LWPOLY 并进行动态或静态转换吗?库代码永远不会是别名安全的,但您自己的代码会。
  • @Alex 这是一个高性能库,解析会有点矫枉过正(我的主观意见)

标签: c++11 postgis strict-aliasing


【解决方案1】:

是的,它违反了严格的别名规则。 LWGEOM 和 LWPOLY 是不相关的类型,int 和 void* 也是如此。因此,例如,对lwgeom-&gt;data 的修改可能无法通过lwpoly-&gt;nrings 读取,反之亦然。

我用 GCC4.9 验证了这一点。我的代码如下:

#include <cinttypes>
#include <iostream>

using namespace std;

typedef struct {
        uint8_t type; /* POLYGONTYPE */
        uint8_t flags;
        int32_t srid;
        int nrings;   /* how many rings we are currently storing */
} LWPOLY; /* "light-weight polygon" */

typedef struct {
        uint8_t type;
        uint8_t flags;
        int32_t srid;
        void *data;
} LWGEOM;

void f(LWGEOM* pgeom, LWPOLY* ppoly) {
    ppoly->nrings = 7;
    pgeom->data = 0;
    std::cout << ppoly->nrings << '\n';
}

int main() {
    LWGEOM geom = {};
    LWGEOM* pgeom = &geom;
    LWPOLY* ppoly = (LWPOLY*)pgeom;
    f(pgeom, ppoly);
}

猜猜看,输出是7。

【讨论】:

  • 他们有标准布局(请参阅我对问题的编辑)并且成员以相同的顺序排列(成员属于同一类型),因此根据标准设置不违反严格别名规则
  • @iggy 查看我的答案的更新。我已经在 GCC4.9 上验证了违规行为。
  • 是的,你是对的,我没有详细说明这个问题。但是,如果您访问公共部分(所以前3个成员),根据标准没有违反
  • 相同的成员类型并不意味着没有别名。如果一种类型是 POD 结构,另一种是具有虚拟成员函数的类,否则两者都具有相同的成员,这将不起作用
  • 经过 GCC 和 clang 的一些测试。不知何故,我发现即使是前 3 名成员也存在同样的问题。
猜你喜欢
  • 2013-03-11
  • 2011-02-15
  • 2015-10-15
  • 1970-01-01
  • 2019-01-29
  • 2017-02-25
  • 2018-10-27
  • 2018-12-14
  • 1970-01-01
相关资源
最近更新 更多