Return array in a function

975,744

Solution 1

In this case, your array variable arr can 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:

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

Solution 2

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.

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:

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

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

Solution 3

In C++11, you can return 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;
}

Solution 4

$8.3.5/8 states-

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

Solution 5

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::vector takes 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)

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 *myArr works identically to using a plain vanilla vector. This example also modifies the parameter list by adding the const keyword. 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.

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

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.

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

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.

Share:
975,744

Related videos on Youtube

Ismail Marmoush
Author by

Ismail Marmoush

You can check my website and blog https://marmoush.com

Updated on September 02, 2020

Comments

  • Ismail Marmoush
    Ismail Marmoush over 3 years

    I have an array int arr[5] that is passed to a function 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?
    • BuggerMe
      BuggerMe over 13 years
      strictly speaking in this context you don't need to return the array since the array is passed by reference so any changes to elements inside 'arr' will be seen outside the function.
    • seand
      seand over 13 years
      returning the array is convenient for chaining functions.
    • detly
      detly over 13 years
      As long as you're not making the mistake of creating an array on the stack and returning a pointer to it.
    • Ismail Marmoush
      Ismail Marmoush over 13 years
      @buggerme yes you're right , but you can consider the array returned arr2 , totally another array , my mistake sry :)
    • GManNickG
      GManNickG over 13 years
      @ismail: It cannot return a new array, unless that array was dynamically allocated. And if that's the case, use std::vector.
    • David Rodríguez - dribeas
      David Rodríguez - dribeas over 13 years
      @BuggerMe: Arrays are not passed by reference (unless you request it with a much funnier syntax), in the code, the array decays into a pointer to the first element and that is passed to the function. The 5 in the function signature is discarded by the compiler.
    • BuggerMe
      BuggerMe over 13 years
      @David in the essence of C, when you pass arrays you always pass them by reference allowing the contents of the array to be changed. What I think you mean is passing the array ptr by reference allowing changing the ptr.
    • David Rodríguez - dribeas
      David Rodríguez - dribeas over 13 years
      @BuggerMe: Not, not really. I was being precise as I have grown used to people misunderstanding the semantics of the pass-by-value syntax for arrays in C++. Passing an array by reference is: void foo( int (&array)[5] ); (array of 5 ints by reference). When you pass by reference what you get inside the function is a reference to the actual type. On the other hand void foo( int array[5] ) is translated by the compiler to void foo(int*) during the function definition. Calling foo( myarray ) produces the decay of the array to a pointer to the first element.
    • BuggerMe
      BuggerMe over 13 years
      @David sorry but a c-array is never passed by value, the only way you can do that (in C) is by putting into a struct. I agree that the pointer to the array is by value but the array itself is passed by value.
    • David Rodríguez - dribeas
      David Rodríguez - dribeas over 13 years
      @BuggerMe: Maybe I was not clear enough, I know that in C/C++, arrays are never actually passed by value. If you carefully read the comment you will see "semantics of the pass-by-value syntax". That is, the syntax is the that of pass-by-value, but the semantics are not that of pass-by-value, the language transforms that syntax into the known decayed array. This c++ discussion is extending well beyond the intended use of the comments. Google for array reference decay c++. One of the first results is this.
  • GManNickG
    GManNickG over 13 years
    To clarify, that "classic C++ statement" is false; arrays are not pointers.
  • seand
    seand over 13 years
    I don't think the 'int [] fillarr ...' is legal. The 'int *fillarr' is what you would use due to array-pointer equivalence.
  • seand
    seand over 13 years
    remember the a[i] == *(a + i) rule
  • Brent Writes Code
    Brent Writes Code over 13 years
    How should I rephrase it? Isn't the array variable really just a constant pointer to the beginning of the memory block allocated for the array? And then the [] just does pointer arithmetic based off of that initial address to locate further elements in the array?
  • Carl Norum
    Carl Norum over 13 years
    @Brent Nash, no. an array is an array. A pointer to the start of the array is a pointer. It just happens that the compiler has some syntactic sugar that does the translation for you in some situations. array and &array are interchangeable in a lot of cases.
  • GManNickG
    GManNickG over 13 years
    @Brent: No. An array is it's own type, it's not a special kind of pointer. The type of a in int a[10] is int[10]. What you can say is arrays "decay" into pointers to their first element. (This is an implicit array-to-pointer conversion.) Then your answer would go along the lines mine does. If you edit your answer to differentiate between arrays, array-to-pointer conversion, and pointers, I'll delete my answer since they would have the same core information and you were first.
  • Brent Writes Code
    Brent Writes Code over 13 years
    I'll leave this answer here if for no other reason than I think this comment chain has some really valuable comments. I think @Potatoswatter should probably get your vote for correct answer though. Thanks for the good comments, all.
  • GManNickG
    GManNickG over 13 years
    Use std::copy over memset, it's safer and easier. (And just as fast if not faster.)
  • GManNickG
    GManNickG over 13 years
    Your second function returns a pointer to an int, not an array.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 13 years
    +1 for giving an example of how an array can be passed by reference. But you are wrong in that you cannot return an array by reference. The simplest syntax to achieve it is by using a typedef: typedef int array[5]; array& foo(); But you don't even need the typedef if you care to write this: int (&foo())[5] { static int a[5] = {}; return a; }, the example in the question would be: int (&foo( int (&a)[5] ))[5] { return a; }. Simple, isn't it?
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 13 years
    The syntax is wrong, the & symbol must appear after the type: void fillarr(std::vector<int> & arr)
  • Potatoswatter
    Potatoswatter over 13 years
    @David: thanks, I got the misimpression from the Comeau message error: function returning array is not allowed which occurs if you leave out the outer parens in the non-typedef syntax. Fortunately, today I reviewed the right-left rule for another question and managed to construct the right thing… after seeing you say it's possible… before seeing that you gave the code :vP .
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 13 years
    The answer by chubsdad has the correct quote from the standard: you cannot return an array, but you can return a reference or pointer to an array. Arrays are non-copyable (as a type) and as such they cannot be returned --which would imply a copy-- and when that syntax is present the compiler will convert the argument into a pointer.
  • Potatoswatter
    Potatoswatter over 13 years
    @David: So it does. This page is getting to be bizarrely long. Never have so many people voluntarily written so many trivial functions returning an array in one place.
  • cubuspl42
    cubuspl42 almost 10 years
    Quoting OP: (...) you can consider the array returned arr2, totally another array (...)
  • MickyD
    MickyD almost 9 years
  • Amir
    Amir about 8 years
    @seand remember the a[i] == *(a + sizeof(a)*i) rule
  • magras
    magras over 7 years
    @Amir, question has C++ tag, in which your expression evaluates to false.
  • Gabriel Staples
    Gabriel Staples over 6 years
    @BrentNash, your final example here begs the question, "why not just keep using y instead of a?" I think it would be helpful for you to add that when you modify an array inside of a function--the array having been passed into the function, the modifications are retained outside the function after it returns/exits, therefore, cout << y[0] << endl; would print the exact same thing in this case as cout << a[0] << endl;, again, because the y array was modified in place by the function.
  • Gabriel Staples
    Gabriel Staples over 6 years
    I just edited the answer to include my comments. I hope that's ok. I think this is important info to make sure people understand.
  • Gabriel Staples
    Gabriel Staples over 6 years
    Also, I would just use void fillarr(int arr[]) if it was me writing this, instead of int* fillarr(int arr[]). Then, after I fill the array, if I really want a separate pointer variable, I'd use int* a = y. This is simpler and more clear than having fillarr return something I think.
  • Adrian
    Adrian over 6 years
    This is not C++
  • hellow
    hellow over 5 years
    Please use proper english wording (you'll instead of u'll) and omit empty phrases like "buddy".
  • hellow
    hellow over 5 years
    Also: "then actually it is passed as a reference" is wrong. The variable y itself is passed as a copy of itself, but because it's a pointer, you will directly operate on the array. Please edit your answer.
  • hellow
    hellow over 5 years
    stackoverflow.com/questions/5573310/… TL;DR "Thus, the two forms are identical."
  • hellow
    hellow over 5 years
    Yes it is technically an array, you're right, but what is copied is a pointer to the array, not the array itself.
  • melpomene
    melpomene over 5 years
    @Amir There is no such rule. a[i] literally means *(a + i); your code is wrong.
  • Amir
    Amir over 5 years
    @melpomene yes i am developing c++ now i found that you are right. suggest me to remove my comment or not?
  • ajaysinghnegi
    ajaysinghnegi over 5 years
    @magras I didn't get your comment question has C++ tag, in which your expression evaluates to false. How is it wrong in cpp? Kindly explain !! Also, what will sizeof(a) return in *(a + sizeof(a)*i) in @Amir comment?
  • ajaysinghnegi
    ajaysinghnegi over 5 years
    @Potatoswatter I am new to cpp, Can you explain the 2nd code snippet in detail? I am not able to break it into parts for the sake of understanding.
  • Potatoswatter
    Potatoswatter over 5 years
    @AjaySinghNegi It’s the same as typedef int arri5[5]; arri5 &fillarr( arri5 & arr ) { return arr; }. Better to use typedef here.
  • magras
    magras over 5 years
    @AjaySinghNegi, sizeof(a) will return size of pointer type, not the size of element. More than that in C++ pointer arithmetic already accommodates to pointed element size, so his rule could be corrected in two ways: a[i] == *(a + i) or if you want to do it manually (and probably trigger UB) a[i] == *(int*)((char*)a + sizeof(*a) * i). Probably there is a difference between plain C and C++, which caused this confusion, but I don't know plain C.
  • ajaysinghnegi
    ajaysinghnegi over 5 years
    @magras Why does this gives the size of array, then? I mean, Shouldn't sizeof(array) give the total size of the array(sizeof(int) * total_no_of_elements)? What is the difference b/w what you are saying and the one I've linked?
  • magras
    magras over 5 years
    @AjaySinghNegi, there is a difference between array and pointer types. In this answer a declared as a pointer: int *a = fillarr(y);. In C++ arrays doesn't store their size at run time. So information about size available only at compile time as a type. It isn't very easy to pass array types in and out of functions so, there are more modern ways to achieve the same goal. If you want to keep and work with array size at compile time, it's better to use std::array. Or if you want to have runtime sized array you can use std::vector specifying size at construct time.
  • Potatoswatter
    Potatoswatter about 5 years
    @ilw for example, foo()[3] = 42;.
  • Potatoswatter
    Potatoswatter about 5 years
    @ilw Just use auto &&.
  • Potatoswatter
    Potatoswatter about 5 years
    @ilw If you need to express something more specific, then it depends what. In any case, with a C++98 compiler, returning the pointer type is probably more portable, or defensive against compiler bugs.
  • Dan
    Dan over 4 years
    Late to the discussion but I don't see it here and someone slap me if I'm missing it....what would be the advantage to returning an array or pointer to the array if the array memory location is being updated by the function to begin with? Or maybe there's not one?
  • Dan
    Dan over 4 years
    again, why return the type when the actual array is updated inside the function? Is it a matter of best practice?