C++ 在函数中返回数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3473438/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 12:54:01  来源:igfitidea点击:

Return array in a function

c++arrayspointersfunctionreturn

提问by Ismail Marmoush

I have an array int arr[5]that is passed to a function fillarr(int arr[]):

我有一个int arr[5]传递给函数的数组fillarr(int arr[])

int fillarr(int arr[])
{
    for(...);
    return arr;
}
  1. How can I return that array?
  2. How will I use it, say I returned a pointer how am I going to access it?
  1. 我怎样才能返回那个数组?
  2. 我将如何使用它,假设我返回了一个指针,我将如何访问它?

采纳答案by Brent Writes Code

In this case, your array variable arrcan actually also be treated as a pointer to the beginning of your array's block in memory, by an implicit conversion. This syntax that you're using:

在这种情况下,arr通过隐式转换,您的数组变量实际上也可以被视为指向内存中数组块开头的指针。您正在使用的这种语法:

int fillarr(int arr[])

Is kind of just syntactic sugar. You could really replace it with this and it would still work:

只是一种语法糖。你真的可以用这个替换它,它仍然可以工作:

int fillarr(int* arr)

So in the same sense, what you want to return from your function is actually a pointer to the first element in the array:

所以在同样的意义上,你想要从你的函数返回的实际上是一个指向数组中第一个元素的指针:

int* fillarr(int arr[])

And you'll still be able to use it just like you would a normal array:

您仍然可以像使用普通数组一样使用它:

int main()
{
  int y[10];
  int *a = fillarr(y);
  cout << a[0] << endl;
}

回答by Potatoswatter

C++ functions can't return C-style arrays by value. The closest thing is to return a pointer. Furthermore, an array type in the argument list is simply converted to a pointer.

C++ 函数不能按值返回 C 风格的数组。最接近的是返回一个指针。此外,参数列表中的数组类型被简单地转换为指针。

int *fillarr( int arr[] ) { // arr "decays" to type int *
    return arr;
}

You can improve it by using an array references for the argument and return, which prevents the decay:

您可以通过对参数和返回使用数组引用来改进它,从而防止衰减:

int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
    return arr;
}

With Boost or C++11, pass-by-reference is only optional and the syntax is less mind-bending:

使用 Boost 或 C++11,传递引用只是可选的,并且语法不那么令人费解:

array< int, 5 > &fillarr( array< int, 5 > &arr ) {
    return arr; // "array" being boost::array or std::array
}

The arraytemplate simply generates a structcontaining a C-style array, so you can apply object-oriented semantics yet retain the array's original simplicity.

array模板仅生成一个struct包含 C 样式数组的对象,因此您可以应用面向对象的语义,同时保留数组的原始简单性。

回答by cubuspl42

In C++11, you can return std::array.

在 C++11 中,您可以返回std::array.

#include <array>
using namespace std;

array<int, 5> fillarr(int arr[])
{
    array<int, 5> arr2;
    for(int i=0; i<5; ++i) {
        arr2[i]=arr[i]*2;
    }
    return arr2;
}

回答by Chubsdad

$8.3.5/8 states-

$8.3.5/8 个州-

"Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions."

“函数不应具有数组或函数类型的返回类型,尽管它们可能具有指针类型的返回类型或对此类事物的引用。不应有函数数组,尽管可以有指向函数的指针数组。”

int (&fn1(int (&arr)[5]))[5]{     // declare fn1 as returning refernce to array
   return arr;
}

int *fn2(int arr[]){              // declare fn2 as returning pointer to array
   return arr;
}


int main(){
   int buf[5];
   fn1(buf);
   fn2(buf);
}

回答by SingleNegationElimination

the answer may depend a bit on how you plan to use that function. For the simplest answer, lets decide that instead of an array, what you really want is a vector. Vectors are nice because the look for all the world like boring, ordinary values you can store in regular pointers. We'll look at other options and why you want them afterwards:

答案可能在一定程度上取决于您打算如何使用该功能。对于最简单的答案,让我们决定您真正想要的是向量而不是数组。向量很不错,因为它看起来像可以存储在常规指针中的无聊的普通值。我们将查看其他选项以及您之后想要它们的原因:

std::vector<int> fillarr( std::vector<int> arr ) {
    // do something
    return arr;
}

This will do exactly what you expect it to do. The upside is that std::vectortakes care of making sure everything is handled cleanly. the downside is that this copies a very large amount of data, if your array is large. In fact it copies every element of the array twice. first it copies the vector so that the function can use it as a parameter. then it copies it again to return it to the caller. If you can handle managing the vector yourself, you can do things quite a bit more easily. (it may copy it a third time if the caller needs to store it in a variable of some sort to do more calculation)

这将完全符合您的期望。好处是std::vector确保一切都得到干净处理。缺点是如果您的阵列很大,这会复制大量数据。事实上,它复制了数组的每个元素两次。首先它复制向量,以便函数可以将其用作参数。然后它再次复制它以将其返回给调用者。如果您可以自己管理矢量,则可以更轻松地完成工作。(如果调用者需要将它存储在某种变量中以进行更多计算,它可能会第三次复制它)

It looks like what you're really trying to do is just populate a collection. if you don't have a specific reason to return a new instance of a collection, then don't. we can do it like this

看起来您真正想做的只是填充一个集合。如果您没有特定理由返回集合的新实例,则不要这样做。我们可以这样做

void fillarr(std::vector<int> &  arr) {
    // modify arr
    // don't return anything
}

this way you get a reference to the array passed to the function, not a private copy of it. any changes you make to the parameter are seen by the caller. You could return a reference to it if you want, but that's not really a great idea, since it sort of implies that you're getting something different from what you passed.

通过这种方式,您可以获得对传递给函数的数组的引用,而不是它的私有副本。调用者可以看到您对参数所做的任何更改。如果需要,您可以返回对它的引用,但这并不是一个好主意,因为它有点暗示您得到的东西与您传递的东西不同。

If you really do need a new instance of the collection, but want to avoid having it on the stack (and all the copying that entails), you need to create some kind of contract for how that instance is handled. the easiest way to do that is to use a smart pointer, which keeps the referenced instance around as long as anyone is holding onto it. It goes away cleanly if it goes out of scope. That would look like this.

如果您确实需要集合的新实例,但又想避免将其放在堆栈中(以及所有需要进行的复制),则需要为如何处理该实例创建某种契约。最简单的方法是使用智能指针,只要有人持有它,它就会保持引用的实例。如果它超出范围,它就会消失。那看起来像这样。

std::auto_ptr<std::vector<int> > fillarr( const std::vector<int> & arr) {
    std::auto_ptr<std::vector<int> > myArr(new std::vector<int>);
    // do stuff with arr and *myArr
    return myArr;
}

For the most part, using *myArrworks identically to using a plain vanilla vector. This example also modifies the parameter list by adding the constkeyword. Now you get a reference without copying it, but you can't modify it, so the caller knows it'll be the same as before the function got to it.

在大多数情况下, using 的*myArr工作方式与使用普通的 vanilla 向量相同。此示例还通过添加const关键字来修改参数列表。现在您无需复制即可获得引用,但您无法修改它,因此调用者知道它将与函数到达之前相同。

All of this is swell, but idiomatic c++ rarely works with collections as a whole. More normally, you will be using iterators over those collections. that would look something more like this

所有这些都很好,但是惯用的 c++ 很少与集合一起工作。更常见的是,您将在这些集合上使用迭代器。那看起来更像这样

template <class Iterator>
Iterator fillarr(Iterator arrStart, Iterator arrEnd) {
    Iterator arrIter = arrStart;
    for(;arrIter <= arrEnd; arrIter++)
       ;// do something
    return arrStart;
}

Using it looks a bit odd if you're not used to seeing this style.

如果你不习惯看到这种风格,使用它看起来有点奇怪。

vector<int> arr;
vector<int>::iterator foo = fillarr(arr.begin(), arr.end());

foo now 'points to' the beginning of the modified arr.

foo 现在“指向”修改后的arr.

What's really nice about this is that it works equally well on vector as on plain C arrays and many other types of collection, for example

真正好的一点是它在向量上与在普通 C 数组和许多其他类型的集合上同样有效,例如

int arr[100];
int *foo = fillarr(arr, arr+100);

Which now looks an awful lot like the plain pointer examples given elsewhere in this question.

现在看起来非常像这个问题其他地方给出的普通指针示例。

回答by Matt

This:

这个:

int fillarr(int arr[])

is actually treated the same as:

实际上被视为与:

int fillarr(int *arr)

Now if you really want to return an array you can change that line to

现在如果你真的想返回一个数组,你可以将该行更改为

int * fillarr(int arr[]){
    // do something to arr
    return arr;
}

It's not really returning an array. you're returning a pointer to the start of the array address.

它并没有真正返回一个数组。您正在返回一个指向数组地址开头的指针。

But remember when you pass in the array, you're only passing in a pointer. So when you modify the array data, you're actually modifying the data that the pointer is pointing at. Therefore before you passed in the array, you must realise that you already have on the outside the modified result.

但是请记住,当您传入数组时,您只是传入了一个指针。因此,当您修改数组数据时,实际上是在修改指针指向的数据。因此,在传入数组之前,您必须意识到在外部已经有了修改后的结果。

e.g.

例如

int fillarr(int arr[]){
   array[0] = 10;
   array[1] = 5;
}

int main(int argc, char* argv[]){
   int arr[] = { 1,2,3,4,5 };

   // arr[0] == 1
   // arr[1] == 2 etc
   int result = fillarr(arr);
   // arr[0] == 10
   // arr[1] == 5    
   return 0;
}

I suggest you might want to consider putting a length into your fillarr function like this.

我建议您可能要考虑像这样将长度放入您的 fillarr 函数中。

int * fillarr(int arr[], int length)

That way you can use length to fill the array to it's length no matter what it is.

这样你就可以使用 length 将数组填充到它的长度,无论它是什么。

To actually use it properly. Do something like this:

真正正确地使用它。做这样的事情:

int * fillarr(int arr[], int length){
   for (int i = 0; i < length; ++i){
      // arr[i] = ? // do what you want to do here
   }
   return arr;
}

// then where you want to use it.
int arr[5];
int *arr2;

arr2 = fillarr(arr, 5);

// at this point, arr & arr2 are basically the same, just slightly
// different types.  You can cast arr to a (char*) and it'll be the same.

If all you're wanting to do is set the array to some default values, consider using the built in memset function.

如果您只想将数组设置为一些默认值,请考虑使用内置的 memset 函数。

something like: memset((int*)&arr, 5, sizeof(int));

类似于: memset((int*)&arr, 5, sizeof(int));

While I'm on the topic though. You say you're using C++. Have a look at using stl vectors. Your code is likely to be more robust.

虽然我在这个话题上。你说你正在使用 C++。看看使用 stl 向量。您的代码可能更健壮。

There are lots of tutorials. Here is one that gives you an idea of how to use them. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html

有很多教程。这是一个让您了解如何使用它们的方法。 http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html

回答by Sandeep

to return an array from a function , let us define that array in a structure; So it looks something like this

要从函数返回一个数组,让我们在结构中定义该数组;所以它看起来像这样

struct Marks{
   int list[5];
}

Now let us create variables of the type structure.

现在让我们创建类型结构的变量。

typedef struct Marks marks;
marks marks_list;

We can pass array to a function in the following way and assign value to it:

我们可以通过以下方式将数组传递给函数并为其赋值:

void setMarks(int marks_array[]){
   for(int i=0;i<sizeof(marks_array)/sizeof(int);i++)
       marks_list.list[i]=marks_array[i];
}

We can also return the array. To return the array , the return type of the function should be of structure type ie marks. This is because in reality we are passing the structure that contains the array. So the final code may look like this.

我们也可以返回数组。要返回数组,函数的返回类型应该是结构类型,即标记。这是因为实际上我们正在传递包含数组的结构。所以最终的代码可能是这样的。

marks getMarks(){
 return marks_list;
}

回答by Adrian

This is a fairly old question, but I'm going to put in my 2 cents as there are a lot of answers, but none showing all possible methods in a clear and concise manner (not sure about the concise bit, as this got a bit out of hand. TL;DR ).

这是一个相当古老的问题,但我要投入 2 美分,因为有很多答案,但没有一个以清晰简洁的方式展示所有可能的方法(不确定简洁的一点,因为这得到了一个有点失控。TL;DR)。

I'm assuming that the OP wanted to return the array that was passed in without copying as some means of directly passing this to the caller to be passed to another function to make the code look prettier.

我假设 OP 想要返回传入的数组而不进行复制,作为将其直接传递给调用者以传递给另一个函数以使代码看起来更漂亮的某种方法。

However, to use an array like this is to let it decay into a pointer and have the compiler treat it likean array. This can result in subtle bugs if you pass in an array like, with the function expecting that it will have 5 elements, but your caller actually passes in some other number.

然而,像这样使用数组就是让它衰减成一个指针,并让编译器把它当作一个数组来对待。如果你传入一个数组,这可能会导致细微的错误,函数期望它有 5 个元素,但你的调用者实际上传入了一些其他数字。

There a few ways you can handle this better. Pass in a std::vectoror std::array(not sure if std::arraywas around in 2010 when the question was asked). You can then pass the object as a reference without any copying/moving of the object.

有几种方法可以更好地处理这个问题。传递一个std::vectorstd::array(不确定std::array在 2010 年提出问题时是否存在)。然后,您可以将对象作为引用传递,而无需对对象进行任何复制/移动。

std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
    // (before c++11)
    for(auto it = arr.begin(); it != arr.end(); ++it)
    { /* do stuff */ }

    // Note the following are for c++11 and higher.  They will work for all
    // the other examples below except for the stuff after the Edit.

    // (c++11 and up)
    for(auto it = std::begin(arr); it != std::end(arr); ++it)
    { /* do stuff */ }

    // range for loop (c++11 and up)
    for(auto& element : arr)
    { /* do stuff */ }

    return arr;
}

std::vector<int>& fillarr(std::vector<int>& arr)
{
    for(auto it = arr.begin(); it != arr.end(); ++it)
    { /* do stuff */ }
    return arr;
}

However, if you insist on playing with C arrays, then use a template which will keep the information of how many items in the array.

但是,如果您坚持使用 C 数组,那么请使用一个模板来保存数组中有多少项的信息。

template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
    // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
    for(int* it = arr; it != arr + N; ++it)
    { /* do stuff */ }
    return arr;
}

Except, that looks butt ugly, and super hard to read. I now use something to help with that which wasn't around in 2010, which I also use for function pointers:

除了,那看起来很丑,而且超级难读。我现在使用一些东西来帮助解决 2010 年不存在的问题,我也将其用于函数指针:

template <typename T>
using type_t = T;

template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
    // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
    for(int* it = arr; it != arr + N; ++it)
    { /* do stuff */ }
    return arr;
}

This moves the type where one would expect it to be, making this farmore readable. Of course, using a template is superfluous if you are not going to use anything but 5 elements, so you can of course hard code it:

这使其中一个会希望它是,使得这种类型远远更具可读性。当然,如果您只使用 5 个元素,那么使用模板是多余的,因此您当然可以对其进行硬编码:

type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

As I said, my type_t<>trick wouldn't have worked at the time this question was asked. The best you could have hoped for back then was to use a type in a struct:

正如我所说,type_t<>在提出这个问题时,我的技巧是行不通的。那时您可能希望的最好的方法是在结构中使用类型:

template<typename T>
struct type
{
  typedef T type;
};

typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

Which starts to look pretty ugly again, but at least is still more readable, though the typenamemay have been optional back then depending on the compiler, resulting in:

这又开始看起来很丑陋,但至少仍然更具可读性,尽管当时typename可能是可选的,这取决于编译器,导致:

type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

And then of course you could have specified a specific type, rather than using my helper.

然后当然你可以指定一个特定的类型,而不是使用我的助手。

typedef int(&array5)[5];

array5 fillarr(array5 arr)
{
    // Prefer using the compiler to figure out how many elements there are
    // as it reduces the number of locations where you have to change if needed.
    for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
    { /* do stuff */ }
    return arr;
}

Back then, the free functions std::begin()and std::end()didn't exist, though could have been easily implemented. This would have allowed iterating over the array in a safer manner as they make sense on a C array, but not a pointer.

当时,自由的功能std::begin()std::end()不存在,但可能已经很容易实现。这将允许以更安全的方式迭代数组,因为它们在 C 数组上有意义,但不是指针。

As for accessing the array, you could either pass it to another function that takes the same parameter type, or make an alias to it (which wouldn't make much sense as you already have the original in that scope). Accessing a array reference is just like accessing the original array.

至于访问数组,您可以将它传递给另一个采用相同参数类型的函数,或者为其创建别名(这没有多大意义,因为您已经在该范围内拥有原始文件)。访问数组引用就像访问原始数组一样。

void other_function(type_t<int(&)[5]> x) { /* do something else */ }

void fn()
{
    int array[5];
    other_function(fillarr(array));
}

or

或者

void fn()
{
    int array[5];
    auto& array2 = fillarr(array); // alias. But why bother.
    int forth_entry = array[4];
    int forth_entry2 = array2[4]; // same value as forth_entry
}

To summarize, it is best to not allow an array decay into a pointer if you intend to iterate over it. It is just a bad idea as it keeps the compiler from protecting you from shooting yourself in the foot and makes your code harder to read. Always try and help the compiler help you by keeping the types as long as possible unless you have a very good reason not to do so.

总而言之,如果您打算对其进行迭代,最好不要让数组衰减为指针。这只是一个坏主意,因为它使编译器无法保护您免于受到伤害,并使您的代码更难阅读。除非您有充分的理由不这样做,否则始终尝试通过尽可能长的保留类型来帮助编译器帮助您。

Edit

编辑

Oh, and for completeness, you can allow it to degrade to a pointer, but this decouples the array from the number of elements it holds. This is done a lot in C/C++ and is usually mitigated by passing the number of elements in the array. However, the compiler can't help you if you make a mistake and pass in the wrong value to the number of elements.

哦,为了完整起见,您可以允许它降级为指针,但这会将数组与其保存的元素数量分离。这在 C/C++ 中做了很多,通常通过传递数组中的元素数量来缓解。但是,如果您犯了错误并将错误的值传递给元素数量,编译器将无法帮助您。

// separate size value
int* fillarr(int* arr, size_t size)
{
    for(int* it = arr; it != arr + size; ++it)
    { /* do stuff */ }
    return arr;
}

Instead of passing the size, you can pass the end pointer, which will point to one past the end of your array. This is useful as it makes for something that is closer to the std algorithms, which take a begin and and end pointer, but what you return is now only something that you must remember.

您可以传递结束指针,而不是传递大小,该指针将指向数组末尾之后的指针。这很有用,因为它使一些更接近 std 算法的东西,它采用开始和结束指针,但你现在返回的只是你必须记住的东西。

// separate end pointer
int* fillarr(int* arr, int* end)
{
    for(int* it = arr; it != end; ++it)
    { /* do stuff */ }
    return arr;
}

Alternatively, you can document that this function will only take 5 elements and hope that the user of your function doesn't do anything stupid.

或者,您可以记录此函数将只需要 5 个元素,并希望您的函数的用户不要做任何愚蠢的事情。

// I document that this function will ONLY take 5 elements and 
// return the same array of 5 elements.  If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
    for(int* it = arr; it != arr + 5; ++it)
    { /* do stuff */ }
    return arr;
}

Note that the return value has lost it's original type and is degraded to a pointer. Because of this, you are now on your own to ensure that you are not going to overrun the array.

请注意,返回值已丢失其原始类型并降级为指针。因此,您现在需要自己确保不会超出阵列。

You could pass a std::pair<int*, int*>, which you can use for begin and end and pass that around, but then it really stops looking like an array.

您可以传递 a std::pair<int*, int*>,您可以将其用于开始和结束并传递它,但是它真的不再看起来像一个数组。

std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
    for(int* it = arr.first; it != arr.second; ++it)
    { /* do stuff */ }
    return arr; // if you change arr, then return the original arr value.
}

void fn()
{
    int array[5];
    auto array2 = fillarr(std::make_pair(&array[0], &array[5]));

    // Can be done, but you have the original array in scope, so why bother.
    int fourth_element = array2.first[4];
}

or

或者

void other_function(std::pair<int*, int*> array)
{
    // Can be done, but you have the original array in scope, so why bother.
    int fourth_element = array2.first[4];
}

void fn()
{
    int array[5];
    other_function(fillarr(std::make_pair(&array[0], &array[5])));
}

Funny enough, this is very similar to how std::initializer_listwork (c++11), but they don't work in this context.

有趣的是,这与std::initializer_list工作方式(c++11)非常相似,但它们在这种情况下不起作用。

回答by nada

the Simplest way to do this ,is to return it by reference , even if you don't write the '&' symbol , it is automatically returned by reference

最简单的方法是通过引用返回它,即使你不写'&'符号,它也会自动通过引用返回

     void fillarr(int arr[5])
  {
       for(...);

  }

回答by Daniel

int *fillarr(int arr[])

You can still use the result like

您仍然可以使用结果

int *returned_array = fillarr(some_other_array);
if(returned_array[0] == 3)
    do_important_cool_stuff();