C:结构体复制与赋值、浅拷贝与深拷贝相关问题 您所在的位置:网站首页 c语言struct数组 C:结构体复制与赋值、浅拷贝与深拷贝相关问题

C:结构体复制与赋值、浅拷贝与深拷贝相关问题

2023-07-31 19:27| 来源: 网络整理| 查看: 265

先思考: 结构体能否用“=”号直接赋值? 如何理解结构体的浅拷贝与深拷贝? 结构体“=”号赋值与“malloc”赋值哪个更好?效率更高?

直接上代码!

1.结构体能否用“=”号直接赋值?

编写C代码:

vi struck_assign.c

内容如下:

#include struct Foo { char a; int b; double c; }foo1, foo2; //define two structs with three different fields void struct_assign(void) { foo2 = foo1; //structure directly assignment } int main() { foo1.a = 'a'; foo1.b = 1; foo1.c = 3.14; struct_assign(); printf("%c %d %lf\n", foo2.a, foo2.b, foo2.c); return 0; }

可以从结果上看出,结构体直接赋值在C语言下是可行的。 汇编C代码:

gcc -S struck_assign.c cat struck_assign.s

我们看看struct_assign()函数的汇编实现,从而从底层看看C语言是如何实现两个结构体之间的赋值操作的:

struct_assign: ... movq %rsp, %rbp .cfi_def_cfa_register 6 movq foo1(%rip), %rax movq foo1+8(%rip), %rdx movq %rax, foo2(%rip) movq %rdx, foo2+8(%rip) nop

这段汇编比较简单,由于结构体的对齐的特性,sizeof(srtruct Foo)=16,通过四次movl操作将foo1的结构体内容拷贝到结构体foo2中。从汇编上看出,结构体赋值,采用的类似于memcpy这种形式,而不是逐个字段的拷贝。

2.如何理解结构体的浅拷贝与深拷贝?

如果结构体中含有其它复杂数据类型呢,例如数组、指针、结构体等,从上面的汇编实现可以看出,只要两个结构体类型相同,就可以实现赋值,如下例:

#include #include struct Foo { char a; int b; double c; char *p_d; }foo1, foo2; void struct_shallow_copy(void) { foo2 = foo1; //structure directly assignment } int main() { char *d = (char *) malloc (4*sizeof(char)); d[0] = 'b'; d[1] = 'c'; d[2] = 'd'; d[3] = '\0'; foo1.a = 'a'; foo1.b = 1; foo1.c = 3.14; foo1.p_d = d; struct_shallow_copy(); printf("%c %d %lf %s\n", foo2.a, foo2.b, foo2.c, foo2.p_d); printf("%#x %#x\n", foo1.p_d, foo2.p_d); return 0; }

可以看出结果和我们想象的是一样的。再次验证结构体的赋值,是直接结构体的内存的拷贝!但正是这个问题,如上面的实例,foo1 和 foo2 中p_d 指针都是指向我们申请的一块大小为4个字节的内存区域,这里注意的是,结构体的拷贝只是浅拷贝,即指针p_d的赋值并不会导致再申请一块内存区域,让foo2的p_d指向它。那么,如果释放掉foo1中的p_d指向的内存,此时foo2中p_d变成野指针,这是对foo2的p_d操作就会出现一些不可预见的问题!

那如何做成深拷贝?

如下代码:

#include #include #include struct Foo { char a; int b; double c; char *p_d; }foo1, foo2; void struct_deep_copy(void) { foo2.a = foo1.a; foo2.b = foo1.b; foo2.c = foo1.c; foo2.p_d = (char *) malloc (4*sizeof(char)); strcpy(foo2.p_d,foo1.p_d); //Deep copy } int main() { char *d = (char *) malloc (4*sizeof(char)); d[0] = 'b'; d[1] = 'c'; d[2] = 'd'; d[3] = '\0'; foo1.a = 'a'; foo1.b = 1; foo1.c = 3.14; foo1.p_d = d; struct_deep_copy(); printf("%c %d %lf %s\n", foo2.a, foo2.b, foo2.c, foo2.p_d); printf("%#x %#x\n", foo1.p_d, foo2.p_d); return 0; }

根据上述的结果,得出结论: 浅拷贝是将一个结构体里面的值完全赋给另一个结构体,当结构体中含有指针变量时,浅拷贝只会拷贝指针所指向的空间地址值,不会自动分配内存(即原指针与拷贝指针都指向同一块内存,一不小心可能犯对同一块动态内存进行多次释放的错误) 深拷贝可以自动为指针分配内存(即原指针与拷贝指针所指向的内存空间不同,只是内存中存的值相同,可以避免对同一块动态内存进行多次释放的错误) 如果结构体内无指针变量,浅拷贝与深拷贝效果相同

3.结构体“=”号赋值与“memcpy”赋值哪个更好?效率更高?

上代码直接比较,看下汇编阶段,哪个流程更少点。

#include #include struct Foo { char a; int b; double c; char *d; }foo1, foo2, foo3; //define two structs with three different fields void struct_assign(void) { foo2 = foo1; //structure directly assignment } void struct_copy(void) { memcpy(&foo3,&foo1,sizeof(struct Foo)); //Structure directly copy } int main() { foo1.a = 'a'; foo1.b = 1; foo1.c = 3.14; foo1.d = "efg"; struct_assign(); printf("%c %d %lf %s\n", foo2.a, foo2.b, foo2.c, foo2.d); printf("%p\n", foo2.d); struct_copy(); printf("%c %d %lf %s\n", foo3.a, foo3.b, foo3.c, foo3.d); printf("%p\n", foo3.d); return 0; }

编译汇编如下:

struct_assign: ... movq %rsp, %rbp .cfi_def_cfa_register 6 movq foo1(%rip), %rax movq %rax, foo2(%rip) movq foo1+8(%rip), %rax movq %rax, foo2+8(%rip) movq foo1+16(%rip), %rax movq %rax, foo2+16(%rip) nop struct_copy: ... movq %rsp, %rbp .cfi_def_cfa_register 6 movq foo1(%rip), %rax movq %rax, foo3(%rip) movq foo1+8(%rip), %rax movq %rax, foo3+8(%rip) movq foo1+16(%rip), %rax movq %rax, foo3+16(%rip) nop

根据上述结果,得出结论: 两种实现,从汇编层面是一致的! 结构体赋值实现也是调用的memcpy。本质上效率应该是差不多的!

Ps:比较效率的话最好能够懂一点简单的汇编代码,看看就明白了。

参考: https://blog.csdn.net/qq_33706673/article/details/85253536 https://bbs.csdn.net/topics/310074713 https://blog.csdn.net/qq_34013719/article/details/109061140

收录于: 嵌入式软件/BSP开发工程师/Linux驱动工程师/C语言经典笔试面试题大全



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有