How much memory does malloc(0) allocate?

Discussion in 'C Programming' started by Lynn McGuire, Jul 26, 2013.

  1. Lynn McGuire

    Lynn McGuire Guest

    Lynn McGuire, Jul 26, 2013
    #1
    1. Advertisements

  2. Lynn McGuire

    Eric Sosman Guest

    Not very. The author seems astonished that the Standard leaves
    the matter up to the implementation, and calls it "a silly debate."
    Okay, he's entitled to his opinion -- but "silly debate" isn't an
    attempt to engage in meaningful discussion.
    Sorry: I refuse to participate in Reddit.

    http://www.nytimes.com/2013/07/28/m...spreading-of-a-smear.html?pagewanted=all&_r=0

    The article, IMHO, is *much* too forgiving.
     
    Eric Sosman, Jul 26, 2013
    #2
    1. Advertisements

  3. Barry Schwarz, Jul 26, 2013
    #3
  4. C11 7.22.3p1:

    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.

    There's really not much more to say about it, and well-written code
    shouldn't care whether malloc(0) returns a null pointer or not.
     
    Keith Thompson, Jul 26, 2013
    #4
  5. Lynn McGuire

    Eric Sosman Guest

    In particular, an allocate-or-die function should look like

    void *mallocOrDie(size_t bytes) {
    void *new = malloc(bytes);
    if (new == NULL && bytes > 0)
    die("memory exhausted!");
    return new;
    }

    The second part of the `if' condition is important, because the
    first part alone is not proof of failure.
     
    Eric Sosman, Jul 26, 2013
    #5
  6. Lynn McGuire

    Lew Pitcher Guest

    True, but well-written code should care if malloc(some_int_variable) returns
    a null pointer or not.

    Specifically, "f the space cannot be allocated, a null pointer is
    returned".

    Distinguishing this condition from "f the size of the space requested is
    zero, ... a null pointer is returned ..." may be critical to the program
    code.

    For example, in implementations which return a null pointer from a call to
    malloc(0), the following code

    extern int memsize[];
    extern char *memblock[];
    extern int nmemsize;

    #include <stdlib.h>

    int counter;

    for (counter = 0; counter < nmemsize; ++counter)
    if ((memblock[counter] = malloc(memsize[memblock]) == NULL)
    abort();

    cannot distinguish between a problem with space allocation in malloc(), and
    the (correct) allocation of a block of memory 0 bytes long.

    Of course, the programmer could just code to evaluate the value that would
    later be passed to malloc(), and select the appropriate processing based on
    if it is zero or not.
     
    Lew Pitcher, Jul 26, 2013
    #6
  7. It'a very easy to write this

    IMAGE *makeimage(int width, int height)
    {
    IMAGE *answer = maloc(sizeof(IMAGE));
    if(answer) return 0;
    answer->rgb = malloc(width * heoght * sizeof(PIXEL));
    if(!answer->rgb)
    {
    free(answer);
    return 0;
    }
    answer->width = width;
    answer->height = height;
    return answer;
    }

    But it means that code will behave differently on being asked to create the
    empty image, which won't normally cause a problem, because people seldom use
    the empty image. But eventually that's hard to track portability bug for
    someone.
     
    Malcolm McLean, Jul 26, 2013
    #7
  8. Lynn McGuire

    Siri Cruise Guest

    How much do you want it to allocate? You can always code

    void *lynnmalloc(size_t n) {
    return n==0 ? 0 : malloc(n);
    }

    or

    void *lynnmalloc(size_t n) {
    return n==0 ? malloc(1) : malloc(n);
    }

    And then you always have the answer you want.
     
    Siri Cruise, Jul 26, 2013
    #8
  9. Lynn McGuire

    James Kuyper Guest

    As he said: "well-written code". In this case, well-written implies that
    it must work the same when height==0 || width==0, regardless of how the
    implementation chooses to handle malloc(0). You have two main choices,
    corresponding to the implementation's choices for malloc(0):

    1. make it always act the way it would if malloc(0) were null - you must
    check for height==0 || width==0, and if that is true, fail the same way
    that you would if malloc(0) were null.

    2. make it always act the way it would if malloc(0) were to behave the
    same as malloc(n) for some non-zero value of n: check for height==0 ||
    width == 0, and in that case call malloc(1), rather than malloc(0).

    If you're doing this in two or more different places, it's simpler to
    just write a wrapper for malloc() such that malloc_wrapper(n) handles
    the case n==0 the way that you want it to be treated.
     
    James Kuyper, Jul 26, 2013
    #9
  10. Lynn McGuire

    Ike Naar Guest

    Assuming 'maloc' is a typo for 'malloc'.
    Returns from the function if 'answer' is non-null.
    Otherwise the statement below will be executed with 'answer' being null.
    Kaboom.
     
    Ike Naar, Jul 26, 2013
    #10
  11. Lynn McGuire

    Kleuske Guest

    The wording in the standard above allows malloc(0) to return anything,
    you just should not dereference the result.

    The tricky part is where the standard allows a pointer to be returned,
    but does not allow to dereference said pointer. It "shall" not be
    dereferenced, but you can bet your hiney it will, sooner or later.
    Murphy rules, after all. Thus the pointer returned is a timebomb, an
    accident waiting to happen.

    This leads me to conclude that attempting to allocate zero bytes isn't
    good practice, *especially* when portability is an issue.

    The "bytes > 0" check in the code above prevents ignominious crashes, but
    masks a programming error. Memory isn't exhausted. Someone passed a bad
    argument.

    I would much prefer an solid assert instead.
     
    Kleuske, Jul 26, 2013
    #11
  12. Lynn McGuire

    Lynn McGuire Guest

    The key here is "well-written" code. I daily
    horse around with a 1.6 million line mix of
    Fortran, C and C++. The Fortran code dates
    back to the 1960s and believe you me, it is
    not well written. Pointers (our Fortran uses
    malloc extensively) are rarely checked for
    validity.

    Lynn
     
    Lynn McGuire, Jul 26, 2013
    #12
  13. That last sentence was probably a bit overstated and dismissive of some
    real concerns.

    I think that most code that calls malloc does so in such a way that it
    *cannot* call malloc(0). Typically the argument is some positive
    multiple of sizeof something, and sizeof always yields a positive
    result.

    But if malloc is used to allocate a dynamically sized buffer, it can be
    possible for the size to be 0 -- and in that case, well-written code has
    to take extra care to allow for the implementation-defined result of
    malloc(0). "Well-written code" is code that takes this extra care so
    that it doesn't have to care what malloc(0) returns.

    And yes, this implementation-defined behavior is annoying. If the C
    library were being designed from scratch today, I'm sure that the
    behavior of malloc(0) would be defined one way or the other. It's the
    way it is (presumably) because early pre-standard implementations did
    not behave consistently, and the authors of the standard wanted to avoid
    breaking code that depended on one particular behavior. (Such code was
    already non-portable, but not all C code has to be portable.)
     
    Keith Thompson, Jul 26, 2013
    #13
  14. Lynn McGuire

    James Kuyper Guest

    It occurs to me that there's a third option, not allowed by the standard
    for malloc(), that is available for your wrapper. It relies upon a new
    C2011 feature, max_align_t, to ensure that the pointer is correctly
    aligned for conversion to any type. If the code cannot rely on C2011, a
    more complicated approach is required.

    malloc_wrapper.h:
    #ifndef H_MALLOC_WRAPPER
    #define H_MALLOC_WRAPPER
    #include <stdlib.h>
    extern void * const zero_alloc;
    void *malloc_wrapper(size_t);
    void free_wrapper(void *);
    #endif

    malloc_wrapper.c:
    #include "malloc_wrapper.h"
    #include <stddef.h>
    static max_align_t dummy;
    void * zero_alloc = &dummy;
    void *malloc_wrapper(size_t size)
    {
    return size ? malloc(size) : zero_alloc;
    }
    void free_wrapper(void *ptr)
    {
    if(ptr != zero_alloc)
    free(ptr);
    }

    In the unlikely event that your program makes large numbers of
    zero-sized allocations, the memory not wasted by this wrapper could be
    significant - but you need to be very careful to not call free(p), if
    it's possible that p==zero_alloc.
     
    James Kuyper, Jul 26, 2013
    #14
  15. Lynn McGuire

    Rosario1903 Guest

    i did not read these links...

    for me malloc(0) can not return 0 because
    that return 0 is the error condition

    it has return some good memory;
    for this or return some new memory
    [a "header" of the that point to no memory... but it is memory...]
    or use the same place i imagine something as

    int memvoid[20];
    void *b=(void*)memvoid;

    void* malloc(u32 a)
    {
    if(a==0&& thereIsSomeMem()) return b;
    ....
    }

    void free(void* a)
    {if(a==(void*)memvoid) return;
    ....
    }

    or something as that
     
    Rosario1903, Jul 26, 2013
    #15
  16. Lynn McGuire

    Eric Sosman Guest

    Not quite "anything." If it returns a non-NULL value, the value

    - Must compare unequal to every other valid pointer value,
    - Including NULL
    - Including prior malloc(0) results not yet free()'d

    - Must be convertible to any data pointer type and back again
    to void* without damage

    So, maybe s/anything/anything within reason/ ?
    The C99 Rationale (I haven't seen a C11 version yet) explains
    the Committee's thinking; see section 7.20.3.
    Fine -- But in your usage, the assert should precede the
    call to malloc(), and not depend on the returned value.
     
    Eric Sosman, Jul 26, 2013
    #16
  17. Lynn McGuire

    Rosario1903 Guest

    i forget realloc

    void* realloc(void* p, size_t r)
    {if(p==0||p==(void*)memvoid)
    return malloc(r);
    }
     
    Rosario1903, Jul 26, 2013
    #17
  18. Lynn McGuire

    Rosario1903 Guest

    there would be one "realloc wrapper" too
     
    Rosario1903, Jul 26, 2013
    #18
  19. Lynn McGuire

    Rosario1903 Guest

    i don't understand why
    "Must compare unequal...
    Including prior malloc(0) results not yet free()'d"

    if a=malloc(0) and v=malloc(0)
    why a!=v
    if both a and v point to 0 space mem
    so they have to point mem with address that can be the
    address the restricter type
    the machine can have... here would be double i think
     
    Rosario1903, Jul 26, 2013
    #19
  20. Lynn McGuire

    Eric Sosman Guest

    I think 7.22.3p1 forbids this dodge:

    "If [malloc(0) returns non-null] the behavior is as if
    the size were some nonzero value, [...]"

    and (a few lines earlier)

    "Each such allocation shall yield a pointer to an object
    disjoint from any other object."

    When the size is non-zero, a successful allocation must be
    distinct from all other successful allocations (and from all
    other valid pointers, null and non-null). That's part of the
    behavior of a successful malloc(), and if malloc(0) undertakes
    to imitate that behavior it must imitate the uniqueness, too.

    Thus, in

    void *p = malloc(0);
    void *q = malloc(0);
    assert (p == NULL || p != q);

    .... I believe the assert must not fire.
     
    Eric Sosman, Jul 26, 2013
    #20
    1. Advertisements

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.