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

C++ correct way to return pointer to array from function

提问人:asimes 提问时间:10/21/2012 最后编辑:NathanOliverasimes 更新时间:10/25/2022 访问量:179577

问:

我对 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;
}
C++(英语:C++) 阵 列 功能 指针

评论

0赞 Pubby 10/21/2012
请查看。std::array
0赞 asimes 10/21/2012
它似乎有效,我只是想知道它是否有任何问题。我听说错误地使用指针可能很危险。
1赞 Some programmer dude 10/21/2012
数组可以毫无问题地作为指针传递,但不能使用指针分配给数组。
1赞 Code Monkey 10/21/2012
在编码业务中,如果某件事有效,那就是正确的。
3赞 Geoff Montee 10/21/2012
只是不要尝试返回指向本地堆栈数组的指针。这样的事情会很糟糕。int* funct() { int arr[5]; return arr; }

答:

28赞 vvnraman 10/21/2012 #1

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

  • 当您使用 语法 创建数组时,它会分配在堆栈上,并且是函数的本地数组。int arr[5];
  • C++ 允许您返回指向此数组的指针,但使用此指针指向的内存在其本地范围之外是未定义的行为使用现实世界的类比阅读这个伟大的答案,以获得比我所能解释的更清晰的理解。
  • 如果可以保证数组的内存未被清除,则仍然可以在作用域之外使用数组。在您的情况下,当您传递到 .arrtest()
  • 如果要传递指向动态分配数组的指针而不必担心内存泄漏,则应对 / 进行一些读取。std::unique_ptrstd::shared_ptr<>

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

您有两种选择。幼稚的方法是使用 /。现代 C++ 的方法是有一个重载的类,如果你想避免复制乘法的结果以将其从函数中取出,你绝对必须使用 new。除了有你的 、 和 之外,你还需要有 和 。通过此搜索的问题和答案,以更深入地了解如何实现这一目标。std::unique_ptrstd::shared_ptr<>Matrixoperator *rvalue referencescopy constructoroperator =destructormove constructormove assignment operator

编辑 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 []restest()delete

评论

0赞 Geoff Montee 10/21/2012
但是,返回本地声明的数组是可以的,因为它不在堆栈上。static int arr[5];
0赞 vvnraman 10/21/2012
是的。完全。但是,由于只有一个副本,因此用例仅限于您可以使用它执行的操作。static int arr[5]
0赞 asimes 10/21/2012
将来,我计划制作一个矩阵乘法器。两个矩阵将是输入,输出将是一个新矩阵。
0赞 Geoff Montee 10/21/2012
没错,@wnraman。如果要维护某种全局状态,则声明为 的变量非常有用。像这样的事情浮现在脑海中,尽管这可能使用了 .staticstrtokstatic char*
18赞 Nikos C. 10/21/2012 #2

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

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

以上方法永远不会起作用,因为返回时不再存在。返回的指针不能再使用。如果你确实使用它,你将读取/写入你不应该的内存。outtest()

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

If you want something that will stick around and cannot go out of scope, you should allocate it with :new

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

The returned pointer will always be valid. Remember do delete it again when you're done with it though, using :delete[]

int* array = test();
// ...
// Done with the array.
delete[] array;

Deleting it is the only way to reclaim the memory it uses.

评论

2赞 Martin Beckett 10/21/2012
比这更糟糕的是 - 它有时会起作用,具体取决于其他正在运行的内容、运行时间和月相
0赞 asimes 10/21/2012
这是我现在遇到的一个问题。我希望在参数中有两个数组并返回一个新数组(将来将是两个输入数组相乘)。答案似乎是在参数中有三个数组,其中第三个数组不重要,但具有可返回的目的。有没有更好的方法?
0赞 Geoff Montee 10/21/2012
嘿@asimes。你的想法是对的。发送第三个数组是最好的选择,比如 where is , is 和 is 。boolean matMult(int* A, int* B, int* C, size_t m, size_t n, size_t p);Am x nBm x pCn x p
0赞 Geoff Montee 10/21/2012
但是,@asimes,您也可以按照 Nikos 的建议声明数组。但是,使用矩阵乘法进行预分配相对容易,因为维度是提前知道的。new
0赞 asimes 10/21/2012
@Geoff_Montee,我会进行实验,看看什么似乎有意义。只有三个参数而不必担心删除可能会更容易。
0赞 user1610015 10/21/2012 #3

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

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

2赞 PiotrNycz 10/21/2012 #4

新问题的新答案:

不能从函数返回指向自动变量 () 的指针。自动变量以返回封闭块(在本例中为函数)结束其生存期 - 因此您将返回指向不存在的数组的指针。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;

函数的 Morever 签名:

int* test (int in[5])

相当于:

int* test (int* in)

所以你看这没有意义。

但是,此签名采用数组,而不是指针:

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

评论

0赞 asimes 10/21/2012
谢谢,我使用了您发布的第一个函数(带有新的 int),它似乎可以做我想做的事。我应该利用它,对吧?此外,这将处于一个永远循环的循环中(OpenGL 基于这些数组中的内容进行绘制)。当我不再需要它时,我只需要删除 m3,对吧?int* arr3 = test(arr1, arr2)
0赞 PiotrNycz 10/21/2012
是的 - 您需要删除此返回的数组 - 请记住,这必须由 不仅 .delete[]delete
2赞 dorserg 10/21/2012 #5

引用数组的变量基本上是指向其第一个元素的指针,所以是的,您可以合法地返回指向数组的指针,因为 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;
}

So if you don't feel competent enough, just use .std::vector

[answer to the updated question]

The correct way to write your function is either this: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
}

Or this (using ):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
}

评论

0赞 asimes 10/21/2012
@dorsrg, thank you for the explanation. I decided to go with but your explanation of undefined behavior is good to knownew int
0赞 dorserg 10/21/2012
@asimes, if you decided to with plain C arrays, I'd suggest you use a version that accepts output array as a function parameter. Firstly, this saves you writing (because output array is allocated on the stack). Secondly, it will ensure that only one part of code needs to know about constant 5 (right now you use it in both , and ). Also, if you learned something useful from this answer, care to upvote? Thanks :)delete[] pmaintest
0赞 Keith Thompson 10/21/2012 #6

Your code (which looks ok) doesn't return a pointer to an array. It returns a pointer to the first element of an array.

In fact that's usually what you want to do. Most manipulation of arrays are done via pointers to individual elements, not via pointers to the array as a whole.

You can define a pointer to an array, for example this:

double (*p)[42];

defines as a pointer to a 42-element array of s. A big problem with that is that you have to specify the number of elements in the array as part of the type -- and that number has to be a compile-time constant. Most programs that deal with arrays need to deal with arrays of varying sizes; a given array's size won't vary after it's been created, but its initial size isn't necessarily known at compile time, and different array objects can have different sizes.pdouble

A pointer to the first element of an array lets you use either pointer arithmetic or the indexing operator to traverse the elements of the array. But the pointer doesn't tell you how many elements the array has; you generally have to keep track of that yourself.[]

If a function needs to create an array and return a pointer to its first element, you have to manage the storage for that array yourself, in one of several ways. You can have the caller pass in a pointer to (the first element of) an array object, probably along with another argument specifying its size -- which means the caller has to know how big the array needs to be. Or the function can return a pointer to (the first element of) a static array defined inside the function -- which means the size of the array is fixed, and the same array will be clobbered by a second call to the function. Or the function can allocate the array on the heap -- which makes the caller responsible for deallocating it later.

Everything I've written so far is common to C and C++, and in fact it's much more in the style of C than C++. Section 6 of the comp.lang.c FAQ discusses the behavior of arrays and pointers in C.

But if you're writing in C++, you're probably better off using C++ idioms. For example, the C++ standard library provides a number of headers defining container classes such as and , which will take care of most of this stuff for you. Unless you have a particular reason to use raw arrays and pointers, you're probably better off just using C++ containers instead.<vector><array>

EDIT : I think you edited your question as I was typing this answer. The new code at the end of your question is, as you observer, no good; it returns a pointer to an object that ceases to exist as soon as the function returns. I think I've covered the alternatives.

0赞 AndersK 10/21/2012 #7

you can (sort of) return an array

instead of

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

write

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);

where test looks like

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;
}

not very efficient since one is copying it delivers a copy of the array