C 浅拷贝混淆

Ale*_*x H 0 c struct reference shallow-copy

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

警告:对 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;
}
Run Code Online (Sandbox Code Playgroud)

我希望看到:

0
0xMemoryLocation

0
0xMemoryLocation

0
0xMemoryLocation

.
.
.
Run Code Online (Sandbox Code Playgroud)

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

0
0x7fff57eaca18

0
0x7fff57eaca1a

0
0x7fff57eaca1c

0
0x7fff57eaca1e

0
0x7fff57eaca20

.
.
.
Run Code Online (Sandbox Code Playgroud)

既然浅拷贝应该是直接拷贝地址,为什么 &(type2Object.grid[i][j].value) 不一样呢?

感谢所有的帮助!

Som*_*ude 5

想想指针。如果您有两个相同类型的指针变量,让我们调用它们ab。如果你这样做a = b,你做一个浅拷贝,只复制实际指针ba,而不是内存b点。这意味着a和 都b指向同一个内存。

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


以你的结构:

typedef struct {
    Type1 grid[3][3];
} Type2
Run Code Online (Sandbox Code Playgroud)

如果你有

Type2 a;
Type2 b = { ... };  // Some initialization, not relevant exactly what
Run Code Online (Sandbox Code Playgroud)

然后是一个任务

a = b;   // Copy structure b to a
Run Code Online (Sandbox Code Playgroud)

这是一个浅拷贝。但是因为结构体的数据是一个数组所以整个数组都被复制了,看起来像一个深拷贝。

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

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);
Run Code Online (Sandbox Code Playgroud)

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

printf("a.pointer = %p\n", (void *) a.pointer);
printf("b.pointer = %p\n", (void *) b.pointer);
Run Code Online (Sandbox Code Playgroud)

以上两行将在赋值后打印相同的值。两个指针都指向同一个内存。这是一个浅拷贝。

此外,像上面显示的那样的结构赋值与执行 eg 没有什么不同

memcpy(&a, &b, sizeof a);
Run Code Online (Sandbox Code Playgroud)

实际上,我认为您的困惑在于,您认为引用另一个结构就像 Java、C# 或 C++ 中的引用,而实际上并非如此。

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

当你做

newType2.grid[i][j] = empty;
Run Code Online (Sandbox Code Playgroud)

您将empty结构实例的内容复制到结构实例中newType2.grid[i][j]。使用memcpy上面显示的调用,赋值所做的实际上是

memcpy(&newType2.grid[i][j], &empty, sizeof newType2.grid[i][j]);
Run Code Online (Sandbox Code Playgroud)

它对 的内容进行按位复制empty


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

int a[] = { 1, 2, 3, 4 };
int *p = a;
Run Code Online (Sandbox Code Playgroud)

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

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

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