我有两个我正在使用的结构,它们的定义几乎相同。这些是在我无法修改的头文件中定义的。
typedef struct
{
uint32_t property1;
uint32_t property2;
} CarV1;
typedef struct
{
uint32_t property1;
uint32_t property2;
/* V2 specific properties */
uint32_t property3;
uint32_t property4;
} CarV2;
在我的代码中,我在我的文件顶部初始化V2结构,以涵盖我的所有基础:
static const carV2 my_car = {
.property1 = value,
.property2 = value,
/* V2 specific properties */
.property3 = value,
.property4 = value
};
稍后,我想检索我初始化的值并将它们复制到要通过void指针从函数返回的结构中。我有时想要汽车的V2属性,有时候想要V1。如何在没有重复定义/初始化的情况下安全地进行memcpy?我对C很新,而且我的理解是这很难看,工程师跟着我看这段代码也不会赞成。干净的方法是什么?
int get_properties(void *returned_car){
int version = get_version();
switch (version){
case V1:
{
CarV1 *car = returned_car;
memcpy(car, &my_car, sizeof(CarV1)); // is this safe? What's a better way?
}
case V2:
{
CarV2 *car = returned_car;
memcpy(car, &my_car, sizeof(CarV2));
}
}
}
答案 0 :(得分:3)
是的,绝对有可能做你所要求的。
您可以使用基本结构成员来实现继承,如下所示:
typedef struct
{
uint32_t property1;
uint32_t property2;
} CarV1;
typedef struct
{
CarV1 base;
/* V2 specific properties */
uint32_t property3;
uint32_t property4;
} CarV2;
在这种情况下,您将删除重复的定义。当然,对于CarV2*
类型的变量,你不能直接引用基数的字段 - 你必须做一个小的重定向,如下所示:
cv2p->base.property1 = 0;
要转发到CarV1*
,请执行以下操作:
CarV1* cv1p = &(cv2p->base);
c1vp->property1 = 0;
你写过memcpy(&car, &my_car, sizeof(CarV1))
。这看起来像是一个错误,因为它正在复制指针变量的数据(即结构的地址,而不是结构本身)。由于car
已经是指针(CarV1*
),我假设my_car
也是如此,您可能想要这样做:
memcpy(car, my_car, sizeof(CarV1));
如果my_car
为CarV2*
且car
为CarV1*
(反之亦然),那么上述代码可保证按C标准工作,因为结构的第一个成员始终处于零偏移量,因此,第一个sizeof(CarV1)
字节的内存布局将是相同的。
编译器不允许以不同的方式对齐/填充该部分(我假设您的意思是优化),因为您已明确声明CarV2
的第一部分 CarV1
。
因为在你的情况下你会遇到无法改变的相同定义的结构,你可能会发现C标准定义了一个名为offsetof
的宏/特殊形式。
为了绝对确定您的内存布局,我建议您在程序的初始化阶段进行一系列检查,以验证所有offsetof(struct CarV1, property1)
是否等于offsetof(struct CarV2, property1)
等共同属性:
void validateAlignment(void)
{
if (offsetof(CarV1, property1) != offsetof(CarV2, property1)) exit(-1);
if (offsetof(CarV1, property2) != offsetof(CarV2, property2)) exit(-1);
// and so on
}
这将阻止程序继续运行,以防编译器使用填充做了任何创意。
它也不会减慢程序的初始化速度,因为offsetof
实际上是在编译时计算的。因此,在完成所有优化后,void validateAlignment(void)
函数应该完全优化(因为静态分析会显示退出条件始终为false)。
答案 1 :(得分:0)
你写的内容几乎可以工作,除了不是memcpy(& car,......你应该只有memcpy(car,......),但在这种情况下没有理由使用memcpy。相反,你应该在一个单独的声明中复制每个字段。
car-> property1 = my_car.property1
(my_car是指针还是没有?从代码片段中无法判断)
对于第二种情况,我认为你可以只分配整个结构:* car = my_car
答案 2 :(得分:0)
没有完美的解决方案,但一种方法是使用联合
typedef union car_union
{
CarV1 v1;
CarV2 v2;
} Car;
当你执行memcpy时,大小不会有所不同 - 如果版本v1则v2特定部分将不会被初始化。
答案 3 :(得分:0)
在C和Objective-C中,这在实践中很好。 (理论上,编译器必须看到包含两个结构作为成员的联合的声明)。
在C ++(和Objective-C ++)中,语言非常仔细地描述了何时安全,何时不安全。例如,如果您从
开始typedef struct {
public:
...
然后编译器可以自由地重新排列struct成员的位置。如果struct不使用C ++特性,那么你是安全的。