简体   繁体   中英

On the difference between pointer and array notation when used with free()

And, welcome back to another fabulous episode of 'Puny Mortals Asking (Potentially) Dumb Questions'.

I wrote a solution to K&R's exercise 1-16 that uses dynamic memory allocation. As part of checking my work, I ran my code through Valgrind. Valgrind reported some memory leaks, though I had a 1:1 malloc:free ratio (which is perplexing). After much fiddling and reading of the GNU c lib manual and the Valgrind user manual , I eventually came to SO. None of the posts that I read really solved my problem, until I noticed some solutions to other unrelated problems that used a notation that may be expressed as the following equivalence:

*(dbl_ptr + i) == dbl_ptr[i]

Where dbl_ptr is of type char** and i is of type int .

In my own, original code, I had been attempting to free() a double pointer in the following manner:

for( i = 0 ; i < count ; ++i ) {
   free( (dbl_ptr + i) );     //Valgrind complains about a leak right here
}

free( dbl_ptr );

But when I recompile the program with these lines instead:

for( i = 0 ; i < count ; ++i ) {
    free( dbl_ptr[i] );    //No more Valgrind complaints
}

free( dbl_ptr );

The program is then reported as being memory safe. Why?

Help me to understand, merciful and benevolent StackOverlords!

CODA:

I recommend reading templatetypedef's answer to this post if you want a solid understanding of what happened here. I don't believe I can write a better explanation of what was going wrong within my own code. It turns out that my error was a combination of misunderstandings about how free() interprets arguments, as well as a fundamental ignorance of the semantics behind the array syntax in C.

Fast and dirty: dbl_ptr[i] implicitly dereferences dbl_ptr + i .

When you call free , the argument should be a pointer to the front of the block of memory you want to deallocate.

So let's suppose you have an array of double* s. That array looks like this:

  dbl_ptr ---> [ 0 ][ 1 ][ 2 ] ... [ k ]
                 |    |    |         |
                 v    v    v         v
                arr  arr  arr       arr

Now, suppose you call

free(dbl_ptr + i);

Graphically, dbl_ptr + i points here (let's pick i = 1 )

  dbl_ptr + i --------+
                      |
                      v
  dbl_ptr ---> [ 0 ][ 1 ][ 2 ] ... [ k ]
                 |    |    |         |
                 v    v    v         v
                arr  arr  arr       arr

Therefore, free interprets this call as if you want to deallocate something from the middle of the block of memory allocated to dbl_ptr , rather than the block of memory pointed at by dbl_ptr[i] . That's a problem, because you can't call free with a pointer to the middle of a block of memory.

On the other hand, calling

free(*(dbl_ptr + i));

or

free(dbl_ptr[i]);

then you're actually saying to deallocate the memory pointed at by the i th element of the array pointed at by dbl_ptr , which is what you want.

You forgot dereference operator in

free( (dbl_ptr + i) );   

It should be

free( *(dbl_ptr + i) ); 

free deallocates the memory pointed to by the pointer argument. In the correct case, free(dbl_ptr[i]) , you look up a pointer from within the array, and then free the memory that it points at. In the incorrect case, free( (dbl_ptr + i)) , the pointer passed to it is actually a pointer to a location within the array, a pointer to the pointer to the memory you want to free.

For an easy way to view the difference, consider the i=0 case. the correct verison is free(dbl_ptr[0]) . The incorrect version is free(dbl_ptr+0) , which is the same as free(dbl_ptr) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM