参数传递方式教材上大都说有两种,一种是值传递,另一种是指针传递。而后者本质上依然是值传递。
底层原理
对于值传递还是指针传递,传递的都是相应的值,只不过指针传递传递的是地址的副本,修改副本对原来数据没有任何影响,需要修改的是地址的指向。关于函数调用的执行流程可参考strcpy为何不安全
bool test(int* x) {int b = 10;//memcpy(a, &b, sizeof(int));x = &b;//*a = b;return true;
}int main()
{// std::cout << "Hello World!\n";std::string filename = "11.png";// FILE* fp = fopen(filename.c_str(), "rb");char* s = new char[20];int a = 0;int * p = &a;test(p);std::cout << a;
}
此时的运行结果是0
而将代码修改为
bool test(int* x) {int b = 10;//memcpy(a, &b, sizeof(int));//x = &b;*x = b;return true;
}
此时的运行结果是10,也就是修改后的内容。
两段代码的区别:第一段代码 x = &b;对应的汇编代码是,修改的是x的值得,对原本的数据a没有任何影响。
00B62F26 lea eax,[b] //将b所指向的内存地址(而不是b的值)加载到eax寄存器中
00B62F29 mov dword ptr [x],eax //将 eax 寄存器中的32位(双字)值存储到 x 所指向的内存地址中
而*x = b;对应的汇编代码是,修改可地址的指向。
00492F1C mov eax,dword ptr [x] //将x的值赋值给eax,x的值是a对应的地址
00492F1F mov ecx,dword ptr [b] //将b内存中的值复制给ecx
00492F22 mov dword ptr [eax],ecx //将ecx的值赋值给eax指向的内存中的值
char *传值问题
bool test(int* x, char * str) {char* p = new char[200];memset(p, 0, 200);p[0] = 'H';p[1] = 'E';p[2] = 'l';p[3] = '\0';int len = strlen(p);strcpy_s(str, strlen(p)+1, p);delete(p);return true;
}
不能使用str = p;
这种只是修改副本的值,对原来的char*没有任何影响。
list传值问题
c++中list传递需要使用指针或者引用,才能改变原本的值。
bool test_list(list<string> *testList) {testList->push_back("3");return false;
}
调用方:
List<String> li;
li.push_back("1");
li.push_back("2");
test_list(&li);
或者
list<string> *li = new std::list<string>();
li->push_back("1");
li->push_back("2");
test_list(li);
这两个语句都是用于创建一个存储字符串的列表。主要的区别在于:
-
数据类型的差异:前者使用的是C++标准库中的list容器,后者是使用指针创建的动态内存。
-
声明方式的差异:前者是一般的对象声明方式,后者是通过new运算符动态地分配内存并返回一个指向该内存区域的指针。
-
内存管理的差异:前者是一个自动管理内存的对象,当程序执行到其作用域结束或者该对象被销毁时,内存会自动释放;后者需要手动使用delete运算符释放动态分配的内存。
因此,建议在使用时考虑到实际情况,选择合适的声明方式和内存管理方式。如果只是需要临时存储字符串,并且不想手动管理内存,可以选择前者;如果需要在程序运行期间动态地分配和释放内存,并且需要手动管理内存,可以选择后者。
c#中的list传递,不需要指针或者引用。像java之类的语言类似,这也是java等语言比c相对简单的原因。
List<String> li = new List<String>();
li.Add("1");
li.Add("2");
await test(li);
Console.WriteLine(li);
Task test(List<String> list_test)
{list_test.Add("3");return Task.CompletedTask;}