编辑:修复了评论中提到的错误,问题仍然存在,所以这个问题实际上并不重复 - 只是我的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)不同?
感谢您的帮助!
答案 0 :(得分:4)
想想指针。如果您有两个相同类型的指针变量,请调用它们a
和b
。如果您a = b
执行浅拷贝,则只将实际指针b
复制到a
,而不是b
指向的内存。这意味着a
和b
都指向同一个内存。
深层复制会将b
指向的内容复制到一些新分配的内存中。深层复制会导致a
和b
指向不同的内存。
采取你的结构:
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.pointer
和b.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
),现在x1
和x2
都有指向内存的指针分配给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 函数,它的堆栈就不再掌握了。
当您从这个区域打印值时,您会得到各种各样的东西(您希望获得核心转储,但遗憾的是,并非总是如此)。