0

I want to understand the behavior of returning a pointer from a function call. Suppose I have the following simple code:

int main(){
    int i;
    float *ssd;
    ssd = test();

    for (i = 0; i < 3; ++i) {
        printf("%f, ", ssd[i]);
    }
    printf("\n \n");

    memset(ssd, 0.0, 3*sizeof(float));

    for (i = 0; i < 3; ++i) {
        printf("%f, ", ssd[i]);
    }
    printf("\n \n");

}

float *test(){

    float *buff = malloc(3* sizeof(float));
    int i;
    for (i = 0; i < 3; ++i) {
        buff[i] = (float) (6.31 + i);
    }

    free(buff);
    return buff;

}

As you see, I created a temporary buffer buff inside test(). Before I return, I freed buff before return statement. Although I tested it and the results were as expected, I don't understand how test() function could return buff values even though free(buff) is before return buff?

anatolyg
  • 26,506
  • 9
  • 60
  • 134
Adam
  • 117
  • 2
  • 6
  • Related: http://stackoverflow.com/q/6441218/509868 – anatolyg Sep 25 '16 at 19:22
  • 2
    Nothing is impossible in a C program with undefined behavior. – Petr Skocik Sep 25 '16 at 19:23
  • 2
    this is incorrect code. It will lead to corruption. It seems to be a use after free error – Ryan Sep 25 '16 at 19:23
  • And a very good explanation about how the function works http://stackoverflow.com/questions/1119134/how-do-malloc-and-free-work – MrSykkox Sep 25 '16 at 19:27
  • "As expected"??? Popular implementations would normally clobber the first bytes of the freed block, which would be immediately evident through your `printf`s. For example: http://coliru.stacked-crooked.com/a/c6b3ad4d0463affa . What compiler did you use to see it work "as expected"? – AnT stands with Russia Sep 25 '16 at 19:28

3 Answers3

5

After

free(buff);

any further operations which dereference buff are undefined behaviour.

Your function test() allocates a buffer, but then frees it before returning it to main, as ssd.

So when ssd is dereferenced in main, although the pointer still has the same value it was allocated by malloc, it is no longer pointing to memory that you "own". So from then on, anything can happen. Your code may still work, or it may not.

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • Thank you for this explanation. In this case, kindly would you mind explaining the best way to modify this case to avoid ambiguity? Putting `free()` after `return` statement won't work for sure so I don't know what to do next! – Adam Sep 25 '16 at 19:35
  • You can only `free` the memory *after* you have done with using its content. So long as the value of `ssd` in `main` has not been changed, you can `free(ssd)` in `main` when you have no further use for it, because it still has the same pointer value as `buff = malloc(3* sizeof(float))` in the function. – Weather Vane Sep 25 '16 at 19:40
  • @Adam If you want to know "how to fix your code", rather than "what is wrong", ask a separate question. You might want to return an array (using techniques described [here](http://stackoverflow.com/q/11656532/509868) or [here](http://stackoverflow.com/q/8617889/509868)) from your function, and not use `malloc` and `free` at all. – anatolyg Sep 25 '16 at 19:48
0

The memory pointed at originally by buff is not overwritten, thus retaining the values you stored in it.

This is usually what we call "memory containing garbage", but since you initialized it then everything is working.

eavidan
  • 5,324
  • 1
  • 13
  • 16
0

From libc documentation,

Occasionally, free can actually return memory to the operating system and make the process smaller. Usually, all it can do is allow a later call to malloc to reuse the space. In the meantime, the space remains in your program as part of a free-list used internally by malloc.

A call to free doesn't guarantee, that the space is cleaned up. But what is guaranteed (at least by the GNU libc implementation) is that

  1. The space thus freed will be possibly used by the runtime in future calls of any dynamic memory allocation routines.

To prove this i modified your program as follows. It basically adds a for loop in your test() function which just allocates and free's the buff for n times (e.x. 10 times) and prints the contents of the buffer in course. So everytime i run the program, it prints the contents for the first time and when malloc() and free() is called the contents of *buff are cleared and garbage is printed. (tested on a mac, gcc 4.8+). Hope this helps.

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
float *test(){
  float *buff = malloc(3* sizeof(float));
  int i, j;
  for (i = 0; i < 3; ++i) {
    buff[i] = (float) (6.31 + i);
  }
  free(buff);
  int n = 10;
  for(i=0;i<n;i++)
  {
    printf("Iteration %d ", i);
    for (j = 0; j < 3; ++j) {
      printf("%f, ", buff[i]);
    }    
    printf("\n");
    buff = malloc(3 * sizeof(float));
    free(buff);
  }
  return buff;

}
int main(){
  int i;
  float *ssd;
  ssd = test();
  printf("\n \n");
  memset(ssd, 0.0, 3*sizeof(float));
  for (i = 0; i < 3; ++i) {
    printf("%f, ", ssd[i]);
  }
  printf("\n \n");
}
Soundararajan
  • 2,000
  • 21
  • 23