-- TOC --
C语言的函数调用,参数传递的方式叫call by value,即做一次copy,入参成了函数的局部变量,在函数执行完毕后消失。
很多C语言的函数接口,入参是地址,copy地址没问题,可以访问地址指向的内容,也可以修改,并在函数返回后,让修改保持。
一般情况下,入参会避免struct结构体,因为copy一个struct结构体的cost较高,而且一不小心,会导致shallow copy的问题。
下面是shallow copy的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct abc {
int a;
int b;
char *c;
} abc;
void test(abc b) {
printf("%p, %s\n", b.c, b.c);
}
int main(void) {
abc a = {1,2,NULL};
a.c = (char*)malloc(32);
memset(a.c, 0, 32);
memcpy(a.c, "12345", 5);
printf("%p, %s\n", a.c, a.c);
test(a);
free(a.c);
return 0;
}
test接口入参是struct abc,调用test后,b是a的一个copy,但是b只是copy了a的所有成员,并没有重新malloc一块内存给b.c这个指针,因此,上面示例代码的两次printf,内容完全一样,即a.c和b.c指向了同一块内存。上面代码运行效果如下:
0x16f82a0, 12345
0x16f82a0, 12345
这就是shallow copy。
如果要避免shallow copy,有两个思路:
传指针大家见得多了,资源局部化的做法,即每当申明一个struct变量,变量的资源不需要再malloc,可以将上面的代码可以修改成这样:
#include <stdio.h>
typedef struct abc {
int a;
int b;
char c[32];
} abc;
void test(abc b) {
printf("%p, %s\n", b.c, b.c);
}
int main(void) {
abc a = {1,2,"12345"};
printf("%p, %s\n", a.c, a.c);
test(a);
return 0;
}
struct abc的成员c是个确定长度的array,在调用test(b)的时候,会将整个array做copy,这样就避免了shallow copy。上面修改后的代码,两行打印出来的地址已经不同了,但因此整体copy的缘故,地址指向的内容完全一样:
0x7ffcc3e84198, 12345
0x7ffcc3e84168, 12345
其实,在C语言中,我很少会见到入参是结构体的情况,一般都是用指向结构体的指针,这样就完全避免了本文所述的问题。但是,本文所述的问题本身,是需要了解的。
这个问题,与C++的copy constructor和copy assignment直接相关!所谓的copy constructor,就是在constructor内,自己做deep copy,以避免shallow copy的问题。copy assignment就是在对象赋值的时候,重载operator=,自己做deep copy。具体请参考:C++ Copy Semantics
本文链接:https://cs.pynote.net/sf/c/202208132/
-- EOF --
-- MORE --