最近在通过函数返回数组时踩到了一些坑,在此记录一下。
首先C++函数的返回类型没有“数组”这一结构,因此一般情况下我们会采用指针去接受数组的首地址来进行返回。另外由于函数体内定义的变量均为局部变量,局部变量被存储在栈区会在函数调用完毕之后被系统回收,因此,如果我们采用地址传递的方式返回局部变量时就会出现问题:该指针指向的变量值因为在函数被调用结束后被系统收回已经不存在了。
#include <iostream>
using namespace std;int* func1()
{int p = 10;return &p;
}int* func2()
{int* p = new int(10);return p;
}int main()
{int *p1 = func1();cout << *p1 << endl;cout << *p1 << endl;/*int* p2 = func2();cout << *p2 << endl;cout << *p2 << endl;*/system("pause");return 0;
}
可以发现当存储在栈区的变量的地址作为函数的返回值然后做打印输出时,第一次打印的结果是正确的,但是第二次打印的结果是一串奇怪的数字而不是想要的结果:10,这是因为在第二次打印输出时,C++已经回收了该局部变量,原先的地址中存储的值已经不再是10。因此如果希望返回变量的地址,应该将该变量开辟在堆区。使用func2()函数的得到的结果如下图所示。
再回到数组的问题上来,数组名不就是一个数组的首地址吗,只要返回首地址就能够愉快用指针来遍历数组了,并且数组名退化的指针也能够通过“[]”的方式来遍历数组了。代码如下所示。
#include <iostream>
using namespace std;int* getArrary()
{int* arr = new int[10];for (int i = 0; i < 10; i++){int a = rand() % 10;arr[i] = a;}return arr;
}int main()
{int* p = getArrary();for (int i = 0; i < 10; i++){cout << p[i] << " ";}system("pause");return 0;
}
到此为止,数组已经被成功的传递并在main()函数中输出了。但是这样的代码仍然存在问题:在使用for循环遍历数组的时候,数组长度是我们自己输入的,那当我们未知数组长度时应该如何进行遍历呢?
那不简单嘛?直接sizeof(arr)/sizeof(arr[0])不就就能得到数组长度。但是我们将数组从函数中取出时用的指针去接收的数组的首地址,也就是说我们返回数组时,数组名已经退化为了指针,这时候返回的指针的大小是固定的4字节(32位编译系统),而不是对应数组所占的空间大小,这么做肯定是有问题的。
那么如何解决这个问题呢?一种办法是不使用sizeof()来得到数组的大小,而是通过自己写的函数来获取。另一种办法是,我们在传递指针的同时返回数组的大小,以返回结构体或者类的方式同时返回指针和数组长度。代码如下所示。
#include <iostream>
using namespace std;struct prtArrary
{int* p;int len;
};prtArrary getArrary()
{int* arr = new int[10];for (int i = 0; i < 10; i++){int a = rand() % 10;arr[i] = a;}int len = 10;prtArrary prtA;prtA.len = len;prtA.p = arr;return prtA;
}int main()
{prtArrary prtA = getArrary();for (int i = 0; i < prtA.len; i++){cout << prtA.p[i] << " ";}delete[] prtA.p;system("pause");return 0;
}
总结一下,在处理数组作为返回值时,应当注意一下几点:
- 采用指针接收数组首地址的方式返回。
- 因为返回的是局部变量的地址,因此数组应开辟在堆区以避免被系统回收。
- 返回值除了包含指针,还应该包含数组长度。(sizeof(指向首地址的指针)=4而不是数组大小)。