【问题标题】:Void pointer castings to initialize structs用于初始化结构的空指针强制转换
【发布时间】:2019-12-05 18:11:52
【问题描述】:

我正在学习面向对象的 C 编程(如何实现虚拟表),我看到了一些用于初始化的铸件,我想知道是否:

1 - 是否有任何未定义的行为?

2 - 此代码是否可移植?

3 - 它在 C++ 中是否也有效(定义明确且可移植)?

在我研究的实际代码中,不是像这里这样简单的数据成员,而是指向构造函数、析构函数和克隆函数的函数指针,但我只对这些类型的转换是否定义良好感兴趣。

此代码使用 gcc 和 g++ 按预期编译和运行。

struct Point
{
        int x;
        int y;
};

struct Square
{
        struct Point *topLeft;
        struct Point *bottomRight;
        int area;
};

int main()
{
        void *square = calloc(1,sizeof(struct Square));
        * (struct Point **) square = (struct Point *) calloc(1,sizeof(struct Point));
        * ( ( (struct Point **) square) + 1) = (struct Point *) calloc(1,sizeof(struct Point));

        struct Square *sqrptr = (struct Square *) square;
        sqrptr->topLeft->x = 2;
        sqrptr->topLeft->y = 3;
        sqrptr->bottomRight->x = 5;
        sqrptr->bottomRight->y = 7;
        sqrptr->area = 20;

        printf("Values: %d %d %d %d\n", (** (struct Point **) square).x,
                                        (** (struct Point **) square).y,
                                        (** ( ( (struct Point **) square) + 1) ).x,
                                        (** ( ( (struct Point **) square) + 1) ).y );

        free(sqrptr->topLeft);
        free(sqrptr->bottomRight);
        free(sqrptr);
}

另外,根据 valgrind,没有内存泄漏。

编辑:我只是尝试使用 C++ 风格的铸件,g++ 没有给出任何错误或警告消息。

【问题讨论】:

  • void *square 应该是struct Square *square
  • * (struct Point **) square 应该是square->topLeft
  • * ( ( (struct Point **) square) + 1) 应该是square->bottomRight
  • 那么你的代码就更简洁了,不需要强制转换。
  • 如果你没有强制转换,编译器会给你什么错误?这就是您应该关注的内容,因为您现在拥有的代码实际上除了告诉编译器“闭嘴,我知道我在做什么”之外什么都不做,完全绕过了 C++ 的类型安全性。

标签: c++ c


【解决方案1】:

由于访问结构中的第二个数据成员,我发布的这段代码具有未定义的行为:

* ( ( (struct Point **) square) + 1) = (struct Point *) calloc(1,sizeof(struct Point));

编译器可以对齐 Square 结构,这样第二个数据成员就不会在第一个结构点之后立即开始。

但是,访问第一个数据成员在 C 和 C++ 中都是有效且定义明确的,因为在第一个数据成员之前没有填充:

struct alignment C/C++

(C11, 6.7.2.1p15) "结构中可能有未命名的填充 对象,但不是在其开头。”

(C++11, 9.2p20) "因此在一个 标准布局结构对象,但不是在其开头,根据需要 实现适当的对齐”

因此,在结构的开头,我可以放置一个指向 VirtualTable 结构的指针,其中包含指向初始化程序的函数指针并清理函数,并在纯 C 中实现面向对象的行为。这在 C++ 中也是有效的。

【讨论】:

  • 确切地说:has undefined behavior -> 当offfsetof(struct Square, bottomRight) != sizeof(struct Point*) 时是may have undefined behavior
  • @kamilcuk 纠正不正确。它总是有未定义的行为
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-01
  • 2012-02-08
  • 1970-01-01
相关资源
最近更新 更多