Why not realloc(&ptr, ...) and free(&ptr)?

Discussion in 'C Programming' started by James Harris, Aug 5, 2013.

  1. James Harris

    James Harris Guest

    I cannot get my head round why calls to realloc and free do not have a
    pointer to the initial pointer passed to them as in the following if ptr had
    been returned by malloc.

    retcode = realloc(&ptr, new_size);
    free(&ptr);

    The idea being that if realloc could reallocate the space it would update
    the pointer and return true. If it could not reallocate the space it would
    leave the pointer unchanged and return false.

    In the case of free() the idea is that it would set the pointer to NULL.

    Anyone know why the above two calls were designed the way they were?

    James
     
    James Harris, Aug 5, 2013
    #1
    1. Advertising

  2. James Harris

    James Kuyper Guest

    On 08/05/2013 05:30 AM, James Harris wrote:
    > I cannot get my head round why calls to realloc and free do not have a
    > pointer to the initial pointer passed to them as in the following if ptr had
    > been returned by malloc.
    >
    > retcode = realloc(&ptr, new_size);
    > free(&ptr);
    >
    > The idea being that if realloc could reallocate the space it would update
    > the pointer and return true. If it could not reallocate the space it would
    > leave the pointer unchanged and return false.
    >
    > In the case of free() the idea is that it would set the pointer to NULL.
    >
    > Anyone know why the above two calls were designed the way they were?


    You mean like this?

    char *pc = malloc(5*sizeof *pi);
    long double _Complex *pd = malloc(5*sizeof *pd);
    free(&pc);
    free(&pd);

    Do you see the problem there? pc and pd have incompatible types; those
    types might have different sizes - there are real-world machines where
    that is the case. Even if they were the same size, they might have
    incompatible representations - though I don't know of any real-world
    example of that.
    Many people think that since void* can accommodate any kind of pointer
    to an object type, void** can accommodate any kind of pointer to a
    pointer to an object type, but C doesn't work that way. It couldn't work
    that way unless it were changed to require all pointers to object types
    to have the same representation and alignment. If free() were changed to
    take a void**, it would have to used this way:
    void * temp = pc;
    free(&temp);
    temp = pd;
    free(&temp);
    This not only makes it more complicated to use free(), but it also
    eliminates the supposed advantage of this change: pc and pd are still
    not null.

    Even if C were changed to require all pointers to have the same
    representation, so that void** could work that way, this still would
    violate one of the design principles of C. You shouldn't have to pay
    for the parts of C that you don't use. Well designed code often doesn't
    need to waste time nulling such a pointer, because the pointer will
    never be used again during the time that it would have been null. That's
    true of most of the code I've written that uses free(). Why should code
    that has been written that way pay the cost of having free() waste it's
    time setting the pointer to null?
    If you do write a lot of code that requires nulling the pointer, you can
    always write a macro that wraps free(), and nulls the pointer. That way,
    you get the nulling that you need, while my code still avoids wasting
    time on nulling that I don't need. I'd have no great objection to adding
    such a macro to the C standard library, but it's so trivial to write one
    on your own that I doubt it would ever be approved.

    Also, keep in mind that a nulling version of free() would not
    necessarily do the entire job that needs to be done. In a typical
    program where you would need to null the pointer, there's often multiple
    pointers pointing into various locations inside the block of allocated
    memory. They all need to be nulled, if there's a chance they'll be used
    again before they've been set to a different non-null value.
    --
    James Kuyper
     
    James Kuyper, Aug 5, 2013
    #2
    1. Advertising

  3. James Harris

    Shao Miller Guest

    On 8/5/2013 05:30, James Harris wrote:
    > I cannot get my head round why calls to realloc and free do not have a
    > pointer to the initial pointer passed to them as in the following if ptr had
    > been returned by malloc.
    >
    > retcode = realloc(&ptr, new_size);
    > free(&ptr);
    >
    > The idea being that if realloc could reallocate the space it would update
    > the pointer and return true. If it could not reallocate the space it would
    > leave the pointer unchanged and return false.
    >
    > In the case of free() the idea is that it would set the pointer to NULL.
    >
    > Anyone know why the above two calls were designed the way they were?
    >


    What James Kuyper said. If you'd shared the declaration of 'ptr' in
    your code, it might've been easier to point to. (No pun intended.)

    Also consider:

    void func(void) {
    long * lp;

    lp = malloc(sizeof *lp);
    /* ... */
    free(lp);
    lp = NULL;
    }

    There's not really a need to set 'lp' to a null pointer value, here, as
    its lifetime is about to expire. You might want to for security
    reasons, perhaps, but then it does cost time.

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
     
    Shao Miller, Aug 5, 2013
    #3
  4. "James Harris" <> writes:

    > I cannot get my head round why calls to realloc and free do not have a
    > pointer to the initial pointer passed to them as in the following if ptr had
    > been returned by malloc.
    >
    > retcode = realloc(&ptr, new_size);
    > free(&ptr);
    >
    > The idea being that if realloc could reallocate the space it would update
    > the pointer and return true. If it could not reallocate the space it would
    > leave the pointer unchanged and return false.
    >
    > In the case of free() the idea is that it would set the pointer to NULL.
    >
    > Anyone know why the above two calls were designed the way they were?


    Types have been mentioned, but there's another issue with realloc. It
    has to copy the contents of the old memory to the new, but that's wasted
    effort if you don't want that behaviour. For example, if it's a hash
    table you will want the old elements re-hashed for the new size, not
    simply copied into the old offsets. Worse, having them copied makes the
    re-hashing a pain in the neck.

    And when sizing down, you might want to do something to the old extra
    elements (free them, for example) but that will now have to be done
    before the realloc and you'll then have problems if the re-size fails.

    All in all, it's simpler to re-size the memory at let the programmer
    decide what needs to be done with both the old and the new allocation.

    --
    Ben.
     
    Ben Bacarisse, Aug 5, 2013
    #4
  5. James Harris

    James Harris Guest

    "James Kuyper" <> wrote in message
    news:kto16s$4kp$...
    > On 08/05/2013 05:30 AM, James Harris wrote:
    >> I cannot get my head round why calls to realloc and free do not have a
    >> pointer to the initial pointer passed to them as in the following if ptr
    >> had
    >> been returned by malloc.
    >>
    >> retcode = realloc(&ptr, new_size);
    >> free(&ptr);
    >>
    >> The idea being that if realloc could reallocate the space it would update
    >> the pointer and return true. If it could not reallocate the space it
    >> would
    >> leave the pointer unchanged and return false.
    >>
    >> In the case of free() the idea is that it would set the pointer to NULL.
    >>
    >> Anyone know why the above two calls were designed the way they were?

    >
    > You mean like this?
    >
    > char *pc = malloc(5*sizeof *pi);
    > long double _Complex *pd = malloc(5*sizeof *pd);
    > free(&pc);
    > free(&pd);
    >
    > Do you see the problem there? pc and pd have incompatible types; those
    > types might have different sizes - there are real-world machines where
    > that is the case. Even if they were the same size, they might have
    > incompatible representations - though I don't know of any real-world
    > example of that.


    So, in summary, pointer to type A and pointer to type B might have different
    sizes and/or different representations of null?

    Trying to understand the ramifications of this.....

    AIUI malloc returns pointer to void. I suppose that, on machines where
    needed, the compiler inserts conversions from malloc's return (which could
    be null) in to a pointer to the right type. It knows the type of the
    variable being assigned to so can add instructions to convert malloc's
    return to a different representation if needed.

    Similarly, when a program calls free() on a pointer the compiler knows that
    that pointer is of type X and can perform any conversions back to the format
    of a void pointer before calling free().

    And when assigning NULL to a pointer or comparing a pointer with NULL the
    compiler can convert length and NULL-representation as necessary.

    OK. I think I've got that.

    So if pointers on a given architecture were always all the same size and
    always had the same representation of NULL no matter what they were pointing
    at it would have been possible to call free(&p) because the free routine
    could have included

    *p = NULL;

    But since the above cannot be guaranteed free() could not set the pointer to
    null. On those odd machines there would be different NULLs.

    Would it still have been OK for free to return NULL so that code could
    include the following?

    p = free(p);

    I know that it does not return NULL. I am trying to understand the design a
    bit better.

    Can you remember any of the machines where pointers to different types had
    different sizes? If you can I'd like to look into some more about how they
    worked and which would still be in use.

    > Many people think that since void* can accommodate any kind of pointer
    > to an object type, void** can accommodate any kind of pointer to a
    > pointer to an object type, but C doesn't work that way. It couldn't work
    > that way unless it were changed to require all pointers to object types
    > to have the same representation and alignment. If free() were changed to
    > take a void**, it would have to used this way:
    > void * temp = pc;
    > free(&temp);
    > temp = pd;
    > free(&temp);
    > This not only makes it more complicated to use free(), but it also
    > eliminates the supposed advantage of this change: pc and pd are still
    > not null.


    I don't understand this. If all pointers were the same size and represented
    NULL in the same way why couldn't free(&p) work? It could both get and set
    the pointer that p pointed at.

    If, on the other hand, you mean the above code to be used where pointers
    were not of the same size then the problem is that there's no way - sensible
    or otherwise - to dereference a void * so I cannot see how the code could
    work.

    In terms of the design of free(), if pointers had different sizes either
    free would need an extra parameter identifying the pointer type or there
    would need to be different versions of free, one per pointer type - or at
    least one per representation of NULL. AFAICS it's OK for mallo to return
    void * because the caller knows what type conversions to carry out; but with
    free() the callee would be expected to deal with the differences - something
    that it could not do without knowing the pointer type.

    Either way, I can see the point about machines with pointer types which
    differ in size or null or both.

    >
    > Even if C were changed to require all pointers to have the same
    > representation, so that void** could work that way, this still would
    > violate one of the design principles of C. You shouldn't have to pay
    > for the parts of C that you don't use. Well designed code often doesn't
    > need to waste time nulling such a pointer, because the pointer will
    > never be used again during the time that it would have been null. That's
    > true of most of the code I've written that uses free(). Why should code
    > that has been written that way pay the cost of having free() waste it's
    > time setting the pointer to null?


    I'm less sure I agree with this. Clearing a pointer (p = NULL) would be
    insignificant when compared with the cost of just calling the free routine
    even if the free routine were to do the absolute minimum. Further, there are
    many examples in the C library of distributed costs. For example, a call to
    printf returns an int that's often not needed. Would you propose another set
    of printf routines which did not return a value on the basis that it could
    save on costs? In fact the whole suite of printf routines are probably an
    order of magnitude (or more) more expensive than they need to be for most of
    what they are used for, because they cope with the general case.

    > If you do write a lot of code that requires nulling the pointer, you can
    > always write a macro that wraps free(), and nulls the pointer. That way,
    > you get the nulling that you need, while my code still avoids wasting
    > time on nulling that I don't need. I'd have no great objection to adding
    > such a macro to the C standard library, but it's so trivial to write one
    > on your own that I doubt it would ever be approved.
    >
    > Also, keep in mind that a nulling version of free() would not
    > necessarily do the entire job that needs to be done. In a typical
    > program where you would need to null the pointer, there's often multiple
    > pointers pointing into various locations inside the block of allocated
    > memory. They all need to be nulled, if there's a chance they'll be used
    > again before they've been set to a different non-null value.


    Sure. That applies any time a region is realloced as well.

    By the way, I take it your comments about free apply to realloc too. AFAICT
    they do, i.e. realloc couldn't know what to change a pointer to on machines
    where there were different sizes of pointer. Again, it could be done as
    above if all pointers on a given machine were of the same size and same NULL
    (nearly all machines in use today?).

    Pity - it would be especially convenient for realloc where the return code
    could indicat success or failure.

    James
     
    James Harris, Aug 5, 2013
    #5
  6. James Harris

    Shao Miller Guest

    On 8/5/2013 11:40, James Harris wrote:
    > "James Kuyper" <> wrote in message
    > news:kto16s$4kp$...
    >> On 08/05/2013 05:30 AM, James Harris wrote:
    >>> I cannot get my head round why calls to realloc and free do not have a
    >>> pointer to the initial pointer passed to them as in the following if ptr
    >>> had
    >>> been returned by malloc.
    >>>
    >>> retcode = realloc(&ptr, new_size);
    >>> free(&ptr);
    >>>
    >>> The idea being that if realloc could reallocate the space it would update
    >>> the pointer and return true. If it could not reallocate the space it
    >>> would
    >>> leave the pointer unchanged and return false.
    >>>
    >>> In the case of free() the idea is that it would set the pointer to NULL.
    >>>
    >>> Anyone know why the above two calls were designed the way they were?

    >>
    >> You mean like this?
    >>
    >> char *pc = malloc(5*sizeof *pi);
    >> long double _Complex *pd = malloc(5*sizeof *pd);
    >> free(&pc);
    >> free(&pd);
    >>
    >> Do you see the problem there? pc and pd have incompatible types; those
    >> types might have different sizes - there are real-world machines where
    >> that is the case. Even if they were the same size, they might have
    >> incompatible representations - though I don't know of any real-world
    >> example of that.

    >
    > So, in summary, pointer to type A and pointer to type B might have different
    > sizes and/or different representations of null?
    >
    > Trying to understand the ramifications of this.....
    >
    > AIUI malloc returns pointer to void. I suppose that, on machines where
    > needed, the compiler inserts conversions from malloc's return (which could
    > be null) in to a pointer to the right type. It knows the type of the
    > variable being assigned to so can add instructions to convert malloc's
    > return to a different representation if needed.
    >


    It's not special for 'malloc', it's a specialty of 'void *'.

    > Similarly, when a program calls free() on a pointer the compiler knows that
    > that pointer is of type X and can perform any conversions back to the format
    > of a void pointer before calling free().
    >
    > And when assigning NULL to a pointer or comparing a pointer with NULL the
    > compiler can convert length and NULL-representation as necessary.
    >
    > OK. I think I've got that.
    >
    > So if pointers on a given architecture were always all the same size and
    > always had the same representation of NULL no matter what they were pointing
    > at it would have been possible to call free(&p) because the free routine
    > could have included
    >
    > *p = NULL;
    >


    Why do you care about assigning the pointer to a null pointer value? Is
    it because you're in a loop, or something? Security?

    > But since the above cannot be guaranteed free() could not set the pointer to
    > null. On those odd machines there would be different NULLs.
    >
    > Would it still have been OK for free to return NULL so that code could
    > include the following?
    >
    > p = free(p);
    >


    It seems to me that it's more common that nobody cares what happens to
    'p' after its value has been passed to 'free'. So then it's more common
    to see:

    free(p);

    If 'free' had a return value, then this code would ignore that return
    value, and people with their compiler-warnings turned up would have to
    do something like:

    (void) free(p);

    > I know that it does not return NULL. I am trying to understand the design a
    > bit better.
    >
    > Can you remember any of the machines where pointers to different types had
    > different sizes? If you can I'd like to look into some more about how they
    > worked and which would still be in use.
    >
    >> Many people think that since void* can accommodate any kind of pointer
    >> to an object type, void** can accommodate any kind of pointer to a
    >> pointer to an object type, but C doesn't work that way. It couldn't work
    >> that way unless it were changed to require all pointers to object types
    >> to have the same representation and alignment. If free() were changed to
    >> take a void**, it would have to used this way:
    >> void * temp = pc;
    >> free(&temp);
    >> temp = pd;
    >> free(&temp);
    >> This not only makes it more complicated to use free(), but it also
    >> eliminates the supposed advantage of this change: pc and pd are still
    >> not null.

    >
    > I don't understand this. If all pointers were the same size and represented
    > NULL in the same way why couldn't free(&p) work? It could both get and set
    > the pointer that p pointed at.
    >


    Beyond sizes, C cares about types. '&p' will yield a 'type-of-p *'. If
    'p' is a 'long *', then '&p' will yield a 'long **'.

    > If, on the other hand, you mean the above code to be used where pointers
    > were not of the same size then the problem is that there's no way - sensible
    > or otherwise - to dereference a void * so I cannot see how the code could
    > work.
    >
    > In terms of the design of free(), if pointers had different sizes either
    > free would need an extra parameter identifying the pointer type or there
    > would need to be different versions of free, one per pointer type - or at
    > least one per representation of NULL. AFAICS it's OK for mallo to return
    > void * because the caller knows what type conversions to carry out; but with
    > free() the callee would be expected to deal with the differences - something
    > that it could not do without knowing the pointer type.
    >
    > Either way, I can see the point about machines with pointer types which
    > differ in size or null or both.
    >
    >>
    >> Even if C were changed to require all pointers to have the same
    >> representation, so that void** could work that way, this still would
    >> violate one of the design principles of C. You shouldn't have to pay
    >> for the parts of C that you don't use. Well designed code often doesn't
    >> need to waste time nulling such a pointer, because the pointer will
    >> never be used again during the time that it would have been null. That's
    >> true of most of the code I've written that uses free(). Why should code
    >> that has been written that way pay the cost of having free() waste it's
    >> time setting the pointer to null?

    >
    > I'm less sure I agree with this. Clearing a pointer (p = NULL) would be
    > insignificant when compared with the cost of just calling the free routine
    > even if the free routine were to do the absolute minimum. Further, there are
    > many examples in the C library of distributed costs. For example, a call to
    > printf returns an int that's often not needed. Would you propose another set
    > of printf routines which did not return a value on the basis that it could
    > save on costs? In fact the whole suite of printf routines are probably an
    > order of magnitude (or more) more expensive than they need to be for most of
    > what they are used for, because they cope with the general case.
    >


    But why bother clearing the pointer? Do you initialize it? If not,
    then why the asymmetric treatment?

    while (cond) {
    void * vp = malloc(4);
    /* ...Test and do work... */
    free(vp);
    vp = NULL;
    }

    Clearing the pointer here is a waste, because it's going to be
    initialized on the next iteration.

    >> If you do write a lot of code that requires nulling the pointer, you can
    >> always write a macro that wraps free(), and nulls the pointer. That way,
    >> you get the nulling that you need, while my code still avoids wasting
    >> time on nulling that I don't need. I'd have no great objection to adding
    >> such a macro to the C standard library, but it's so trivial to write one
    >> on your own that I doubt it would ever be approved.
    >>
    >> Also, keep in mind that a nulling version of free() would not
    >> necessarily do the entire job that needs to be done. In a typical
    >> program where you would need to null the pointer, there's often multiple
    >> pointers pointing into various locations inside the block of allocated
    >> memory. They all need to be nulled, if there's a chance they'll be used
    >> again before they've been set to a different non-null value.

    >
    > Sure. That applies any time a region is realloced as well.
    >
    > By the way, I take it your comments about free apply to realloc too. AFAICT
    > they do, i.e. realloc couldn't know what to change a pointer to on machines
    > where there were different sizes of pointer. Again, it could be done as
    > above if all pointers on a given machine were of the same size and same NULL
    > (nearly all machines in use today?).
    >
    > Pity - it would be especially convenient for realloc where the return code
    > could indicat success or failure.


    'realloc' does indicate success or failure. It has one failure value
    and many success values.

    Something you might not be aware of: C says that a pointer's value
    becomes indeterminate after that value has been passed to 'free'. So given:

    void * vp1 = malloc(4);
    void * vp2 = vp1;
    void * vp3 = vp2;
    free(vp1);

    All of these pointers contain indeterminate values after the call to
    'free'. This is because the lifetime of the pointed-to object has
    expired, if I recall correctly.

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
     
    Shao Miller, Aug 5, 2013
    #6
  7. In article <ktok4a$c4v$>,
    Shao Miller <> wrote:
    ....
    >Why do you care about assigning the pointer to a null pointer value? Is
    >it because you're in a loop, or something? Security?


    The usual reason is so that the code can determine that the pointer has
    been freed. I.e., it acts as a sentinel value. Among other things, this
    could be used to implement parameter verification; a routine could check to
    see if a passed-in-value was NULL and issue an error message like:

    "p is NULL. Perhaps it has been free'd..."

    ....
    >Something you might not be aware of: C says that a pointer's value
    >becomes indeterminate after that value has been passed to 'free'. So given:
    >
    > void * vp1 = malloc(4);
    > void * vp2 = vp1;
    > void * vp3 = vp2;
    > free(vp1);
    >
    >All of these pointers contain indeterminate values after the call to
    >'free'. This is because the lifetime of the pointed-to object has
    >expired, if I recall correctly.


    Beg to differ. I think that *de-refencing* a pointer value after it has
    been free'd is, of course, UB, but the pointer itself cannot change,
    because of C's call-by-value semantics.

    I.e., in your example above, the value of vp1 cannot be changed as a result
    of being passed to free().

    --
    This is the GOP's problem. When you're at the beginning of the year
    and you've got nine Democrats running for the nomination, maybe one or
    two of them are Dennis Kucinich. When you have nine Republicans, seven
    or eight of them are Michelle Bachmann.
     
    Kenny McCormack, Aug 5, 2013
    #7
  8. James Harris

    Shao Miller Guest

    On 8/5/2013 13:00, Kenny McCormack wrote:
    > In article <ktok4a$c4v$>,
    > Shao Miller <> wrote:
    > ...
    >> Why do you care about assigning the pointer to a null pointer value? Is
    >> it because you're in a loop, or something? Security?

    >
    > The usual reason is so that the code can determine that the pointer has
    > been freed. I.e., it acts as a sentinel value. Among other things, this
    > could be used to implement parameter verification; a routine could check to
    > see if a passed-in-value was NULL and issue an error message like:
    >
    > "p is NULL. Perhaps it has been free'd..."
    >


    I see.

    > ...
    >> Something you might not be aware of: C says that a pointer's value
    >> becomes indeterminate after that value has been passed to 'free'. So given:
    >>
    >> void * vp1 = malloc(4);
    >> void * vp2 = vp1;
    >> void * vp3 = vp2;
    >> free(vp1);
    >>
    >> All of these pointers contain indeterminate values after the call to
    >> 'free'. This is because the lifetime of the pointed-to object has
    >> expired, if I recall correctly.

    >
    > Beg to differ. I think that *de-refencing* a pointer value after it has
    > been free'd is, of course, UB, but the pointer itself cannot change,
    > because of C's call-by-value semantics.
    >
    > I.e., in your example above, the value of vp1 cannot be changed as a result
    > of being passed to free().
    >


    Nah, C explicitly does state[6.2.4p2] that this is an exception to the
    usual "callee cannot affect the caller's objects", because it's actually
    affecting the set of all pointer values; which are good and which are
    bad, m'kay? Doing printf("%p", vp3) after the 'free' might attempt to
    load the value of 'vp3' into an address register and the CPU notices
    that this does not address any storage, so it's a "mistake."

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
     
    Shao Miller, Aug 5, 2013
    #8
  9. On 05-Aug-13 12:00, Kenny McCormack wrote:
    > In article <ktok4a$c4v$>,
    > Shao Miller <> wrote:
    >> Something you might not be aware of: C says that a pointer's value
    >> becomes indeterminate after that value has been passed to 'free'.
    >> So given:
    >>
    >> void * vp1 = malloc(4);
    >> void * vp2 = vp1;
    >> void * vp3 = vp2;
    >> free(vp1);
    >>
    >> All of these pointers contain indeterminate values after the call to
    >> 'free'. This is because the lifetime of the pointed-to object has
    >> expired, if I recall correctly.

    >
    > Beg to differ. I think that *de-refencing* a pointer value after it
    > has been free'd is, of course, UB, but the pointer itself cannot
    > change, because of C's call-by-value semantics.


    The value of vp1, vp2 and vp3 does not change after calling free(), but
    those existing values all become indeterminate.

    For instance, merely loading an indeterminate value into an address
    register can fail because that address value no longer refers to any
    valid object. You don't need to dereference it to cause problems.

    S

    --
    Stephen Sprunk "God does not play dice." --Albert Einstein
    CCIE #3723 "God is an inveterate gambler, and He throws the
    K5SSS dice at every possible opportunity." --Stephen Hawking
     
    Stephen Sprunk, Aug 5, 2013
    #9
  10. James Harris

    James Harris Guest

    "Shao Miller" <> wrote in message
    news:ktok4a$c4v$...

    ....

    >> So if pointers on a given architecture were always all the same size and
    >> always had the same representation of NULL no matter what they were
    >> pointing
    >> at it would have been possible to call free(&p) because the free routine
    >> could have included
    >>
    >> *p = NULL;
    >>

    >
    > Why do you care about assigning the pointer to a null pointer value? Is
    > it because you're in a loop, or something? Security?


    A loop, no. Security, yes. Since the pointer value is no longer valid
    setting it to null makes that clear to any later code. It could also be
    passed to realloc which would then behave as malloc. Of course, it's
    programmer responsibilty to handle any other pointers to the same area.

    >
    >> But since the above cannot be guaranteed free() could not set the pointer
    >> to
    >> null. On those odd machines there would be different NULLs.
    >>
    >> Would it still have been OK for free to return NULL so that code could
    >> include the following?
    >>
    >> p = free(p);
    >>

    >
    > It seems to me that it's more common that nobody cares what happens to 'p'
    > after its value has been passed to 'free'. So then it's more common to
    > see:
    >
    > free(p);
    >
    > If 'free' had a return value, then this code would ignore that return
    > value, and people with their compiler-warnings turned up would have to do
    > something like:
    >
    > (void) free(p);


    If the compiler warned about that wouldn't it also warn about

    printf("x");

    Having to declare

    (void) printf("x");

    would be unusual.

    Incidentally, I was once told casts were a bad idea (TM) as they mask other
    issues. Maybe a compiler should complain about them too!

    James
     
    James Harris, Aug 5, 2013
    #10
  11. James Harris

    James Kuyper Guest

    On 08/05/2013 01:29 PM, James Harris wrote:
    > "Shao Miller" <> wrote in message
    > news:ktok4a$c4v$...

    ....
    >> Why do you care about assigning the pointer to a null pointer value? Is
    >> it because you're in a loop, or something? Security?

    >
    > A loop, no. Security, yes. Since the pointer value is no longer valid
    > setting it to null makes that clear to any later code. It could also be
    > passed to realloc which would then behave as malloc. Of course, it's
    > programmer responsibilty to handle any other pointers to the same area.


    When nulling free()d pointers is necessary (and it seldom has been, in
    my code), the reason for doing so is generally to allow code elsewhere
    in the program to do:

    if(p)
    {
    // *p exists
    // Do things with *p
    }

    If the other piece of code will always know whether or not *p currently
    exists, without having to check the value of p, then p doesn't need to
    be nulled.

    ....
    >> It seems to me that it's more common that nobody cares what happens to 'p'
    >> after its value has been passed to 'free'. So then it's more common to
    >> see:
    >>
    >> free(p);
    >>
    >> If 'free' had a return value, then this code would ignore that return
    >> value, and people with their compiler-warnings turned up would have to do
    >> something like:
    >>
    >> (void) free(p);

    >
    > If the compiler warned about that wouldn't it also warn about
    >
    > printf("x");


    It should, for the sake of consistency.

    > Having to declare
    >
    > (void) printf("x");
    >
    > would be unusual.
    >
    > Incidentally, I was once told casts were a bad idea (TM) as they mask other
    > issues. Maybe a compiler should complain about them too!


    A cast is also often the result of someone trying to shut up a compiler
    warning, who didn't understand that the cast doesn't actually fix the
    problem that triggered the complaint. That's one reason why casts should
    be treated with suspicion. However, it is sometimes the legitimate
    purpose of a cast to disable such complaints.
     
    James Kuyper, Aug 5, 2013
    #11
  12. James Harris

    James Kuyper Guest

    On 08/05/2013 11:40 AM, James Harris wrote:
    > "James Kuyper" <> wrote in message
    > news:kto16s$4kp$...

    ....
    > So, in summary, pointer to type A and pointer to type B might have different
    > sizes and/or different representations of null?
    >
    > Trying to understand the ramifications of this.....
    >
    > AIUI malloc returns pointer to void. I suppose that, on machines where
    > needed, the compiler inserts conversions from malloc's return (which could
    > be null) in to a pointer to the right type. It knows the type of the
    > variable being assigned to so can add instructions to convert malloc's
    > return to a different representation if needed.
    >
    > Similarly, when a program calls free() on a pointer the compiler knows that
    > that pointer is of type X and can perform any conversions back to the format
    > of a void pointer before calling free().
    >
    > And when assigning NULL to a pointer or comparing a pointer with NULL the
    > compiler can convert length and NULL-representation as necessary.


    Note: be careful about how you use NULL and null. "null" is an adjective
    which can, in ordinary English usage, be applied to many things, but in
    C it's applied only to pointer and character values. NULL is the name of
    a C macro whose expansion is required to be a null pointer constant.
    There is only one NULL, #defined in several C standard headers. Any
    given pointer type may or may not have multiple representations of null
    pointer values; but if it has more than one, they must all compare
    equal. There is a huge variety of different possible null pointer
    constants, though 0 and (void*)0 are overwhelmingly the most common ones.

    When used in a context where a pointer is required, null pointer
    constants are automatically converted to null pointer values. This
    causes many people (yourself included) to use NULL in contexts where "a
    null pointer value" would have been more appropriate. However, not all
    null pointer values are the immediate result of such a conversion
    applied to NULL; most are not.

    ....
    > So if pointers on a given architecture were always all the same size and
    > always had the same representation of NULL no matter what they were pointing
    > at it would have been possible to call free(&p) because the free routine
    > could have included
    >
    > *p = NULL;


    Correct. To be precise, that would require a rewrite of the rules
    governing compatible types, a rewrite that would be feasible because of
    the "same representation" requirement.

    > But since the above cannot be guaranteed free() could not set the pointer to
    > null. On those odd machines there would be different NULLs.
    >
    > Would it still have been OK for free to return NULL so that code could
    > include the following?
    >
    > p = free(p);


    Yes, if free() had been defined to return a null pointer value, that
    would also have worked, and no rearrangement to the rules would be needed.

    > Can you remember any of the machines where pointers to different types had
    > different sizes? If you can I'd like to look into some more about how they
    > worked and which would still be in use.


    I've never used such a machine, but others have mentioned them in this
    forum. I never wrote down the names of those machines, just made note of
    the fact that they exist (yes - present tense). Hopefully some of those
    people can give you some examples.

    As I understand it, the single most common reason for such pointers is
    the decision to implement C on systems where the word size (the size of
    the unit of memory that machine addresses refer to) is too big to be
    used as the size of a C byte. On such machines, for types where
    _Alignof(type) > word_size, pointers to objects of that type consist of
    just a machine address for the start of the object. However, for types
    such as char, where _Alignof(type)<word_size, pointers consist of a
    machine address and a byte offset within the word. Depending upon how
    much addressable memory a system has, having to include the byte offset
    could require an increase in the pointer size.

    >> Many people think that since void* can accommodate any kind of pointer
    >> to an object type, void** can accommodate any kind of pointer to a
    >> pointer to an object type, but C doesn't work that way. It couldn't work
    >> that way unless it were changed to require all pointers to object types
    >> to have the same representation and alignment. If free() were changed to
    >> take a void**, it would have to used this way:
    >> void * temp = pc;
    >> free(&temp);
    >> temp = pd;
    >> free(&temp);
    >> This not only makes it more complicated to use free(), but it also
    >> eliminates the supposed advantage of this change: pc and pd are still
    >> not null.

    >
    > I don't understand this. If all pointers were the same size and represented
    > NULL in the same way why couldn't free(&p) work? It could both get and set
    > the pointer that p pointed at.


    In the above section, I'm describing the case where the definition of
    free() were changed to take a void** argument, but the rules about
    compatible types were not. Perhaps I should have been clearer about that.

    > If, on the other hand, you mean the above code to be used where pointers
    > were not of the same size then the problem is that there's no way - sensible
    > or otherwise - to dereference a void * so I cannot see how the code could
    > work.


    In the context I was talking about, free() could have been defined as
    follows:

    void free(void**p)
    {
    // free the block of memory pointed at by *p

    *p = NULL;
    }

    The only pointer that gets dereferenced has the type void**, which is
    perfectly acceptable, not void*, which would be a constraint violation.

    > In terms of the design of free(), if pointers had different sizes either
    > free would need an extra parameter identifying the pointer type or there
    > would need to be different versions of free, one per pointer type - or at
    > least one per representation of NULL. ...


    Those are other, more complicated ways of dealing with the issue. They
    all serve to demonstrate how the way free() was actually defined was a
    better choice.

    >> Even if C were changed to require all pointers to have the same
    >> representation, so that void** could work that way, this still would
    >> violate one of the design principles of C. You shouldn't have to pay
    >> for the parts of C that you don't use. Well designed code often doesn't
    >> need to waste time nulling such a pointer, because the pointer will
    >> never be used again during the time that it would have been null. That's
    >> true of most of the code I've written that uses free(). Why should code
    >> that has been written that way pay the cost of having free() waste it's
    >> time setting the pointer to null?

    >
    > I'm less sure I agree with this. Clearing a pointer (p = NULL) would be
    > insignificant when compared with the cost of just calling the free routine
    > even if the free routine were to do the absolute minimum. ...


    I don't claim that it's a big cost, merely one that shouldn't be paid.

    ....
    > By the way, I take it your comments about free apply to realloc too. AFAICT
    > they do, i.e. realloc couldn't know what to change a pointer to on machines
    > where there were different sizes of pointer. Again, it could be done as
    > above if all pointers on a given machine were of the same size and same NULL
    > (nearly all machines in use today?).


    Correct.
     
    James Kuyper, Aug 5, 2013
    #12
  13. James Harris

    Shao Miller Guest

    On 8/5/2013 13:29, James Harris wrote:
    >
    > If the compiler warned about that wouldn't it also warn about
    >
    > printf("x");
    >
    > Having to declare
    >
    > (void) printf("x");
    >
    > would be unusual.
    >


    Yup. People with their warnings turned way up have to worry about that
    one, too.

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
     
    Shao Miller, Aug 5, 2013
    #13
  14. "James Harris" <> writes:
    > "James Kuyper" <> wrote in message
    > news:kto16s$4kp$...
    >> On 08/05/2013 05:30 AM, James Harris wrote:

    [...]
    > Would it still have been OK for free to return NULL so that code could
    > include the following?
    >
    > p = free(p);
    >
    > I know that it does not return NULL. I am trying to understand the design a
    > bit better.


    It would have been ok, but probably not particularly useful. Having a
    function always returning the same constant value seems like a bit of
    wasted effort. And since it would still be perfectly possible to write

    free(p);

    many programmers would still do that; it doesn't *enforce* setting p to
    NULL.

    An alternative might have been to make free() a macro that deallocates
    the memory pointed to by its argument, and then sets its argument to
    NULL. Of course you can already do that:

    #define FREE(p) (free(p); (p) = NULL)

    but if the macro in the standard library, the function version wouldn't
    have to be. But it's way too late to change that.

    >> Many people think that since void* can accommodate any kind of pointer
    >> to an object type, void** can accommodate any kind of pointer to a
    >> pointer to an object type, but C doesn't work that way. It couldn't work
    >> that way unless it were changed to require all pointers to object types
    >> to have the same representation and alignment. If free() were changed to
    >> take a void**, it would have to used this way:
    >> void * temp = pc;
    >> free(&temp);
    >> temp = pd;
    >> free(&temp);
    >> This not only makes it more complicated to use free(), but it also
    >> eliminates the supposed advantage of this change: pc and pd are still
    >> not null.

    >
    > I don't understand this. If all pointers were the same size and represented
    > NULL in the same way why couldn't free(&p) work? It could both get and set
    > the pointer that p pointed at.


    Even if all pointers have the same size and representation, they're
    still of distinct types.

    But if the C standard assumed that all pointers "smell alike", it could
    theoretically change its type system so that void** is a generic
    pointer-to-pointer type, in the same way that void* is a generic pointer
    type. Then a free()-like function that takes a void** argument could
    work. But that would add a requirement that the argument to free() must
    be an lvalue, which is not currently the case (it commonly is anyway,
    but such a requirement could break *some* code).

    char *ptr malloc(42);
    assert(ptr != NULL);
    ptr++;
    free(ptr - 1);

    Historically, I suspect that free() was first implemented when C
    was more weakly typed than it is now; pointers and integers could
    be freely mixed, and dinosaurs roamed the earth. At the same
    time, there was a strong assumption that the programmer knows what
    he's doing, so there was little perceived need for a deallocation
    function to set your pointers to NULL for you. ANSI added stronger
    type checking, but tried not to break existing code.

    But that's probably over-thinking the matter. There are several
    possible ways to write a deallocation function. The author of the
    library whose interface was eventually incorporated into the standard
    picked a way that worked well enough.

    If you're looking for a language and standard library that's a model of
    coherent design, C may not be the best place to look. It is what it is.

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Working, but not speaking, for JetHead Development, Inc.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Aug 5, 2013
    #14
  15. Ben Bacarisse <> writes:
    > "James Harris" <> writes:
    >> I cannot get my head round why calls to realloc and free do not have a
    >> pointer to the initial pointer passed to them as in the following if ptr had
    >> been returned by malloc.
    >>
    >> retcode = realloc(&ptr, new_size);
    >> free(&ptr);
    >>
    >> The idea being that if realloc could reallocate the space it would update
    >> the pointer and return true. If it could not reallocate the space it would
    >> leave the pointer unchanged and return false.
    >>
    >> In the case of free() the idea is that it would set the pointer to NULL.
    >>
    >> Anyone know why the above two calls were designed the way they were?

    >
    > Types have been mentioned, but there's another issue with realloc. It
    > has to copy the contents of the old memory to the new, but that's wasted
    > effort if you don't want that behaviour. For example, if it's a hash
    > table you will want the old elements re-hashed for the new size, not
    > simply copied into the old offsets. Worse, having them copied makes the
    > re-hashing a pain in the neck.
    >
    > And when sizing down, you might want to do something to the old extra
    > elements (free them, for example) but that will now have to be done
    > before the realloc and you'll then have problems if the re-size fails.
    >
    > All in all, it's simpler to re-size the memory at let the programmer
    > decide what needs to be done with both the old and the new allocation.


    Once you've called realloc(), you can't access the old allocation.

    An alternative is to malloc() the new allocation, then do whatever you
    like with the old and new chunks of memory, then free() the old
    allocation. But that doesn't give you realloc()'s ability to re-use the
    old space.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Working, but not speaking, for JetHead Development, Inc.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Aug 5, 2013
    #15
  16. Shao Miller <> writes:
    [...]
    > Nah, C explicitly does state[6.2.4p2] that this is an exception to the
    > usual "callee cannot affect the caller's objects", because it's actually
    > affecting the set of all pointer values; which are good and which are
    > bad, m'kay? Doing printf("%p", vp3) after the 'free' might attempt to
    > load the value of 'vp3' into an address register and the CPU notices
    > that this does not address any storage, so it's a "mistake."


    I'm not sure that's necessarily an exception.

    The relevant sentence is:

    The value of a pointer becomes indeterminate when the object it
    points to (or just past) reaches the end of its lifetime.

    but I'd argue that that doesn't imply that it has a different value.
    Rather it keeps the *same* value; what changes is that that value
    is determinate before the call to free(), and indeterminate after.

    And the same would apply to a pointer to an object with automatic
    storage duration.

    I think there's a DR (which I can't find at the moment) that says that
    free(p) can actually change the representation of p -- which implies
    that the unsigned char objects that make up its representation can have
    their values change. If I'm remembering it correctly, I'm not at all
    convinced that that can be derived from the normative wording of the
    standard. (Can someone else find a reference to it?)

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Working, but not speaking, for JetHead Development, Inc.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Aug 5, 2013
    #16
  17. James Harris

    BartC Guest

    "James Harris" <> wrote in message
    news:ktnquj$6ku$...
    > I cannot get my head round why calls to realloc and free do not have a
    > pointer to the initial pointer passed to them as in the following if ptr
    > had been returned by malloc.
    >
    > retcode = realloc(&ptr, new_size);
    > free(&ptr);
    >
    > The idea being that if realloc could reallocate the space it would update
    > the pointer and return true. If it could not reallocate the space it would
    > leave the pointer unchanged and return false.
    >
    > In the case of free() the idea is that it would set the pointer to NULL.
    >
    > Anyone know why the above two calls were designed the way they were?


    Because using pointers to pointers is more complex? (Apart from all the
    type-matching issues mentioned.)

    As it is now, it's fairly straightforward, and the call to realloc() matches
    that to malloc(), with both returning a pointer. You can also pass an
    r-value to these functions, although that has limited use.

    In any case it's possible to wrap free() and realloc() with your own
    versions that behave your way, I think someone mentioned.

    However, zeroing one pointer probably is not so useful, as there could be a
    whole bunch of other pointers into the same block. And if the ptr in &ptr
    has been supplied via a function parameter, the original pointer can't be
    changed.

    --
    Bartc
     
    BartC, Aug 5, 2013
    #17
  18. James Harris

    Shao Miller Guest

    On 8/5/2013 14:39, Keith Thompson wrote:
    > Shao Miller <> writes:
    > [...]
    >> Nah, C explicitly does state[6.2.4p2] that this is an exception to the
    >> usual "callee cannot affect the caller's objects", because it's actually
    >> affecting the set of all pointer values; which are good and which are
    >> bad, m'kay? Doing printf("%p", vp3) after the 'free' might attempt to
    >> load the value of 'vp3' into an address register and the CPU notices
    >> that this does not address any storage, so it's a "mistake."

    >
    > I'm not sure that's necessarily an exception.
    >
    > The relevant sentence is:
    >
    > The value of a pointer becomes indeterminate when the object it
    > points to (or just past) reaches the end of its lifetime.
    >
    > but I'd argue that that doesn't imply that it has a different value.


    I didn't mean to suggest that it changed the value's bits, but its meaning.

    > Rather it keeps the *same* value; what changes is that that value
    > is determinate before the call to free(), and indeterminate after.
    >
    > And the same would apply to a pointer to an object with automatic
    > storage duration.
    >
    > I think there's a DR (which I can't find at the moment) that says that
    > free(p) can actually change the representation of p -- which implies
    > that the unsigned char objects that make up its representation can have
    > their values change. If I'm remembering it correctly, I'm not at all
    > convinced that that can be derived from the normative wording of the
    > standard. (Can someone else find a reference to it?)
    >


    DR #260?:

    http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_260.htm

    --
    - Shao Miller
    --
    "Thank you for the kind words; those are the kind of words I like to hear.

    Cheerily," -- Richard Harter
     
    Shao Miller, Aug 5, 2013
    #18
  19. In article <ktosqr$f4$>,
    Shao Miller <> wrote:
    ....
    >I didn't mean to suggest that it changed the value's bits, but its meaning.


    We're obviously into "angels and pins" territory here - and all of us trying
    to remain catechismically correct while arguing about religious dogma.

    But, that said, the following ought to be good enough to demonstrate C's
    call-by-value semantics:

    int x = (int) p;
    free(p);
    assert(x == (int) p);

    --
    The scent of awk programmers is a lot more attractive to women than
    the scent of perl programmers.

    (From the "GAWK" manual)
     
    Kenny McCormack, Aug 5, 2013
    #19
  20. Keith Thompson <> writes:

    > Ben Bacarisse <> writes:
    >> "James Harris" <> writes:
    >>> I cannot get my head round why calls to realloc and free do not have a
    >>> pointer to the initial pointer passed to them as in the following if ptr had
    >>> been returned by malloc.
    >>>
    >>> retcode = realloc(&ptr, new_size);
    >>> free(&ptr);
    >>>
    >>> The idea being that if realloc could reallocate the space it would update
    >>> the pointer and return true. If it could not reallocate the space it would
    >>> leave the pointer unchanged and return false.
    >>>
    >>> In the case of free() the idea is that it would set the pointer to NULL.
    >>>
    >>> Anyone know why the above two calls were designed the way they were?

    >>
    >> Types have been mentioned, but there's another issue with realloc. It
    >> has to copy the contents of the old memory to the new, but that's wasted
    >> effort if you don't want that behaviour. For example, if it's a hash
    >> table you will want the old elements re-hashed for the new size, not
    >> simply copied into the old offsets. Worse, having them copied makes the
    >> re-hashing a pain in the neck.
    >>
    >> And when sizing down, you might want to do something to the old extra
    >> elements (free them, for example) but that will now have to be done
    >> before the realloc and you'll then have problems if the re-size fails.
    >>
    >> All in all, it's simpler to re-size the memory at let the programmer
    >> decide what needs to be done with both the old and the new allocation.

    >
    > Once you've called realloc(), you can't access the old allocation.


    It's worse than that. I don't know what I was thinking. Consider it a
    null remark -- there's nothing useful in it.

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Aug 5, 2013
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Sid
    Replies:
    5
    Views:
    1,110
  2. Christopher Benson-Manica

    Re: realloc, need to free old ptr?

    Christopher Benson-Manica, Aug 26, 2003, in forum: C Programming
    Replies:
    4
    Views:
    360
    Dave Thompson
    Sep 1, 2003
  3. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,237
    Smokey Grindel
    Dec 2, 2006
  4. G Fernandes
    Replies:
    9
    Views:
    628
    DHOLLINGSWORTH2
    Feb 27, 2005
  5. Jason

    difference between *ptr++ and ++*ptr ?

    Jason, May 15, 2005, in forum: C Programming
    Replies:
    19
    Views:
    6,666
    Chris Torek
    May 19, 2005
Loading...

Share This Page