NO

Author Topic: is it right in c?  (Read 8725 times)

vedro-compota

  • Guest
is it right in c?
« on: May 25, 2012, 12:56:55 AM »
Hi there)
C-athlets, please tell me -  is this warning correct in C (originally it's from C++):
Quote
warning C4172: returning address of local variable or temporary
The error is caused because the address of the variable you are returning falls out of scope as soon as the function exits, so although you can return the address, as soon as the function exits the address is no longer valid, and your character buffer will be deleted.

.
source = http://social.msdn.microsoft.com/forums/en-US/Vsexpressvc/thread/6547d2bf-2884-4ad2-b600-adaab0fa1031

CommonTater

  • Guest
Re: is it right in c?
« Reply #1 on: May 25, 2012, 04:41:41 AM »
Yes, this is real in C too... 

For example:
Code: [Select]
int* test (int a, int b)
  {  int c;
    c = a + b;
    return &c; }
The local variable c is destroyed as the stack is restored when the function exits.  You thus end up returning an invalid pointer to your function's parent... and land yourself in the world of "Undefined Behaviour" where anything can happen.

The issue only gets worse for strings and arrays...

I'm attaching a small project you can play with to see why you should never return the address of an array... Try it with and without the static keyword, you'll get the idea...
 

vedro-compota

  • Guest
Re: is it right in c?
« Reply #2 on: May 25, 2012, 07:24:53 AM »
thank you ,CommonTater ) I'll try

vedro-compota

  • Guest
Re: is it right in c?
« Reply #3 on: May 27, 2012, 08:32:36 PM »
if use it without "static" - will get  delirious values instead of a1

Code: [Select]
#include <stdio.h>

int* MyFunction(int a, int b, int c)
  {  static int array[3];
     array[0] = a;
     array[1] = b;
     array[2] = c;
     return array;  } // return a pointer.

int*  func2(int a, int b )
  {  static int array[3];
     array[0] = 0;
     array[1] = 0;
        return array;  } // return a pointer.

int main (void)
  { int *a1, *a2;  // int pointers

    printf("calling a1 = MyFunction(10,20,30);\t");
    a1 = MyFunction(10,20,30);
    printf("a1 has %d %d %d\n",a1[0],a1[1],a1[2]);

    printf("calling a2 = MyFunction(100,200,300);\t");
    a2 = MyFunction(100,200,300);
    printf("a2 has %d %d %d\n",a2[0],a2[1],a2[2]);

    printf("\nLooks good, except...\t");
    printf("a1 now has %d %d %d\n",a1[0],a1[1],a1[2]);

    printf("calling a2 = MyFunction(10,200,300);\t");
    a2 = MyFunction(10,200,300);
    printf("a1 has %d %d %d\n",a1[0],a1[1],a1[2]);


//    getchar();
    return 0;

}
in this connection - pair of questions:
1) does this mean that i can't  for example pass in function file pointer FILE* (only one parameter) and return char**  - array of strings  which definition and memory allocation take place in body of my function (for multiple times universal using - without rewriting of returning parameter memory)?

2) why in this case I got "good" array in first printf() (after exit from MyFunction() ) but don't in second printing a1 by printf (also after exit from  MyFunction()):
Code: [Select]

#include <stdio.h>

int* MyFunction(int a, int b, int c)
  {    int array[3];
     array[0] = a;
     array[1] = b;
     array[2] = c;
     return array;  } // return a pointer.

int main (void)
  { int *a1, *a2;  // int pointers

    printf("calling a1 = MyFunction(10,20,30);\t");
    a1 = MyFunction(10,20,30); // works and exits from "local" memory scope

    printf("a1 has %d %d %d\n",a1[0],a1[1],a1[2]);// first

    printf("\nLooks good, except...\t");
    printf("a1 now has %d %d %d\n",a1[0],a1[1],a1[2]); // second

//    getchar();
    return 0;
}
as I understand  -  C shouldn't give adequate array values outside of  MyFunction (if we don't use static) . What is  wrong in my
reasoning?


« Last Edit: May 27, 2012, 10:02:42 PM by vedro-compota »

CommonTater

  • Guest
Re: is it right in c?
« Reply #4 on: May 27, 2012, 10:15:31 PM »
The only way you can return an array of *any kind* from a function is to create the array with malloc() so that it's not created on the program's stack.
 
When you do this...
Code: [Select]
int* myfunction(int a, int b)
  { int array[10][10];
   
     // whatever
 
    return array; }
The following (simplified) sequence of events takes place...
1) The return address is pushed onto the stack
2) The function's parameters are pushed onto the stack
3) The array is created on the stack
4) Whatever processing you do takes place
5) When the return call is encountered...
.....A) the value to be returned is placed in a CPU register
.....B) the stack is cleared all the way back to the return address.
6) The program continues from the return address onward.
 
Step 5B is what kills it... since it will also destroy your array.
 
Adding the static keyword doesn't work either. In this case the array is not destroyed but it will hold whatever values came from the last call. Since what you are actually returning is a pointer to the array, if you have multiple calls to the same function, you end up with all your arrays connected to the same address, effectively cross-connecting them which will create lots and lots of problems with magic numbers as my little demo program illustrates...
 
If you create the array with malloc() it is *not* created on the stack and thus is not destroyed when your function returns.  However; you will have to use free() to release the memory as soon as you're done with it, or your program will "leak" memory creating problems in the system memory pool.
 
It is often your best bet to create the array outside of your function (in it's parent) and pass in a pointer to the array that your function can use...  That way the array will persist until the parent function exits and you don't need to free() it.
 
« Last Edit: May 27, 2012, 10:18:24 PM by CommonTater »

vedro-compota

  • Guest
Re: is it right in c?
« Reply #5 on: May 27, 2012, 10:38:29 PM »
Quote
It is often your best bet to create the array outside of your function (in it's parent) and pass in a pointer to the array that your function can use...  That way the array will persist until the parent function exits and you don't need to free() it.
"create" - does it mean only define? like this -
char ** myarr;
fillarr( myarr , filepointer) ; // pass into custom func
or you mean to allocate memory in parante's stack - just as in your example -
Code: [Select]
int array[10][10];
CommonTater , please tell something about my previous second question -   about two printf() -  or  may be it's only example of A and B variants?

CommonTater

  • Guest
Re: is it right in c?
« Reply #6 on: May 27, 2012, 11:08:44 PM »
Quote
It is often your best bet to create the array outside of your function (in it's parent) and pass in a pointer to the array that your function can use...  That way the array will persist until the parent function exits and you don't need to free() it.
"create" - does it mean only define? like this -
char ** myarr;
fillarr( myarr , filepointer) ; // pass into custom func
or you mean to allocate memory in parante's stack - just as in your example -
Code: [Select]
int array[10][10];

Ok... as briefly as I can...
 
You first have to appreciate there are two kinds of storage at work...
.... The stack, which is a small chunk of memory for programs to store local and temporary variables.
.... The Heap, which is chunks of memory drawn from the system's free pool for use by programs.
 
1) When you do .... int array[100]; ... you are using up 400 bytes (100 * sizeof(int)) on the program's stack.  This is temporary memory that is reset when your function exits... all variables in the function's scope... { to } ...are destroyed when the function returns.
 
2) When you do... int * array = malloc(100 * sizeof(int)); ... you are storing only 4 bytes on the stack (the pointer to array) and the 400 bytes for the data are allocated on the program's Heap memory **not** on the stack.  Thus when the function exits, this memory is still reserved by your program... you can use it outside the function's scope.  All you need is the pointer... which you *can* return from a function successfully. The catch is that you have to free() it when you're done with it or your program will needlessly tie up system memory.
 
Each of these strategies has it's uses.  If you need a smallish array to be used only inside a given scope you can create it on the stack with no worries. ("Smallish" meaning a few hundred bytes).  However, if your array is large and/or you wish to access it outside the current scope you must create it on the program's heap, which has access to a lot more memory (typically several gigabytes)
 
My example of creating a large number of structs in memory as an array of structs, is a good use of the Heap... if nUsers was more than about 5,000... it would crash the program with a stack overflow and you can't return the array from the function to use it anywhere else... with malloc you can return the pointer to the allocated memory and treat it as an array of nUsers ...
 
Quote
CommonTater , please tell something about my previous second question -   about two printf() -  or  may be it's only example of A and B variants?

That's because C --the language itself-- does not know how to return an array.  It doesn't actually know how big it is, outside of the scope ... { to } ...that it's created in so what it tries to do is return a pointer to the array... which gets destroyed when the function exits.  That either of your printf() in that case worked is pure dumb luck.  For the first one the values were likely still sitting in memory but for the second one it's very likely they'd been overwritten, since that chunk of memory is no longer reserved and has been reused.
 
This is what we call "undefined behaviour"... that is, anything can happen.  Trust me... it only appeared to work. 
 
 

vedro-compota

  • Guest
Re: is it right in c?
« Reply #7 on: May 28, 2012, 06:19:39 AM »
Code: [Select]
That either of your printf() in that case worked is pure dumb luck.ok. I just wanted to hear about it! the first one is also "undefined behaviour"....ok. know understand.

Thanks for so good explanation.
 

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Re: is it right in c?
« Reply #8 on: May 28, 2012, 09:33:03 AM »
Stack locations are reused from the next subroutine each time you return from the current one.
Local variables defined on the stack from a caller (the routine that called your actual one) are valid in called routines, but are not after you return from code that creates the variables.
A stack is a stack while you push something in it the elements inside are valid, when you pop them (i.e. returning from a call) they are no more valid. If they have not been overwritten yet when you read them you can get a valid data, but you are not guarantee that they will be still valid on next read: this is your lottery.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

vedro-compota

  • Guest
Re: is it right in c?
« Reply #9 on: May 28, 2012, 02:36:57 PM »
ok. I've understood "why stack is dangerous" , but I don't understand this - 
if I only define pointer in local function and than allocatate memory  for it in heap - e.g. by calloc()  - could I return this pointer into other scopes and use data getting by it?
as I see - yes, because pointer returning by this function could be using multiple times  -
Code: [Select]
/*преобразуем содержимое ранее считанное из файла в массив незакоментированных строк */
char** tfile_to_tdarr(FILE *fp) /*text file to two-dimensional array of char = c*/
 { /* */
     char** arr;
   
.............................
...........................
..........
arr = (char**)calloc(k+1,sizeof(char*)); /* EOF*/
...........................
..................
............................
.......................
.............................
........
return arr;
 }
   

CommonTater

  • Guest
Re: is it right in c?
« Reply #10 on: May 28, 2012, 03:04:01 PM »
ok. I've understood "why stack is dangerous" , but I don't understand this - 
if I only define pointer in local function and than allocatate memory  for it in heap - e.g. by calloc()  - could I return this pointer into other scopes and use data getting by it?
as I see - yes, because pointer returning by this function could be using multiple times  -
Code: [Select]
/*преобразуем содержимое ранее считанное из файла в массив незакоментированных строк */
char** tfile_to_tdarr(FILE *fp) /*text file to two-dimensional array of char = c*/
 { /* */
     char** arr;
   
.............................
...........................
..........
arr = (char**)calloc(k+1,sizeof(char*)); /* EOF*/
...........................
..................
............................
.......................
.............................
........
return arr;
 }
   

It's not that "stack is dangerous" it's that it's *local* to the current function.  When used correctly it's very much your friend since it makes for very speedy functions.

Again you need to understand how C works... C returns a *value*, in this case the value of a pointer, stored in a CPU register.  The actual stack variable can be destroyed and the value in the register remains untouched.  Since malloc() and calloc() create reserved memory on the heap that memory is not destroyed when the stack is cleared... so yes you can safely return a pointer to allocated memory from a function.  In fact it's the only safe way to do this.



vedro-compota

  • Guest
Re: is it right in c?
« Reply #11 on: May 28, 2012, 03:13:19 PM »
Hurrah!!!!)) not all my knowledge are wrong))
thank you , CommonTater! thank you)

CommonTater

  • Guest
Re: is it right in c?
« Reply #12 on: May 28, 2012, 03:30:58 PM »
Hurrah!!!!)) not all my knowledge are wrong))
thank you , CommonTater! thank you)

Just most of it... 

Yeah a little "zinger" to make a point.  My friend you really do need to get into the books and make a deliberate study of programming in C...
 
I'm just waiting till you figure out your calloc() call isn't working...


 
« Last Edit: May 28, 2012, 03:32:41 PM by CommonTater »