您可以在一个语句中将结构的实例部分复制到另一个不同类型的实例吗?

时间:2016-07-27 13:59:06

标签: c

对于部分副本,我希望使用副本设置匹配字段,但不设置不匹配的字段(即保留其先前的值)。

似乎没有发生。我很好奇为什么以及这是否是一些未定义的行为?

#include <stdio.h>

#define print_base(s) printf("%d %d\n", s.a, s.b)
#define print_extended(s) printf("%d %d %d\n", s.a, s.b, s.c)

typedef struct
{
    int a;
    int b;
} base;

typedef struct
{
    int a;
    int b;
    int c;
} extended;

int main()
{
    base s1 = {1,2};
    extended s2 = {0};
    extended s3 = {0};
    extended s4 = {4,5,6};
    extended s5 = {4,5,6};
    extended s6 = {7,8,9};
    base s7 = {0};

    print_extended(s2);
    s2 = *(extended*)&s1;
    print_extended(s2);

    print_extended(s3);
    memcpy(&s3, &s1, sizeof(s3));
    print_extended(s3);

    print_extended(s4);
    s4 = *(extended*)&s1;
    print_extended(s4);

    print_extended(s5);
    memcpy(&s5, &s1, sizeof(s5));
    print_extended(s5);

    /* lossy copy */
    print_base(s7);
    s7 = *(base*)&s6;
    print_base(s7);

    return 0;
}

/*
Actual  Expected
0 0 0   0 0 0
1 2 1   1 2 0
0 0 0   0 0 0
1 2 1   1 2 0
4 5 6   4 5 6
1 2 1   1 2 6
4 5 6   4 5 6
1 2 1   1 2 6
0 0     0 0
7 8     7 8
*/

2 个答案:

答案 0 :(得分:0)

s2 = *(extended*)&s1;

这具有未定义的行为,&s1处的对象不是通过类型extended*的指针访问的正确类型。

memcpy(&s3, &s1, sizeof(s3));

这也有未定义的行为,因为您从一个仅sizeof(extended)字节长的对象中读取sizeof(base)个字节。

如果你考虑一下,它显然不会像你期望的那样工作。复制extended结构时,编译器会从源中读取三个单词(即sizeof(extended)),并将这三个单词复制到目标。它不会复制少于三个单词并保留最后一个单词。你告诉它从地址sizeof(extended)读取&s1字节,这正是它的作用。并且最后一个成员被设置为垃圾值,因为您从不属于s1并且包含一些垃圾值的内存位置读取它。

如果您告诉编译器复制sizeof(base)个字节,为什么期望编译器复制sizeof(extended)个字节?如果您不希望它复制更多字节,那么不要告诉它复制更多字节!

答案 1 :(得分:0)

当你使用指针转换从一个内存位置复制到另一个内存位置时,你基本上是“自己动手”

这些陈述:

/some/action

等同于:

s2 = *(extended*)&s1;
s1 = *(base*)&s2;

我认为很清楚为什么从 base * source 复制到 extended * dest 只会覆盖部分数据而反之则会产生灾难性的后果(你有一个超出内存访问权限,可能会覆盖重要数据。)