7.2.2 函数返回时的“副本”
函数执行完毕后,函数内部声明的局部变量会自动消亡,对应的内存被释放,由内存管理器收回,但返回值会被放置(复制)到指定位置(可能是CPU寄存器,也可能是某个内存单元),然后上级函数从这个位置取得返回值。这个位置,可以看成是函数返回值的“副本”,这解释了为什么可以用“return局部变量;”来返回一个值,示例见代码7.6。
代码7.6 函数返回时的“副本”AboutReturn
<————————————文件名:example706.cpp———————————————> 01 #include<iostream> 02 using namespace std; 03 char*GetMem(int num)//函数定义,返回char型指针 04 { 05 char*p=new char[num]; 06 return p;//将指向动态内存的指针p返回,复制到外部,p会被撤销 07 } 08 int main() 09 { 10 char*pChar=NULL; 11 pChar=GetMem(10);//函数调用,pChar指向函数GetMem中申请的动态内存 12 if(pChar!=NULL)//如果申请成功 13 { 14 cout<<"内存申请成功"<<endl; 15 delete[]pChar;//释放内存资源 16 } 17 else 18 cout<<"内存申请失败"<<endl; 19 return 0; 20 }
输出结果如下所示。
内存申请成功
【代码解析】在GetMem()函数内声明的char型指针p虽然是局部变量,但第6行,通过“return p;”语句,将该指针的值复制到了外部副本中,通过外部副本传递给了pChar,这样,pChar便指向了在GetMem()函数中申请的动态内存。
前面“野指针”一节也已经讲到,不能返回指向栈内存的指针,那样的话,即使上级函数从函数返回的“副本”处取得了指针值,但该指针指向的内存已被撤销,会给系统带来意想不到的错误,代码7.1就是个很好的例子。
如果函数返回的指针指向的是静态存储区,情况会是什么样呢?见代码7.7。
代码7.7 返回指向静态存储区的指针SpecialPointer
<———————————-文件名:example707.cpp———————————————-> 01 #include<iostream> 02 using namespace std; 03 char*GetMem(int num) 04 { 05 char*p="Hello,C++";//指针p指向的是常量字符串,位于静态存储区 06 return p; 07 } 08 int main() 09 { 10 char*pChar=NULL; 11 pChar=GetMem(10);//调用GetMem函数返回指针pChar,不可对pChar 12 //指向的内存进行改写 13 if(pChar!=NULL) 14 cout<<"内存申请成功"<<endl; 15 else 16 cout<<"内存申请失败"<<endl; 17 return 0; 18 }
输出结果如下所示。
内存申请成功
【代码解析】该代码运行没有问题,但因为代码第5行的“Hello,C++”是常量字符串,位于静态存储区.rodata,函数GetMem()每次返回的始终是同一块“只读”内存,无法通过指针对该块内存进行修改,否则会引发修改常量的错误。
注意
对比代码7.1中“char sz[]="Hello,C++";”和代码7.7中“char*p="Hello,C++";”体会数组和指针的异同。