我有许多结构,前面有前3个字段,下面是 简化示例:
struct my_struct1 {
/* common fields. */
int a;
int b;
int c;
};
struct my_struct2 {
int a;
int b;
int c;
uint32_t d;
uint32_t e;
};
struct my_struct3 {
int a;
int b;
int c;
uint16_t d;
char e;
};
static void func1(struct my_struct1 *s)
{
/* ... */
}
static void func2(struct my_struct2 *s)
{
/* ... */
}
static void func3(struct my_struct3 *s)
{
/* ... */
}
int main(void)
{
struct my_struct1 s = {1, 2, 3};
struct my_struct2 p = {1, 2, 3, 4, 5};
struct my_struct3 q = {1, 2, 3, 4, 'a'};
func1(&s);
func2(&p);
func3(&q);
/* XXX */
func3((struct my_struct3 *)&s);
return 0;
}
将s
强制转换为struct my_struct3 *
并传递给func3
并确保在堆栈上分配的s
或其他对象不会被破坏是否安全?
原因是我想编写一个带有指针的通用API,初始化公共字段(对于结构来说很常见)。另一个函数特定于my_struct*
并设置其余字段。
我不确定void *
是否可以解决此问题。
更新 我应该提一下,遗憾的是我不能改变结构布局,即添加一个公共部分不是一个选项,因为我使用的代码已经很老了,我不是允许改变其核心结构。
我看到的唯一丑陋的解决方法是将void *
和enum struct_type
参数传递给generic_init
函数,并基于struct_type
强制转换void *
适当的结构。
答案 0 :(得分:2)
充实EOF和Eugene Sh的评论。已经解释过:
将my_struct1强制转换为my_struct3是不安全的,因为my_struct3有更多的成员,my_struct1和编译器根本不会警告有关访问其他成员(d和e),覆盖my_struct1背后的内容。只要my_struct1与my_struct3的开头完全对应,反过来就可以这样做。我不确定标准中是否有任何保证可以覆盖你,但我不会赌它。
在单独的结构类型中分离出公共部分的优点 如下:
struct common {
int a;
int b;
int c;
};
struct my_struct1 {
struct common com;
};
struct my_struct2 {
struct common com;
uint32_t d;
uint32_t e;
};
struct my_struct3 {
struct common com;
uint16_t d;
char e;
};
void init_common(struct common *com)
{
com->a = 1;
com->b = 2;
/* ... */
}
struct my_struct1 s = {{1, 2, 3}};
struct my_struct2 p = {{1, 2, 3}, 4, 5};
struct my_struct3 q = {{1, 2, 3}, 4, 'a'};
init_common(&s.com);
init_common(&p.com);
init_common(&q.com);
答案 1 :(得分:2)
就我可以解释标准而言,将my_struct1*
类型的指针强制转换为类型为mystruct_3*
或反向的指针可能会因为指针转换而产生未定义的行为规则(参见C11 standard ISO/IEC 9899:TC2):
6.3.2.3指针...(7)指向对象或不完整类型的指针可以转换为指向不同对象或不完整类型的指针。如果 结果指针未正确对齐指向 类型,行为是未定义的。 ...
因此,由于my_struct1
和my_struct3
可能具有不同的对齐方式,因此根据my_struct1
正确对齐的指针未必根据my_struct3
正确对齐。< / p>
但是,即使你可以保证所有结构具有相同的对齐方式,在我看来,将指针传递给my_struct1
到func3
类型的对象是 -
不安全,即使公共成员是每个结构中的第一个成员,即使func3
只访问公共成员。
原因是编译器可能会在成员之间引入填充:
6.7.2.1结构和联合说明符...(13)在结构对象中,非位字段成员和位域的单位 驻留的地址按其顺序增加 声明。指向结构对象的指针,适当转换,指向 到它的初始成员(或者如果该成员是位字段,那么到 它居住的单位),反之亦然。 可能有未命名的 在结构对象中填充,但不在其开头。
因此,由于my_struct1
和my_struct3
具有不同的成员集,编译器引入填充的规则可能在这两个结构之间有所不同。我认为不太可能发生这种情况,但我没有在标准中找到任何声明来保证填充 - 即使对于前三个成员 - 对于my_struct1
和my_struct3
也是如此。
答案 2 :(得分:0)
这是半安全的。如果第一个成员相同,则可以保证指向结构的指针也是指向第一个成员的指针。从技术上讲,编译器可以在第一个成员之后插入任意填充。实际上,没有编译器会这样做,因此如果两个结构共享第一个和第二个成员,则指向第二个成员的指针也具有相同的偏移量。但是,对于您的成员“b”,偏移量可能不是地址+ sizeof(int)。为了提高性能,整数可以填充到8个字节。
为避免歧义,您可以将公共成员显式设置为结构“common”。