您能够将一个结构体的实例赋给另一个实例吗,像这样:
struct Test t1;
struct Test t2;
t2 = t1;
我曾看到它用于简单结构,但对于复杂结构是否有效呢?
编译器如何根据数据项的类型来复制数据,即区分int
和字符串之间的区别?
如果结构体类型相同,则是可以的。可以把它想象成内存复制。
是的,结构体支持赋值。但是,存在一些问题:
struct S {
char * p;
};
struct S s1, s2;
s1.p = malloc(100);
s2 = s1;
首先看这个例子:
一个简单C程序的C代码如下所示
struct Foo {
char a;
int b;
double c;
} foo1, foo2;
void foo_assign(void)
{
foo1 = foo2;
}
int main(/*char *argv[],int argc*/)
{
foo_assign();
return 0;
}
foo_assign()的等效ASM代码为:
00401050 <_foo_assign>:
401050: 55 push %ebp
401051: 89 e5 mov %esp,%ebp
401053: a1 20 20 40 00 mov 0x402020,%eax
401058: a3 30 20 40 00 mov %eax,0x402030
40105d: a1 24 20 40 00 mov 0x402024,%eax
401062: a3 34 20 40 00 mov %eax,0x402034
401067: a1 28 20 40 00 mov 0x402028,%eax
40106c: a3 38 20 40 00 mov %eax,0x402038
401071: a1 2c 20 40 00 mov 0x40202c,%eax
401076: a3 3c 20 40 00 mov %eax,0x40203c
40107b: 5d pop %ebp
40107c: c3 ret
正如您所看到的,赋值操作在汇编中只是简单地被替换为一个“mov”指令,赋值运算符意味着将数据从一个内存位置移动到另一个内存位置。
赋值只对结构体的直接成员起作用,并且在结构体中具有复杂数据类型时会失败。其中,“复杂”意味着您不能拥有指向列表的指针数组。
在结构体内部的字符数组本身在大多数编译器上都无法工作,这是因为赋值将尝试简单地复制,甚至不考虑数据类型是否为复杂类型。
这只是一个简单的复制,就像你使用memcpy()
一样(事实上,有些编译器实际上会为该代码生成memcpy()
调用)。在C中没有“字符串”,只有指向一堆字符的指针。如果您的源结构包含这样一个指针,那么将复制该指针,而不是字符本身。
memcpy
,请参见此处:https://godbolt.org/z/nPxqWc - 但是现在,如果我传递相同的指针a
和b
,并且*a = *b
被转换为一个memcpy
,这是未定义的行为,因为对于memcpy
,“内存区域不能重叠。”(引用自手册)。那么编译器在使用memcpy
时是错误的,还是我在编写此类赋值时出错了? - not-a-user是的,您可以使用简单的赋值语句将一个结构体实例分配给另一个结构体实例。
对于非指针或不包含指针的结构体成员,赋值意味着复制。
对于指针结构体成员,赋值意味着指针将指向另一个指针的相同地址。
让我们亲自来看看:
#include <stdio.h>
struct Test{
int foo;
char *bar;
};
int main(){
struct Test t1;
struct Test t2;
t1.foo = 1;
t1.bar = malloc(100 * sizeof(char));
strcpy(t1.bar, "t1 bar value");
t2.foo = 2;
t2.bar = malloc(100 * sizeof(char));
strcpy(t2.bar, "t2 bar value");
printf("t2 foo and bar before copy: %d %s\n", t2.foo, t2.bar);
t2 = t1;// <---- ASSIGNMENT
printf("t2 foo and bar after copy: %d %s\n", t2.foo, t2.bar);
//The following 3 lines of code demonstrate that foo is deep copied and bar is shallow copied
strcpy(t1.bar, "t1 bar value changed");
t1.foo = 3;
printf("t2 foo and bar after t1 is altered: %d %s\n", t2.foo, t2.bar);
return 0;
}