views:

126

answers:

6

I know C pretty well, however I'm confused of how temporary storage works.

Like when a function returns, all the allocation happened inside that function is freed (from the stack or however the implementation decides to do this).

For example:

void f() {
    int a = 5;
} // a's value doesn't exist anymore 

However we can use the return keyword to transfer some data to the outside world:

int f() {
    int a = 5;
    return a;
} // a's value exists because it's transfered to the outside world

Please stop me if any of this is wrong.

Now here's the weird thing, when you do this with arrays, it doesn't work.

int []f() {
    int a[1] = {5};
    return a;
} // a's value doesn't exist. WHY?

I know arrays are only accessible by pointers, and you can't pass arrays around like another data structure without using pointers. Is this the reason you can't return arrays and use them in the outside world? Because they're only accessible by pointers?

I know I could be using dynamic allocation to keep the data to the outside world, but my question is about temporary allocation.

Thanks!

+10  A: 

When you return something, its value is copied. a does not exist outside the function in your second example; it's value does. (It exists as an rvalue.)

In your last example, you implicitly convert the array a to an int*, and that copy is returned. a's lifetime ends, and you're pointing at garbage.

No variable lives outside its scope, ever.

GMan
To go a bit further: as soon as you return from the `*f()` function, the memory address of the first byte of the array allocated in `*f()` is copied into whatever variable you're using. The memory allocated for the array may still contain the correct array data, but as soon as you call another function, it's likely to overwrite that memory. The result is that the memory address, while still a valid address, no longer contains the array you're expecting.
TreDubZedd
@GMan: What if the function returned type array instead of pointer to integer? Then would there still be implicit pointer conversion, or would the array value be copied (the array itself) ?
Luca Matteis
@Luca: It would be a pointer. However, it might work if you stick it in a struct.
Steven Sudit
@Luca: There is no such thing as "type array". Arrays are represented as pointers to the zeroth element in C.
advait
@thethimble: Wrong, `int a[];`, a is an array of integers, no pointers are involved.
Luca Matteis
@thethimble: No, they aren't. Pointers point, arrays are arrays, they aren't the same. @Luca: You cannot return an array because arrays cannot be copied. But yes, if they could be copied you could return an array, and the entire value of the array would be copied and returned.
GMan
@thethimble: Well, ok, but there's a difference between `char b[10]` and `char* pb`. The former is a const pointer to the first character of a buffer that's allocated on the stack. The latter is just a free pointer.
Steven Sudit
You're both right. Arrays are not *identical* to pointers, but pointers are definitely involved.
Steven Sudit
@Steven: That's still wrong. The former has the type `char[10]`; I don't see any pointers there.
GMan
@GMan: You can pass `b` to a function that requires a `char*`. There's a pointer there, even if you don't see it. Remember, `b[2]` is exactly the same as `*(b + 2)`.
Steven Sudit
@Steven: You can pass it because *there's an implicit conversion from array to pointer*, not because it's a pointer. And how elements in an array are accessed has nothing to do with what type an array is.
GMan
@Steven Sudit , no b is an array, its type is char[10] and sizeof b is 10. b however decays to a pointer to the first element in that array if it's used as a value - though it can only be used as an rvalue, not an lvalue.
nos
@nos: Ok, I'll acknowledge that `sizeof` sees `b` as the entire array. Having said that, any attempt to use the array amounts to treating `b` as `pb`, except with a `const` tossed in.
Steven Sudit
@GMan: There is no distinction between an array and a pointer in C. The types of `char b[10]` and `char* pb` are both `char*`. See http://www.fredosaurus.com/notes-cpp/arrayptr/26arraysaspointers.html
advait
quixoto
@thethimble: The fact that the `sizeof` an array is not linked to the `sizeof` a pointer to that element type is one difference. The inability to repoint an array is another.
Steven Sudit
@thethimble: Remove that from your bookmarks, it's wrong. Email the person behind the site and tell them to stop teaching, since they clearly don't know what they're talking about. How about instead of going by some site, you go by the standard? Refer to section 6.7​.5.2. Think I'm wrong, better send an email to the standards committee and tell them they don't know what they're doing. @Everyone: **Arrays are not pointers**, period. Read the standard, stop doing silly tests and drawing conclusions, referring to non-authoritative sources, etc. They aren't the same, learn it.
GMan
+1  A: 

In the first example the data is copied and returned to the calling function, however the second returns a pointer so the pointer is copied and returned, however the data that is pointed to is cleaned up.

kniemczak
A: 

In implementations of C I use (primarily for embedded 8/16-bit microcontrollers), space is allocated for the return value in the stack when the function is called.

Before calling the function, assume the stack is this (the lines could represent various lengths, but are all fixed):

[whatever]
...

When the routine is called (e.g. sometype myFunc(arg1,arg2)), C throws the parameters for the function (arguments and space for the return value, which are all of fixed length) on to the stack, followed by the return address to continue code execution from, and possibly backs up some processor registers.

[myFunc local variables...]
[return address after myFunc is done]
[myFunc argument 1]
[myFunc argument 2]
[myFunc return value]
[whatever]
...

By the time the function fully completes and returns to the code it was called from, all of it's variables have been deallocated off the stack (they might still be there in theory, but there is no guarantee)

In any case, in order to return the array, you would need to allocate space for it somewhere else, then return the address to the 0th element.

Some compilers will store return values in temporary registers of the processor rather than using the stack, but it's rare (only seen it on some AVR compilers).

Nick T
A: 

When you attempt to return a locally allocated array like that, the calling function gets a pointer to where the array used to live on the stack. This can make for some spectacularly gruesome crashes, when later on, something else writes to the array, and clobbers a stack frame .. which may not manifest itself until much later, if the corrupted frame is deep in the calling sequence. The maddening this with debugging this type of error is that real error (returning a local array) can make some other, absolutely perfect function blow up.

JustJeff
A: 

You still return a memory address, you can try to check its value, but the contents its pointing are not valid beyond the scope of function,so dont confuse value with reference.

LostMohican
A: 
int []f() {
    int a[1] = {5};
    return a;
} // a's value doesn't exist. WHY?

First, the compiler wouldn't know what size of array to return. I just got syntax errors when I used your code, but with a typedef I was able to get an error that said that functions can't return arrays, which I knew.

typedef int ia[1];
ia h(void) {
    ia a = 5;
    return a;
}

Secondly, you can't do that anyway. You also can't do

int a[1] = {4};
int b[1];
b = a; // Here both a and b are interpreted as pointer literals or pointer arithmatic

While you don't write it out like that, and the compiler really wouldn't even have to generate any code for it this operation would have to happen semantically for this to be possible so that a new variable name could be used to refer the value that was returned by the function. If you enclosed it in a struct then the compiler would be just fine with copying the data.

Also, outside of the declaration and sizeof statements (and possibly typeof operations if the compiler has that extension) whenever an array name appears in code it is thought of by the compiler as either a pointer literal or as a chunk of pointer arithmetic that results in a pointer. This means that the return statement would end looking like you were returning the wrong type -- a pointer rather than an array.

If you want to know why this can't be done -- it just can't. A compiler could implicitly think about the array as though it were in a struct and make it happen, but that's just not how the C standard says it is to be done.

nategoose
On your last point: It's always possible to wrap arrays in structures and pass them by value that way. Very ugly, but possible.
R..