将前缀元素一致的结构体指针转换是否安全?是否符合C标准?
首先定义两个结构体:
typedef struct { int *array; int first; int last; } arguments;
以及包含额外成员的结构体:
typedef struct { int *array; int first; int last; // only used in children int read_fd; int write_fd; } child_arguments;
现有函数 void do_something(arguments* args) { ... } 接收arguments类型指针参数,请问执行如下类型转换是否安全:
void do_something_in_child(child_arguments* child_args) { do_something( (arguments*) child_args ); }
我的推测是:由于C语言不会重排结构体成员,新增成员不会影响原有成员的内存填充,该操作可能安全,但不确定是否真的安全,以及该操作是否符合C标准,还是属于未定义行为但多数编译器支持?
解答
这个类型转换是完全安全且符合C标准的,不属于未定义行为。
根据C标准的规定:如果两个结构体类型的初始成员序列完全一致(成员的类型、顺序都相同,且前面没有差异成员),那么指向这两个结构体的指针可以互相转换,且通过转换后的指针访问初始序列里的成员是合法的有定义行为。
在你的代码里,child_arguments的前三个成员和arguments的所有成员完全匹配,类型、顺序没有任何差异。当你把child_arguments*转换为arguments*后,do_something函数中访问args->array、args->first、args->last时,实际访问的就是原child_arguments对象中对应的成员,这和直接访问child_args的这些成员效果完全一致,不存在内存布局或访问权限的问题。
需要注意:反过来如果把arguments*转换为child_arguments*,再去访问read_fd或write_fd就是未定义行为——因为原arguments对象根本没有这两个成员,访问它们会造成内存越界。但你当前的场景是从包含更多成员的结构体指针转换到只包含初始成员的结构体指针,这是标准明确允许的合法操作。
内容的提问来源于stack exchange,提问作者Tudor

