C ++从函数返回指向数组的指针的正确方法

时间:2012-10-20 21:36:03

标签: c++ arrays function pointers

我对C ++很新,一直在避免使用指针。从我在网上看到的,我不能返回一个数组,但我可以返回一个指向它的指针。我做了一个小代码来测试它,并想知道这是否是正常/正确的方法:

#include <iostream>
using namespace std;

int* test (int in[5]) {
    int* out = in;
    return out;
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int* pArr = test(arr);
    for (int i = 0; i < 5; i++) cout<<pArr[i]<<endl;
    cout<<endl;
    return 0;
}

修改:这似乎不太好。我该怎么改写呢?

int* test (int a[5], int b[5]) {
    int c[5];
    for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
    int* out = c;
    return out;
}

7 个答案:

答案 0 :(得分:16)

您现在的代码是正确的,但我很难弄清楚它是如何在现实世界中使用的。话虽如此,请注意从函数返回指针时的一些注意事项:

  • 当您创建一个语法为int arr[5];的数组时,它会在堆栈上分配并且是函数的本地数据。
  • C ++允许您返回指向此数组的指针,但它是 未定义的行为 ,以使用此指针在其本地范围之外指向的内存。阅读this great answer using a real world analogy以获得比我所能解释的更清晰的理解。
  • 如果可以保证不清除阵列的内存,仍然可以在范围外使用数组。在您的情况下,当您将arr传递给test()
  • 时,情况就是如此
  • 如果您想将指针传递给动态分配的数组而不必担心内存泄漏,您应该在std::unique_ptr / std::shared_ptr<>上阅读。

编辑 - 回答矩阵乘法的用例

您有两种选择。天真的方式是使用std::unique_ptr / std::shared_ptr<>。 Modern C ++的方法是让Matrix类重载operator *,如果你想避免复制乘法的结果,你必须使用新的rvalue references。功能。除了copy constructoroperator =destructor之外,您还需要move constructormove assignment operator。浏览this search的问题和答案,以获得有关如何实现这一目标的更多信息。

编辑2 - 回答附加问题

int* test (int a[5], int b[5]) {
    int *c = new int[5];
    for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
    return c;
}

如果您将其用作int *res = test(a,b);,那么稍后在代码中,您应该调用delete []res来释放test()函数中分配的内存。您现在看到的问题是,手动跟踪何时调用delete非常困难。因此,在答案中概述了如何处理它的方法。

答案 1 :(得分:6)

您的代码没问题。请注意,如果返回指向数组的指针,并且该数组超出范围,则不应再使用该指针。例如:

int* test (void)
{
    int out[5];
    return out;
}

上述内容永远不会有效,因为out返回时test()不再存在。不能再使用返回的指针。如果你使用它,你将阅读/写入你不应该的内存。

在原始代码中,arr数组在main()返回时超出范围。显然这没问题,因为从main()返回也意味着你的程序正在终止。

如果你想要的东西会一直存在并且不能超出范围,你应该用new分配它:

int* test (void)
{
    int* out = new int[5];
    return out;
}

返回的指针始终有效。请记住,使用delete[]

时,请在完成后再次删除它
int* array = test();
// ...
// Done with the array.
delete[] array;

删除它是回收它使用的内存的唯一方法。

答案 2 :(得分:2)

新问题的新答案:

您无法从函数返回指向自动变量(int c[5])的指针。自动变量以返回封闭块结束其生命周期(在本例中为函数) - 因此您将返回指向不存在的数组的指针。

让变量动态化:

int* test (int a[5], int b[5]) {
    int* c = new int[5];
    for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
    return c;
}

或者更改您的实施以使用std::array

std::array<int,5> test (const std::array<int,5>& a, const std::array<int,5>& b) 
{
   std::array<int,5> c;
   for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
   return c;
}

如果您的编译器没有提供std::array,您可以用包含数组的简单结构替换它:

struct array_int_5 { 
   int data[5];
   int& operator [](int i) { return data[i]; } 
   int operator const [](int i) { return data[i]; } 
};

旧问题的旧答案:

你的代码是正确的,......嗯,好吧,......没用。因为数组可以分配给没有额外功能的指针(注意你已经在函数中使用了它):

int arr[5] = {1, 2, 3, 4, 5};
//int* pArr = test(arr);
int* pArr = arr;

更多功能签名:

int* test (int in[5])

相当于:

int* test (int* in)

所以你认为没有意义。

但是这个签名需要一个数组,而不是指针:

int* test (int (&in)[5])

答案 3 :(得分:1)

引用数组的变量基本上是指向其第一个元素的指针,所以是的,您可以合法地返回指向数组的指针,因为 thery本质上是相同的东西。自己检查一下:

#include <assert.h>

int main() {
  int a[] = {1, 2, 3, 4, 5}; 

  int* pArr = a;
  int* pFirstElem = &(a[0]);

  assert(a == pArr);
  assert(a == pFirstElem);

  return 0;
}

这也意味着将数组传递给函数应该通过指针(而不是通过int in[5])来完成,并且可能还有数组的长度:

int* test(int* in, int len) {
    int* out = in;
    return out;
}

那就是说,使用指针(没有完全理解它们)是对的,这是非常危险的。例如,引用在堆栈上分配并超出范围的数组会产生未定义的行为

#include <iostream>

using namespace std;

int main() {
  int* pArr = 0;
  {
    int a[] = {1, 2, 3, 4, 5};
    pArr = a; // or test(a) if you wish
  }
  // a[] went out of scope here, but pArr holds a pointer to it

  // all bets are off, this can output "1", output 1st chapter
  // of "Romeo and Juliet", crash the program or destroy the
  // universe
  cout << pArr[0] << endl; // WRONG!

  return 0;
}

因此,如果您感觉不够用力,请使用std::vector

[回答更新的问题]

编写test函数的正确方法是:

void test(int* a, int* b, int* c, int len) {
  for (int i = 0; i < len; ++i) c[i] = a[i] + b[i];
}
...
int main() {
   int a[5] = {...}, b[5] = {...}, c[5] = {};
   test(a, b, c, 5);
   // c now holds the result
}

或者这(使用std::vector):

#include <vector>

vector<int> test(const vector<int>& a, const vector<int>& b) {
  vector<int> result(a.size());
  for (int i = 0; i < a.size(); ++i) {
    result[i] = a[i] + b[i];
  }
  return result; // copy will be elided
}

答案 4 :(得分:0)

在真实应用中,返回数组的方式称为,使用out参数。当然你实际上不必返回指向数组的指针,因为调用者已经拥有它,你只需要填充数组。传递另一个指定数组大小的参数也是很常见的,以免溢出它。

使用out参数的缺点是调用者可能不知道数组需要多大才能存储结果。在这种情况下,您可以返回std :: vector或类似的数组类实例。

答案 5 :(得分:0)

您的代码(看起来没问题)不会返回指向数组的指针。它返回一个指向 数组的第一个元素的指针。

事实上,这通常是你想要做的。大多数数组操作都是通过指向各个元素的指针完成的,而不是通过指向整个数组的指针来完成的。

可以定义一个指向数组的指针,例如:

double (*p)[42];

p定义为指向double的42个元素数组的指针。一个很大的问题是你必须指定数组中元素的数量作为类型的一部分 - 并且该数字必须是编译时常量。大多数处理数组的程序都需要处理不同大小的数组;给定数组的大小在创建后不会发生变化,但其初始大小在编译时不一定是已知的,不同的数组对象可以有不同的大小。

指向数组第一个元素的指针允许您使用指针算法或索引运算符[]来遍历数组的元素。 但是指针不会告诉你数组有多少元素;你通常必须自己跟踪。

如果函数需要创建数组并返回指向其第一个元素的指针,则必须以多种方式之一自行管理该数组的存储。您可以让调用者传入指向数组对象(的第一个元素)的指针,可能还有指定其大小的另一个参数 - 这意味着调用者必须知道数组需要多大。或者函数可以返回指向函数内部定义的静态数组(的第一个元素)的指针 - 这意味着数组的大小是固定的,同一个数组将被第二次调用函数破坏。或者函数可以在堆上分配数组 - 这使得调用者负责以后解除分配。

到目前为止我写的所有东西都是C和C ++的共同点,事实上它在C语言中比C ++更具风格。 comp.lang.c FAQ的第6节讨论了C中数组和指针的行为。

但如果你是用C ++编写的,那么最好使用C ++习语。例如,C ++标准库提供了许多定义容器类的头文件,例如<vector><array>,它们将为您处理大部分内容。除非你有特殊的理由使用原始数组和指针,否则你最好只使用C ++容器。

编辑:我认为您在输入此答案时编辑了您的问题。问题末尾的新代码就像你观察者一样,没有任何好处;它返回一个指向对象的指针,该对象在函数返回后立即停止存在。我想我已经涵盖了其他选择。

答案 6 :(得分:0)

你可以(有点)返回一个数组

而不是

int m1[5] = {1, 2, 3, 4, 5};
int m2[5] = {6, 7, 8, 9, 10};
int* m3 = test(m1, m2);

struct mystruct
{
  int arr[5];
};


int m1[5] = {1, 2, 3, 4, 5};
int m2[5] = {6, 7, 8, 9, 10};
mystruct m3 = test(m1,m2);

测试看起来像

struct mystruct test(int m1[5], int m2[5])
{
  struct mystruct s;
  for (int i = 0; i < 5; ++i ) s.arr[i]=m1[i]+m2[i];
  return s;
}

效率不高,因为复制它会传递数组的副本