函数返回局部变量的原理:
一般说来,函数中是可以进行局部变量的返回的,不然岂不是全部要用全局变量,如果使用了全局变量,那还有必要进行返回吗?那函数就没有它存在的意义了!但是要注意了,这里所谓的局部变量的返回很有内涵,什么样的值才可以进行返回而不出错?
其实,只要遵守一句话即可:函数不能返回指向栈内存的指针!
为什么?因为返回的都是值拷贝!
我们先看一个最普通的例子1:
#include <stdio.h>
int func()
{
int a = 10;
printf("&a = %p\n",&a);
return a;
}
int main () {
int A;
printf("&A = %p\n",&A);
A = func();
printf("%d\n",A);
return 0;
}
执行结果:
我们知道,局部变量的作用域是函数内部,函数一旦执行结束,栈上的局部变量会进行销毁,内存得到释放。因此,此时函数返回的是该局部变量的值拷贝,这是没有问题的。但是如果返回的是局部变量的地址,那么返回的只是该局部变量指针的拷贝,而随着函数运行结束,该拷贝指针所指向的栈内存已经被释放,那么指向一个未知区域就会导致调用的错误。
那如果返回的指针指向的是堆内存,又会怎么样?
这样的使用是没有问题的,在函数内new空间,在函数外delete空间。但是这样并不是一种好的编程风格,尽量在同一个作用域内进行new和delete操作,否则还要调用者手动进行内存的释放,试问这样的接口是不是很烂。如果确实需要这样做,那就传指针进去吧!
函数返回值为值对象,指针,引用
函数返回临时对象,如下例子2:
#include <iostream>
#include <string>
using namespace std;
string func()
{
string a = "hello world";
cout << "&a = " << &a << endl;
return a;
}
int main () {
string A;
cout << "&A = " << &A << endl;
A = func();
cout << A << endl;
return 0;
}
执行结果:
与例子1结论是一样的,函数返回string是值拷贝,但是拷贝的工作是string类的拷贝构造函数进行的(但是是gcc做了优化,返回值为对象时,不再产生临时对象,因而不再调用复制构造函数。函数返回值是对象的值,调用对象的拷贝构造函数?)。
但是将上面例子使用C语言改写一下,返回值是指向字符串的首地址的指针,如下例子3:
#include <stdio.h>
char* fun()
{
char a[12] = "hello world";
printf("a = %p\n",a);
return a;
}
int main()
{
char *p;
printf("p = %p\n",p);
p = fun();
printf("p = %p\n",p);
printf("%s\n",p);
return 0;
}
执行结果:
运行结果虽然没有error,但是运行结果不是我们想要的结果(乱码)。
通过warning知道:函数返回局部变量的地址。
分析:回到函数返回值的本质——值拷贝。通过打印结果也能看出,p被赋值后的值与a的值是一样的,只是函数fun结束,a[12]局部变量被销毁,所以p指向的地址(0x7fff81c45b20)所对应的值被销毁了,所以打印乱码了。
将第5行改为:
char* a = "hello world";
运行成功,也是想要的结果。这是因为"hello world"是常量,并不是在栈区的内存,所以函数结束,该常量还是存在的。
但是将上面例子使用C++语言再改写一下,返回值是返回字符串引用,如下例子4:
#include <iostream>
#include <string>
using namespace std;
string& func()
{
string a = "hello world";
cout << "&a = " << &a << endl;
return a;
}
int main () {
string A;
cout << "&A = " << &A << endl;
A = func();
cout << "&A = " << &A << endl;
cout << "A = " << A << endl;
return 0;
}
执行结果:
运行结果虽然没有error,但是运行结果不是我们想要的结果(空)。
通过warning知道:函数返回局部变量的地址。
分析:与返回指针的例子道理是一样的。