memcpy类似继承的结构 - 是否安全?

时间:2013-05-15 02:31:35

标签: c oop inheritance memcpy

我有两个我正在使用的结构,它们的定义几乎相同。这些是在我无法修改的头文件中定义的。

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));
         }
    }
}

4 个答案:

答案 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_carCarV2*carCarV1*(反之亦然),那么上述代码可保证按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 ++特性,那么你是安全的。