永远不要返回局部变量的指针或引用

永远不要返回函数的局部变量的指针或引用,因为函数执行完之后,将释放分配给局部变量的内存,这段内存可能会被回收用作其他用途,也可能暂时不动,这是没法预测的。如果被回收用途其他用途了,那返回的指针就是一个野指针,要么读取的数据是错误的,要么出现内存访问异常错误。如果这段内存暂时还没被回收,那从指针读到的数据也还是正确的,但你没法预料下一秒是否会被回收了。
举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

int *sum(int number1, int number2) {
int result = number1 + number2;
return &result;
}

int main() {
int *result = sum(2, 3);
cout<< "2 + 3 = " << *result<<endl;
return 0;
}

这段代码在我的电脑上运行就会出现段错误,程序异常终止。
那有时候我们真的需要返回一个局部变量的话,应该怎么办呢?有很多方法可以满足这种需求。

1. 静态局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

int *sum(int number1, int number2) {
static int result = number1 + number2;
return &result;
}

int main() {
int *result = sum(2, 3);
cout<< "2 + 3 = " << *result<<endl;
return 0;
}

静态局部变量存放在内存的全局数据区。函数结束时,静态局部变量不会消失,每次该函数调用时,也不会为其重新分配空间。它始终驻留在全局数据区,直到程序运行结束。静态局部变量的初始化与全局变量类似.如果不为其显式初始化,则C++自动为其初始化为0。
静态局部变量与全局变量共享全局数据区,但静态局部变量只在定义它的函数中可见。

简单来说就是静态局部变量具有全局变量的生命周期,具有局部变量的作用域。
在实际项目中我们一般推荐使用这种方式。

2. 全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

static int result;
int *sum(int number1, int number2) {
result = number1 + number2;
return &result;
}

int main() {
int *result = sum(2, 3);
cout<< "2 + 3 = " << *result<<endl;
return 0;
}

在标准库中的 struct tm *localtime (const time_t *__timer) 实现就是返回的全局变量指针。
建议优先考虑静态局部变量,如果非要使用全局变量的话,最好使用 static 把全局变量的作用域限制在本文件内。避免造成命名污染。

3. 动态申请内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;

int *sum(int number1, int number2) {
int *result = new int;
*result = number1 + number2;
return result;
}

int main() {
int *result = sum(2, 3);
cout<< "2 + 3 = " << *result<<endl;
delete result;
return 0;
}

使用这种方法需要注意,由于用new申请的动态内存,调用者需要负责释放(delete)这段内存。否则就会造成内存泄漏。
很多内存泄漏问题都是这种动态申请内存后忘了释放内存造成的。所以这种方法我们能避免就尽量避免。避免不了的话,则建议使用智能指针来自动管理内存的释放问题。

4. 智能指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <memory>
using namespace std;

shared_ptr<int> sum(int number1, int number2) {
shared_ptr<int> result(new int);
*result = number1 + number2;
return result;
}

int main() {
shared_ptr<int> result = sum(2, 3);
cout<< "2 + 3 = " << *result <<endl;
return 0;
}

这种写法就不需要手动释放分配的内存。但这是 c++ 11 之后才支持的语法。在 android 系统中我们可以使用 sp 智能指针。
对于内存占用比较大的对象来说,推荐使用智能指针。因为系统会自动释放内存, 而使用静态局部变量的话,内存就会一直被占用着。