??alloc(0) - std interpretation

Discussion in 'C Programming' started by sNOiSPAMt@amu.edu.pl, May 17, 2004.

  1. Guest

    7.20.3#1
    [...] If the size of the space requested is
    zero, the behavior is implementation-defined: either a null
    pointer is returned, or the behavior is as if the size were
    some nonzero value, except that the returned pointer shall
    not be used to access an object.

    Then the ??alloc() functions are described in 7.20.3.* .

    Does the above quote's "either, or" apply to all the ??alloc() functions
    *together* or *separately*?

    Example:
    In GNU libc malloc(0)!=NULL, but realloc(..., 0)==NULL. Is this
    compliant?

    TIA

    --
    Stan Tobias
     
    , May 17, 2004
    #1
    1. Advertising

  2. <> wrote in message news:...
    >
    > 7.20.3#1
    > [...] If the size of the space requested is
    > zero, the behavior is implementation-defined: either a null
    > pointer is returned, or the behavior is as if the size were
    > some nonzero value, except that the returned pointer shall
    > not be used to access an object.
    >
    > Then the ??alloc() functions are described in 7.20.3.* .
    >
    > Does the above quote's "either, or" apply to all the ??alloc() functions
    > *together* or *separately*?
    >
    > Example:
    > In GNU libc malloc(0)!=NULL, but realloc(..., 0)==NULL. Is this
    > compliant?


    realloc(ptr, 0) is defined to be equal to free(ptr) and return NULL.

    Since the 'either' doesn't make sense in the case of realloc(), the logical
    conclusion is that the non-NULL return is allowed for malloc() independently
    of how realloc() behaves.

    S

    --
    Stephen Sprunk "Stupid people surround themselves with smart
    CCIE #3723 people. Smart people surround themselves with
    K5SSS smart people who disagree with them." --Aaron Sorkin
     
    Stephen Sprunk, May 17, 2004
    #2
    1. Advertising

  3. Guest

    Stephen Sprunk <> wrote:
    > realloc(ptr, 0) is defined to be equal to free(ptr) and return NULL.


    No, it's nowhere defined like that. It's not even equivalent of free()
    (google for "realloc equivalent free"), although it may be implemented
    this way.

    In C89 there is a clause: " If size is zero and ptr is not a null
    pointer, the object it points to is freed.", but this does not mean
    that realloc(..., 0) merely performs free(); IMO it requires realloc()
    to free the original buffer irrespective of whether subsequent zero-sized
    allocation succeeds or not (in opposition to non-zero size reallocation,
    where the original buffer cannot change on failure). Both C89 and C99
    agree on that, only they're differently worded.

    In general, not freeing realloc(..., 0) result leads to memory leak.

    > Since the 'either' doesn't make sense in the case of realloc(), the logical
    > conclusion is that the non-NULL return is allowed for malloc() independently
    > of how realloc() behaves.


    Provided your assumption was wrong, the conclusion is irrelevant.
    But my question equally might apply to malloc()/calloc() implementation.
    So, can malloc(0)==NULL and calloc(0,0)!=NULL ?

    --
    Stan Tobias
     
    , May 17, 2004
    #3
  4. On Mon, 17 May 2004 wrote:
    >
    > In general, not freeing realloc(..., 0) result leads to memory leak.


    Correct. Implementations are allowed to return NULL from
    realloc(..., 0), in which case the free() does nothing; but they
    are also allowed to return not-NULL, in which case the free() is
    required.

    > But my question equally might apply to malloc()/calloc() implementation.
    > So, can malloc(0)==NULL and calloc(0,0)!=NULL ?


    Sure. Do you have any reason to suspect otherwise? [In
    practice, this will never happen; it's purely a QoI issue.]

    -Arthur
     
    Arthur J. O'Dwyer, May 17, 2004
    #4
  5. Guest

    Arthur J. O'Dwyer <> wrote:

    > On Mon, 17 May 2004 wrote:
    >> But my question equally might apply to malloc()/calloc() implementation.
    >> So, can malloc(0)==NULL and calloc(0,0)!=NULL ?


    > Sure. Do you have any reason to suspect otherwise? [In


    Yes, reading 7.20.3 in n869.txt, indeed I do.

    All sentences in 7.20.3#1 apply equally to all ??alloc() fns. Further,
    "Each such allocation shall yield a pointer to an object disjoint from
    any other object." applies to the *whole group* of ??alloc() fns (ie. I
    need not worry that malloc() and calloc() might overlap). I don't see why
    "If the size of the space requested is zero, [...]" should have different
    context; if it was meant to have, the unambiguous way to express this
    would be to repeat this clause at each ??alloc() description.

    BTW, I don't understand the last sentence there: "The value of a pointer
    that refers to freed space is indeterminate."; where does it apply to?

    > practice, this will never happen; it's purely a QoI issue.]


    Well, it does happen to malloc() vs. realloc(). What I mean to say,
    is that no QoI shall save me any work for portable code.

    The practical difference is for strict error detection. If for zero
    allocation size return value may differ for each ??alloc(), then I have
    to write different error-checking code for each of them.

    For example:

    #if MALLOC_0_NULL
    # define malloc_error(ret, size) (ret == NULL && size > 0)
    #else
    # define malloc_error(ret, size) (ret == NULL)
    #endif

    #if CALLOC_0_NULL
    # define calloc_error(ret, nmemb, size) (ret == NULL && nmemb > 0 && size > 0)
    #else
    # define calloc_error(ret, nmemb, size) (ret == NULL)
    #endif

    #if REALLOC_PTR_0_NULL
    # define realloc_error(ret, ptr, size) \
    (ptr == NULL ? malloc_error(ret, size) : ret == NULL && size > 0)
    #else
    # define realloc_error(ret, ptr, size) \
    (ptr == NULL ? malloc_error(ret, size) : ret == NULL)
    #endif

    (I wrote those from scratch, hope there's no error)

    MALLOC_0_NULL, CALLOC_0_NULL, REALLOC_PTR_0_NULL have to be known for
    every implementation; there seems to be no "general" solution, ie.
    without knowing them.

    --
    Stan Tobias
     
    , May 18, 2004
    #5
  6. Jack Klein Guest

    On 17 May 2004 16:58:48 GMT, wrote in
    comp.lang.c:

    >
    > 7.20.3#1
    > [...] If the size of the space requested is
    > zero, the behavior is implementation-defined: either a null
    > pointer is returned, or the behavior is as if the size were
    > some nonzero value, except that the returned pointer shall
    > not be used to access an object.
    >
    > Then the ??alloc() functions are described in 7.20.3.* .
    >
    > Does the above quote's "either, or" apply to all the ??alloc() functions
    > *together* or *separately*?
    >
    > Example:
    > In GNU libc malloc(0)!=NULL, but realloc(..., 0)==NULL. Is this
    > compliant?
    >
    > TIA


    I've seen several replies and your responses in this thread, so I'll
    just add three points:

    1. Since the behavior is implementation-defined, you should consult
    the implementation's required documentation.

    2. The proper place to ask for definitive interpretation of the
    wording of the standard is news:comp.std.c, not here. Several members
    of the standards committees read and respond there regularly.

    3. N869 is not the standard, and the wording you quote (farther down
    the thread, I admit) is no longer where you found it. It has moved
    somewhere else. But the statement that freeing a pointer renders it
    indeterminate merely provides a way of stating that any use of its
    value, even if not to dereference, produces undefined behavior, as
    does the use of any indeterminate value.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
     
    Jack Klein, May 18, 2004
    #6
  7. On Mon, 18 May 2004 wrote:
    >
    > Arthur J. O'Dwyer <> wrote:
    > > On Mon, 17 May 2004 wrote:
    > > >
    > > > So, can malloc(0)==NULL and calloc(0,0)!=NULL ?

    > >
    > > Sure. Do you have any reason to suspect otherwise? [In

    >
    > Yes, reading 7.20.3 in n869.txt, indeed I do.


    [I'm reading N869, too, BTW. I don't have a copy of the Standard.]

    > All sentences in 7.20.3#1 apply equally to all ??alloc() fns. Further,
    > "Each such allocation shall yield a pointer to an object disjoint from
    > any other object." applies to the *whole group* of ??alloc() fns (ie. I
    > need not worry that malloc() and calloc() might overlap).


    Naturally.

    > I don't see why "If the size of the space requested is zero, [...]"
    > should have different context; if it was meant to have, the unambiguous
    > way to express this would be to repeat this clause at each ??alloc()
    > description.


    Suppose the implementation's default behavior upon malloc(0) is to
    return a newly allocated chunk of memory containing no payload -- just
    a header and footer (speaking from the POV of the malloc implementor,
    not the C programmer). Now suppose I write

    while (1) {
    if (malloc(0) == NULL) break;
    }
    puts("malloc returned NULL");

    Do you see now that at some point 'malloc(0)' *MUST* return NULL,
    no matter what it normally returns?
    Since we don't know when the system will run out of free memory,
    we can never say with certainty when 'malloc' (or 'calloc') will
    return NULL.


    > BTW, I don't understand the last sentence there: "The value of a pointer
    > that refers to freed space is indeterminate."; where does it apply to?


    free(p);
    /* p is now indeterminate, and its value must not be used */
    See the recent thread in Google Groups' archives if you still have
    questions. This is a RAQ (Recently Asked Question). :)


    > > practice, this will never happen; it's purely a QoI issue.]

    >
    > Well, it does happen to malloc() vs. realloc(). What I mean to say,
    > is that no QoI shall save me any work for portable code.


    I meant: If any real implementation made the following code
    print "Yes", then that real implementation would be considered to
    have a poor "Quality of Implementation."

    #include <stdio.h>
    #include <stdlib.h>
    int main(void) {
    if (malloc(0) != NULL) return 0;
    if (calloc(0,0) == NULL) return 0;
    puts("Yes");
    }

    As for "portability," this has little to do with it. See below.

    > The practical difference is for strict error detection. If for zero
    > allocation size return value may differ for each ??alloc(), then I have
    > to write different error-checking code for each of them.
    >
    > For example:
    >
    > #if MALLOC_0_NULL
    > # define malloc_error(ret, size) (ret == NULL && size > 0)
    > #else
    > # define malloc_error(ret, size) (ret == NULL)
    > #endif


    Simply '#define malloc_error(ret,size) (ret == NULL)' and then
    make sure to error-check the input to 'malloc' so that you never
    pass 0 to 'malloc' to begin with. If you're dealing with legacy
    code, rather than writing your own, you can always put

    #define safe_malloc(b) malloc((b)+1) /* guaranteed nonzero */
    #define malloc_error(p, z) (0 == (p)) /* NULL return from malloc */

    and then do a search-and-replace of 'malloc' by 'safe_malloc'.
    But that's a very silly way to ensure portability. It would be
    much better to write sensible, non-zero-allocating code in the
    first place.

    > MALLOC_0_NULL, CALLOC_0_NULL, REALLOC_PTR_0_NULL have to be known for
    > every implementation; there seems to be no "general" solution, ie.
    > without knowing them.


    The general solution is to write code that doesn't depend on
    quirks or corner cases in the implementation.

    HTH,
    -Arthur
     
    Arthur J. O'Dwyer, May 18, 2004
    #7
  8. Guest

    Arthur J. O'Dwyer <> wrote:

    > [I'm reading N869, too, BTW. I don't have a copy of the Standard.]


    Prompted by Jack Klein's answer, I've finally bought the C99 Standard
    myself. Indeed, the wording differs slightly from n869.txt, but it
    chages nothing in the discussion.


    [snip]
    >
    > while (1) {
    > if (malloc(0) == NULL) break;
    > }
    > puts("malloc returned NULL");
    >
    > Do you see now that at some point 'malloc(0)' *MUST* return NULL,
    > no matter what it normally returns?


    But that's an error return, right? All I was saying was in the case
    of *successful* return.


    > > BTW, I don't understand the last sentence there: "The value of a pointer
    > > that refers to freed space is indeterminate."; where does it apply to?

    >

    [snip]
    > questions. This is a RAQ (Recently Asked Question). :)


    I'm sorry. I remember even seeing that discussion in my news-reader.
    Thank you for pointing this out to me, I'll read it later.


    > > > practice, this will never happen; it's purely a QoI issue.]

    > >
    > > Well, it does happen to malloc() vs. realloc(). What I mean to say,
    > > is that no QoI shall save me any work for portable code.

    >
    > I meant: If any real implementation made the following code
    > print "Yes", then that real implementation would be considered to
    > have a poor "Quality of Implementation."

    [snipped code]

    Yes, I agree it would be absurd if malloc(0)!=calloc(0,0). What I wanted
    to point out is that from my POV it might be considered equally absurd
    that malloc(0)!=realloc(p,0). realloc(p,0) is not free(p), it "allocates"
    zero-sized buffer, same way as malloc(0) does. If my interpretation
    was right, then there wouldn't be such asymmetry between strict error
    checking for malloc() and realloc():

    /* works for all ??alloc()s */
    #if ALLOC_0_NULL
    # define alloc_error(ret, size) (ret != NULL && size > 0)
    #else
    # define alloc_error(ret, size) (ret != NULL)
    #endif

    > Simply '#define malloc_error(ret,size) (ret == NULL)' and then
    > make sure to error-check the input to 'malloc' so that you never
    > pass 0 to 'malloc' to begin with. If you're dealing with legacy
    > code, rather than writing your own, you can always put
    >
    > #define safe_malloc(b) malloc((b)+1) /* guaranteed nonzero */
    > #define malloc_error(p, z) (0 == (p)) /* NULL return from malloc */
    >
    > and then do a search-and-replace of 'malloc' by 'safe_malloc'.
    > But that's a very silly way to ensure portability. It would be
    > much better to write sensible, non-zero-allocating code in the
    > first place.


    One way or another, you have to end up writing your own wrapper library
    (that does not even have to depend on the implementation!), which renders
    the Standard somewhat "useless" :)

    Another way is to only check for:
    #define alloc_error(ret, size) (ret == NULL && size > 0)
    ie. always assume malloc(0)==NULL convention. This is what I call relaxed
    error checking, and will work when malloc(0)!=NULL (if the rest of the
    code is portable, it must be prepared for accepting NULL and won't depend
    on distinct returned pointers).

    What I want for now is a portable (by that I understand correct wrt Std)
    code that works with Standard Library; I want to know what the Standard
    actually says, and what is deviation from it. The second motivation
    is simply curiosity.

    Thank you for your answers. Encouraged by Jack Klein (I thank him too)
    I'm going to re-ask this question in CSC.

    --
    Stan Tobias
     
    , May 18, 2004
    #8
    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.

Share This Page