C浅拷贝混淆

时间:2016-10-25 11:34:18

标签: c struct reference shallow-copy

编辑:修复了评论中提到的错误,问题仍然存在,所以这个问题实际上并不重复 - 只是我的C编程技能在这一点上不是很好,下面给出的解决方案回答了问题并没有解决局部变量bug。

警告:相当新的C

我理解当我们将结构分配给另一个结构时,会执行浅拷贝。但是,我无法理解为什么会发生这种情况的结果:

假设以下我尝试初始化结构,使用赋值运算符将其称为Type2结构,Type1成员,然后应该执行浅表副本。这意味着将复制Type2成员的地址:

typedef struct {
    uint8_t someVal;
} Type1

typedef struct {
    Type1 grid[3][3];
} Type2

//Constructor for Type2 "objects"
Type2 Type2_Constructor(void) {
    Type1 empty = {.value = o}
    Type2 newType2;
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++) {
            //Shallow copy empty struct
            newType2.grid[i][j] = empty;
        }
    return newType2;
}

int main (void) {    
    Type2 type2Object = Type2_Constructor();   
    for (int i = 0; i < 3; i ++) 
        for (int j = 0; j < 3; j++){
            printf("%u",type2OBject.grid[i][j].value);
            printf("\n\r%p\n\r",&(type2Object.grid[i][j].value));
            printf("\n\r");
        }   
    return 0;
}

我希望看到:

0
0xMemoryLocation

0
0xMemoryLocation

0
0xMemoryLocation

.
.
.

事实上,我所看到的是这样的地址增加了2个字节:

0
0x7fff57eaca18

0
0x7fff57eaca1a

0
0x7fff57eaca1c

0
0x7fff57eaca1e

0
0x7fff57eaca20

.
.
.

由于浅拷贝应该直接复制地址,为什么&amp;(type2Object.grid [i] [j] .value)不同?

感谢您的帮助!

3 个答案:

答案 0 :(得分:4)

想想指针。如果您有两个相同类型的指针变量,请调用它们ab。如果您a = b执行浅拷贝,则只将实际指针b复制到a,而不是b指向的内存。这意味着ab都指向同一个内存。

深层复制会将b指向的内容复制到一些新分配的内存中。深层复制会导致ab指向不同的内存。

采取你的结构:

typedef struct {
    Type1 grid[3][3];
} Type2

如果你有

Type2 a;
Type2 b = { ... };  // Some initialization, not relevant exactly what

然后是作业

a = b;   // Copy structure b to a

这是一份浅薄的副本。 但是 因为结构的数据是整个数组被复制的数组,而看起来像一样是深层复制。

如果结构中有指针,则只复制指针。另一个例子,有一个新的结构:

typedef struct {
    char *pointer;
} Type3;

Type3 a;
Type3 b = { "abcd" };

a = b;

printf("a.pointer = %s\n", a.pointer);
printf("b.pointer = %s\n", b.pointer);

上面的代码会为a.pointerb.pointer打印相同的字符串。但它是相同的指针。如果我们添加一个新的指针打印输出:

printf("a.pointer = %p\n", (void *) a.pointer);
printf("b.pointer = %p\n", (void *) b.pointer);

分配后的上述两行将打印相同的值。两个指针都指向同一个内存。这是一个浅薄的副本。

此外,如上所示的结构分配与例如没有区别。

memcpy(&a, &b, sizeof a);

实际上我认为你的困惑是你认为引用另一个结构就像Java或C#或C ++中的引用一样,当它不是。

grid结构中Type2数组中的每个元素都是Type1结构的唯一实例。作为独特的实例,它们都占用不同的内存,导致您打印的地址完全不同。

当你这样做时

newType2.grid[i][j] = empty;

empty结构实例的内容复制到结构实例newType2.grid[i][j]中。使用上面显示的memcpy调用,分配的作用实际上是

memcpy(&newType2.grid[i][j], &empty, sizeof newType2.grid[i][j]);

它对empty

的内容进行了按位复制

关于指针和数组之间的区别,请考虑以下定义:

int a[] = { 1, 2, 3, 4 };
int *p = a;

在内存中它看起来像这样:

+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
^
|
+---+     
| p |
+---+

也就是说,您的数组及其内容占用的空间足以容纳四个int值。然后你有指针p指向数组中的第一个元素(数组衰减到指向它们的第一个元素的指针,对于上面的数组a,使用a作为指针等于&a[0])。

答案 1 :(得分:2)

您似乎误解了浅/深拷贝是什么。浅拷贝会复制每个成员(或者如果你愿意,可以进行按位复制)。深层副本还会复制结构的自有资源

让我们从一个简单的例子开始:

struct X {
   int val;
};

X x1 = {24};
X x2 = {42};

x1 = x2;

是的,内置赋值运算符运算符=执行浅表复制。这意味着以上内容等同于:

x1.val = x2.val;

在这种情况下,X 没有其拥有的外部资源,因此它也被视为深层副本,因为x2拥有的每一个都被复制到x1 。这是X不拥有任何外部资源这一事实的结果。

X拥有外部资源 /例如:

时,浅/深拷贝的重要性发挥作用
struct X {
   int *my_very_own_external_value;
};

X x1, x2;
x1.my_very_own_external_value = malloc(sizeof(int));
*x1.my_very_own_external_value = 24;

x2.my_very_own_external_value = malloc(sizeof(int));
*x2.my_very_own_external_value = 42;

浅层副本就是这样:

x1 = x2;

这相当于:

x1.my_very_own_external_value  = x2.my_very_own_external_value;

如果您理解指针意味着x1丢失了已分配内存的地址(谁持有24),现在x1x2都有指向内存的指针分配给x2,即存储42的那个;

如果你想要深层拷贝,你需要写:

free(x1.my_very_own_external_value);
x1.my_very_own_external_value  = malloc(sizeof(int));
*x1.my_very_own_external_value  = *x2.my_very_own_external_value;

现在您已经复制了x2外部拥有的内存(外部资源)。

免责声明:我可能用C ++风格编写

答案 2 :(得分:0)

您的代码有错误:

//Constructor for Type2 "objects"
Type2 * Type2_Constructor(void) {
    Type1 empty = ...;
    Type2 newType2;
    ... do something ...
    Type2 * newType2Ptr = &newType2;
    return newType2Ptr;
}

newType2 是一个局部变量 - 在堆栈上定义的结构。 然后你将它的地址返回给调用函数。 一旦退出 Type2_Constructor 函数,它的堆栈就不再掌握了。

当您从这个区域打印值时,您会得到各种各样的东西(您希望获得核心转储,但遗憾的是,并非总是如此)。

相关问题