views:

197

answers:

6

Hi before someone storm "it's simple or duplicate" i swear i've searched but because it's pretty easy i couldn't find it between much more complex ones , unlucky maybe. Anyway my question is very simple , I have normal array int arr[5] that is passed to function fillarr(int arr[]) all i want is fixing the next code:

int fillarr(int arr[])
{ for(...);
return arr ;}
  1. how i can return that array ?.
  2. how will i use it, say i returned a pointer how i'm going to access it ?.

thanks,

A: 
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();
Daniel
Sorry for edits, I was in c# mode for the first attempt.
Daniel
I don't think the 'int [] fillarr ...' is legal. The 'int *fillarr' is what you would use due to array-pointer equivalence.
seand
+4  A: 

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;
}
Brent Nash
To clarify, that "classic C++ statement" is false; arrays are not pointers.
GMan
remember the a[i] == *(a + i) rule
seand
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?
Brent Nash
+1 GMan, it's dangerous to confuse the two.
Carl Norum
Carl Norum
@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.
GMan
A fair argument. There are enough other good answers that I'll just remove this one then.
Brent Nash
I've added a note about an implicit conversion, which I think makes this a solid answer. (Indeed, it's a more elaborate version of my deleted one.)
GMan
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.
Brent Nash
really thanks alot !! didn't expect that much help
ismail marmoush
+5  A: 

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++0x, 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.

Potatoswatter
+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` But you don't even need the typedef if you care to write this: `int ( return a; }`, the example in the question would be: `int ( }`. Simple, isn't it?
David Rodríguez - dribeas
@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 .
Potatoswatter
The answer by [chubsdad](http://stackoverflow.com/questions/3473438/c-return-array-in-a-function/3473570#3473570) 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.
David Rodríguez - dribeas
@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.
Potatoswatter
+2  A: 

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.

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.

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.

something like: 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.

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

Matt H
Use `std::copy` over `memset`, it's safer and easier. (And just as fast if not faster.)
GMan
A: 

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.

TokenMacGuy
David Rodríguez - dribeas
+2  A: 

$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);
}
Chubsdad
Your second function returns a pointer to an `int`, not an array.
GMan