0

I realize that variables declared in a function call are pushed onto the stack and once the function reaches its end the local variables declared onto the stack are popped off and go into lala land.

The thing I don't undertstand is that if I declare a pointer in a function I can return the pointer with no compiler complaint, whereas with an array it does give me a warning.

Here is an example of what I'm referring to:

char * getStringArray();
char * getStringPointer();

int main(){
        char * temp1 = getStringPointer();
        char * temp2 = getStringArray();
        return 0;
}
char * getStringPointer(){
        char * retString = "Fred";
        return retString;
}
char * getStringArray(){
        char retString[5] = {'F', 'r','e','d','\0'};
        return retString;
}

During compilation it throws a "returning address of local variable" warning about getStringArray(). What confuses me is that I've been told that referencing an array solely by its name(like retString, no[])refers to its address in memory, acting like a pointer.

Arrays can be handy and there are many times I would like to use an array in a function and return it, is there any quick way around this?

As I referred to in a comment, will this work? I'm guessing malloc allocates to heap but so its fine. I'm still a little unclear of the difference between static data and data on the heap.

char * getStringPointer(){
        char * retString = (char *)malloc(sizeof(char)*4+1);
        *(retString+0) = 'F';
        *(retString+1) = 'r';
        *(retString+2) = 'e';
        *(retString+3) = 'd';
        *(retString+4) = '\0';
        return retString;
}
Khaines0625
  • 387
  • 1
  • 5
  • 17
  • what is lala land? Pointers are just pointers that point to an address in memory. What warnings is being thrown regarding `getStringArray()`, its not surprising though, you have declared and defined a array of five characters and return that back, but the definition does not match the prototype which is declared and defined as pointer to char. – t0mm13b May 19 '16 at 22:54
  • @t0mm13b An array of `char` decays to a pointer to `char`. So, language-lawyer-wise, there's nothing *illegal* about `getStringArray`. It is, of course, pointing to memory that goes out scope, though. – jonspaceharper May 19 '16 at 22:58
  • You should never depend on a C compiler to warn of possible errors. That is what `lint` and similar utilities are for and even they don't catch everything problematic. – Richard Chambers May 19 '16 at 23:03
  • @JonHarper Of course I know that *the first element of the array decays into a pointer of type T*! But, look at the definition of both the variable and prototype. That would certainly raise a warning there. The variable is array, the prototype is function returning pointer to type `char`. – t0mm13b May 19 '16 at 23:08
  • What compiler are you using and what is the warning level? – Richard Chambers May 19 '16 at 23:11

4 Answers4

3

"Fred" is an unnamed global, so it's not going away, and returning a pointer to it is safe. But getStringArray() is de-allocating everything that pointer points to. That's the difference.

donjuedo
  • 2,475
  • 18
  • 28
  • @Khaines0625 you can, of course, have a global array as well... its down to storage modifiers that can affect the return value. – t0mm13b May 19 '16 at 23:15
3

getStringPointer is allocating a pointer on the stack, then returning that pointer, which points to the string 'Fred\0' somewhere in your executable (not on the stack). getStringArray allocates space for 5 chars on the stack, assigns them 'F' 'r' 'e' 'd' and '\0', then returns a pointer to the address of 'F', which is on the stack (and therefore invalid after the function returns).

There are two ways round it: either you can malloc some space for your array on the heap, or you can make a struct that contains an appropriately sized array and return that. You can return numerical, pointer and struct types from functions, but not arrays.

patstew
  • 1,806
  • 17
  • 21
  • Once `getStringArray` returns, `retString` is popped off the stack and accessing the returned pointer would be UB – yano May 19 '16 at 23:06
  • Indeed. I think the OP understands that pointers to the stack are no longer valid after a function returns (from the 1st para in the question), but they don't understand that the string constant `"Fred"` is not on the stack. – patstew May 19 '16 at 23:10
  • Ok so what I'm gathering from the multiple answers and comments is that declaring a pointer within a function is global, but declaring an Array is not. It is interesting that you do no suggest making the array static. Any reason for that? – Khaines0625 May 19 '16 at 23:15
  • Also, when I think "Global variable" I think not only of a variable allocated to the heap but a variable that has a scope greater than just the function it is declared in. By declaring the Array static would I be able to access the data outside of the function(not counting the returned value where the function was originally called.) by name? – Khaines0625 May 19 '16 at 23:18
  • The pointer itself isn't global, it's the string constant. The memory address where "Fred" resides is written when you program is loaded, and can be accessed at any time (though the only place you can get a pointer to it is on the line it's declared on). If you declare a variable static in a function its name is only available in that function. – patstew May 19 '16 at 23:21
  • The important thing to think about is what memory the line is actually allocating on the stack. `char retString[5]` allocates 5 bytes on the stack. The address is not stored anywhere, until `return retstring` returns the address of the first byte. `char* retstring` allocates space for an 8-byte pointer and `return retstring` returns a copy of that allocation. (Assuming 64-bit pointers) – patstew May 19 '16 at 23:25
  • @Khaines0625 Declaring a pointer within a function absolutely is not global. As Patrick said the scope of the pointer is within the function only. In your sample code `"Fred"` is a string literal that is stored elsewhere in the executable with static duration. Within `getStringPointer`, it's only the pointer that is stored on the stack. Once the function returns, `retString` is popped off the stack, but `"Fred"` remains in memory. – yano May 19 '16 at 23:26
  • @Khaines0625 Do not try to use the return value of `getStringArray`, that is UB since the array is popped off the stack once the function returns. For a colorful explanation of this, see Eric Lippert's answer here (it's c++ but the concept is the same): http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794 – yano May 19 '16 at 23:30
  • I'm still a little unsure of the difference between static data and data on the heap. I added code in the original question so you can read it. – Khaines0625 May 19 '16 at 23:37
  • That's fine. Anything on the heap stays there until you `free` it. To greatly simplify it, when your program is loaded it will copy all the code to a location in memory, all the constant and static data to another location, it'll grab another to use as the stack, and another for the heap. "Fred" is in the constant data section, and will remain there for the lifetime of the program. (Ironically {'F', 'r', 'e', 'd', '\0'} *might* end up there too in reality, the data has to exist somewhere, but you can't take a pointer to an initialiser list) – patstew May 19 '16 at 23:49
  • The program will use and reuse the stack each time you call a function, so pointers to the stack are only valid for as long as the data pointed to is in scope (the function hasn't returned). The heap/static sections will only by written by you, so anything there is safe to point to. At least until you screw it up yourself. – patstew May 19 '16 at 23:50
  • Thank you for all the info. – Khaines0625 May 20 '16 at 00:02
0

The array is unambiguously a pointer to stack-local memory so the compiler is certain that's what you are leaking, so that's why you get the warning.

Just make the array static and both the warning and the lifetime restriction will vanish.

Now, the pointer could be pointing to something static or in the heap (in your case it actually was static) and so it's not obvious from looking at just that one expression whether you are leaking something stack-local or not. That's why you don't get the warning when you return a pointer, regardless of whether it's a safe return or not. Again, the static storage class will save you. Your string constant happens to be static, so that works fine.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • You said "static or in the heap." I was under the impression that static variables are stored in the heap? Is that not true? – Khaines0625 May 19 '16 at 23:27
  • It's not true. Static data is stored in a segment of the executable that exists in the actual image file itself. In C, the heap is strictly used for objects allocated via `malloc(3).` For one thing, all initialization would then have to be dynamic, and therefore slow. – DigitalRoss May 20 '16 at 17:22
0

By adding a static modifier to the variable retString and returning back a pointer will resolve the issue.

char * getStringArray(){
        static char retString[5] = {'F', 'r','e','d','\0'};
        return retString;
}

That will return the appropriate value.

Notice how its still treated as a pointer, because the first element of the array is decayed into a pointer of type T, in this case, char. This will make the compiler happy as it matches the prototype declaration of the function itself as defined at the start of the source.

See ideone sample

t0mm13b
  • 34,087
  • 8
  • 78
  • 110