Thread: Accessing a pointer after it has been freed

  1. #1
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057

    Accessing a pointer after it has been freed

    As part of codeform, I have a dynamically allocated array of pointers. Some of the pointers point to the same location in memory; but if they do, they will be consecutive. So when I go to free the array, I use
    Code:
    for(x = 0; x < size; x ++) {
        if(!x || array[x] == array[x-1]) free(array[x]);
    }
    
    free(array);
    However, I have been reading a C book*, and it seems to indicate that you can't do anything with "invalid pointers" (which can be formed by freeing the memory that a pointer pointed to), including compare them with anything else. This seems silly to me; I'm not dereferencing the pointer or anything. But still, I'm wondering if the above code is valid or not. If it isn't, that won't matter, for I could set duplicate pointers to NULL beforehand:
    Code:
    if(size) for(x = 0; x < size-1; x ++) {
        if(array[x] == array[x+1]) array[x] = 0;
    }
    
    for(x = 0; x < size; x ++) {
        free(array[x]);
    }
    * C: A reference manual, fifth editon, page 139.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You'll need to be sure you set them to NULL if you're freeing them. You really aren't supposed to do anything to a pointer which is invalid, other than assign it something valid (or NULL). Anything other than that is a potentially BadThing(TM). So really you can do something with an invalid pointer, in that you can reassign it a new value.


    Quzah.
    Hope is the first step on the road to disappointment.

  3. #3
    Registered User
    Join Date
    Jun 2004
    Posts
    201
    Quote Originally Posted by dwks
    However, I have been reading a C book*, and it seems to indicate that you can't do anything with "invalid pointers" (which can be formed by freeing the memory that a pointer pointed to), including compare them with anything else.
    That is correct in a pedantic kind of C standard way.

    This code does trigger undefined behaviour according to the standard.

    Code:
    char *p; /* not initialized */
    char *q = malloc(10);
    
    if (p>q) {... }
    This is UB because the value in the uninitialized pointer might trigger a trap on some systems. The thing is there are currently no known systems where this actually does go wrong. So it's kinda silly to say its bad code

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    free() does not change the value of the pointer, as far as the caller is concerned. So, AFAIK, the comparison is valid (even if not a particularly good idea). Dereferencing the pointers after the call to free() would yield undefined behaviour.

    There is also the problem that the code
    Code:
    for(x = 0; x < size; x ++) {
        if(!x || array[x] == array[x-1]) free(array[x]);
    }
    would free some pointers twice if three or more consecutive elements of array contain the same value. And that is undefined behaviour.

  5. #5
    Registered User
    Join Date
    Jun 2004
    Posts
    201
    Quote Originally Posted by grumpy
    free() does not change the value of the pointer, as far as the caller is concerned. So, AFAIK, the comparison is valid (even if not a particularly good idea). Dereferencing the pointers after the call to free() would yield undefined behaviour.

    There is also the problem that the code
    Code:
    for(x = 0; x < size; x ++) {
        if(!x || array[x] == array[x-1]) free(array[x]);
    }
    would free some pointers twice if three or more consecutive elements of array contain the same value. And that is undefined behaviour.
    dereferencing is clearly UB, but even comparing the value of a pointer that is freed or uninitialized is UB.

    There's also another bug in the code for when x equals zero.

  6. #6
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Laserve
    dereferencing is clearly UB, but even comparing the value of a pointer that is freed or uninitialized is UB.
    As I said, I'm not 100% sure on that. Comparing uninitialised pointers, I agree, yields UB (simply fetching the value of any uninitialised variable does that, let alone comparing it). But I failed to find anything in the C standard which supports the conclusion that the comparison in this;
    Code:
        /*  assume x is a pointer, and it's value was returned by malloc()/calloc()/realloc() */
        free(x);
        if (x > some_pointer_value)
           do_whatever();
    yields UB. Would you provide a reference into the standard please?

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Just rewrite the loop as this and stop worrying about the problem
    Code:
    for(x = 0; x < size; x ++) {
        if(x == (size - 1) || array[x] != array[x+1]) free(array[x]);
    }
    This way, you only free the last pointer of each sequence.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    free() does not change the value of the pointer, as far as the caller is concerned. So, AFAIK, the comparison is valid (even if not a particularly good idea). Dereferencing the pointers after the call to free() would yield undefined behaviour.
    That's what I thought, too; but it seemed to indicate otherwise.

    There is also the problem that the code
    Code:
    for(x = 0; x < size; x ++) {
        if(!x || array[x] == array[x-1]) free(array[x]);
    }
    would free some pointers twice if three or more consecutive elements of array contain the same value. And that is undefined behaviour.
    Yes, I copied it from memory, obviously incorrectly.

    Code:
    char *p; /* not initialized */
    char *q = malloc(10);
    
    if (p>q) {... }
    This is UB because the value in the uninitialized pointer might trigger a trap on some systems. The thing is there are currently no known systems where this actually does go wrong. So it's kinda silly to say its bad code
    My code wasn't like that, because the variable did have a value. It was more like this:
    Code:
    char *p = malloc(10), *q;
    
    if(something) q = p;
    else q = malloc(10);
    
    free(p);
    if(p != q) free(q);
    Just rewrite the loop as this and stop worrying about the problem
    Okay, good idea. I was just curious.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    Registered User
    Join Date
    Jun 2004
    Posts
    201
    Quote Originally Posted by grumpy
    As I said, I'm not 100% sure on that. Comparing uninitialised pointers, I agree, yields UB (simply fetching the value of any uninitialised variable does that, let alone comparing it). But I failed to find anything in the C standard which supports the conclusion that the comparison in this;
    Code:
        /*  assume x is a pointer, and it's value was returned by malloc()/calloc()/realloc() */
        free(x);
        if (x > some_pointer_value)
           do_whatever();
    yields UB. Would you provide a reference into the standard please?
    yeah it might be guaranteed to work since if the pointer contains a valid value before the free it will still contain a valid value after the free. I was only thinking about the uninitialised variable case. I'll try and find this I I remember when I get home

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by grumpy
    But I failed to find anything in the C standard which supports the conclusion that the comparison in this;
    Code:
        /*  assume x is a pointer, and it's value was returned by malloc()/calloc()/realloc() */
        free(x);
        if (x > some_pointer_value)
           do_whatever();
    yields UB. Would you provide a reference into the standard please?
    Not in the way you expected, but look at 6.5.8.5, which defines the semantics of relational operations on pointers. Its says:
    If two pointers [...] both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object, pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. All pointers to members of the same union object compare equal. If the expression P points to an element of an array object and the expression Q points to the last element of the same array object, the pointer expression Q+1 compares greater than P. In all other cases, the behavior is undefined.
    Emphasis mine. What all this blather says is that relational operations on arbitrary pointers are undefined except in some special cases. Since after calling free() there is nothing special where x points to, all comparisons of x are undefined behaviour. Of course, it would take an implementation that actively enforces this constraint to actually do anything but the obvious.
    Also interesting in this is that relational operations are invalid and should not compile for void pointers. I don't think any compiler actually conforms to this.
    The situation is slightly different with equality, but not much. Two differences:
    1) void pointers are allowed.
    2) Comparison with the null pointer is defined.
    Other than that, 6.5.9.6 gives the semantics, and it, too, requires that the pointer point to an actual object or be the null pointer. Which is not the case for a free()'d pointer.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #11
    Registered User
    Join Date
    Jun 2004
    Posts
    201
    Quote Originally Posted by CornedBee
    Other than that, 6.5.9.6 gives the semantics, and it, too, requires that the pointer point to an actual object or be the null pointer. Which is not the case for a free()'d pointer.
    yeah think this is the thnigie I had in mind. it's strictly UB but no implementation is known that would show "strange" behaviour like crashes or reformats of drives or something

  12. #12
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065
    Quote Originally Posted by dwks
    As part of codeform, I have a dynamically allocated array of pointers. Some of the pointers point to the same location in memory; but if they do, they will be consecutive. So when I go to free the array, I use <snip>
    dwks: Why not create a second array equal to the first, set all the values to NULL, then add in each pointer IFF it is not in the list already. Then, all you'd have to do is to loop that one list, free anything that isn't NULL, then free both arrays. That would rid yourself of the issues of the UB in the *unlikely* event that a free to the pointer cause you to loose the original value of the variable (which it shouldn't ever do).

  13. #13
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >This seems silly to me
    It makes perfect sense to me. Once you've freed the memory that a pointer points to, you can no longer guarantee any of the requirements for comparison.
    My best code is written with the delete key.

  14. #14
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by Kennedy
    dwks: Why not create a second array equal to the first, set all the values to NULL, then add in each pointer IFF it is not in the list already. Then, all you'd have to do is to loop that one list, free anything that isn't NULL, then free both arrays. That would rid yourself of the issues of the UB in the *unlikely* event that a free to the pointer cause you to loose the original value of the variable (which it shouldn't ever do).
    Because that solution is extremely complex, when a simple restructuring of the loop (as I have shown) avoids all problems?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #15
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    I've re-written the loop (actually, I did so before there were any replys to this thread, because I thought that's what the answer would be).
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Why does C need pointer conversion
    By password636 in forum C Programming
    Replies: 2
    Last Post: 04-10-2009, 07:33 AM
  2. Replies: 5
    Last Post: 04-04-2009, 03:45 AM
  3. Smart pointer class
    By Elysia in forum C++ Programming
    Replies: 63
    Last Post: 11-03-2007, 07:05 AM
  4. scope of a pointer?
    By Syneris in forum C++ Programming
    Replies: 6
    Last Post: 12-29-2005, 09:40 PM
  5. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM