我正在学习面向对象的 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++ 没有给出任何错误或警告消息。
最佳答案
由于访问结构中的第二个数据成员,我发布的代码段具有未定义的行为:
* ( ( (struct Point **) square) + 1) = (struct Point *) calloc(1,sizeof(struct Point));
编译器可以对齐 Square 结构,以便第二个数据成员不会立即在第一个结构 Point 之后开始。
但是,访问第一个数据成员在 C 和 C++ 中都是有效且定义良好的,因为第一个数据成员之前没有填充:
(C11, 6.7.2.1p15) "There may be unnamed padding within a structure object, but not at its beginning."
(C++11, 9.2p20) "There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment"
因此,在结构的开头,我可以放置一个指向 VirtualTable 结构的指针,其中包含指向初始值设定项和清理函数的函数指针,并在纯 C 中实现面向对象的行为。这在 C++ 中也有效。
关于c++ - 用于初始化结构的空指针转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57236295/