Skip to content(if available)orjump to list(if available)

Some bits on malloc(0) in C being allowed to return NULL

bobmcnamara

Ages ago I worked with a system where malloc(0) incremented a counter and returned -1.

free(-1) decremented the counter.

This way you could check for leaks :p

o11c

Noncompliant, since `malloc(0)` is specified to return a unique pointer if it's not `NULL`.

On most platforms an implementation could just return adjacent addresses from the top half of the address space. On 32-bit platforms it doesn't take long to run out of such address space however, and you don't want to waste the space for a bitmap allocator. I suppose you could just use a counter for each 64K region or something, so you can reuse it if the right number of elements has been freed ...

bobmcnamara

> Noncompliant, since `malloc(0)` is specified to return a unique pointer if it's not `NULL`.

I know I've seen that somewhere, but may I ask what standard you're referring to?

If I recall correctly, this was an archaic stackless microcontroller. The heap support was mostly a marketing claim.

jmgao

C89: https://port70.net/%7Ensz/c/c89/c89-draft.html

If the size of the space requested is zero, the behavior is implementation-defined; the value returned shall be either a null pointer or a unique pointer.

fredoralive

Presumably the ANSI C standard or one of the later editions? They also cover the standard library as well as the language. (Presumably the bit about "Each such allocation shall yield a pointer to an object disjoint from any other object." if the random C99 draft I found via google is accurate to the final standard - I suppose you might question if this special use is technically an allocation of course).

Of course, microcontrollers and the like can have somewhat eccentric implementations of languages of thing and perhaps aren't strictly compliant, and frankly even standard compliant stuff like "int can be 16 bits" might surprise some code that doesn't expect it.

LPisGood

Noncompliant, but what could this reasonably impact?

o11c

Pointers are frequently used as keys for map-like data structures. This introduces collisions that the programmer can't check for, whereas NULL is very often special-cased.

bobmcnamara

> Noncompliant, since `malloc(0)` is specified to return a unique pointer if it's not `NULL`.

I know I've seen that somewhere, but may I ask what standard you're referring to?

sgerenser

I might be missing something, but how does this help in checking for leaks? I mean, I guess you could use it to check for leaks specifically of 0-sized allocations, but wouldn’t it be better just to return NULL and guarantee that 0-sized allocations never use any memory at all?

spacechild1

> but wouldn’t it be better just to return NULL and guarantee that 0-sized allocations never use any memory at all?

I think you are exactly right.

bobmcnamara

At the end of main, if the count wasn't balanced, then you knew you had a mismatch between malloc()/free().

If malloc() had returned a real pointer, you'd have to free that too.

> wouldn’t it be better just to return NULL and guarantee that 0-sized allocations never use any memory at all?

Better: takes less memory Worse: blinds you to this portability issue.

carra

Not the best choice to begin the title with "some bits" in this context. My mind was trying to understand this sentence in a completely different way...

AaronDinesh

Why should it be allowed to return a valid pointers anyways? Surely it should always return NULL?

cjensen

There are three reasonable choices: (a) return the null pointer (b) return a valid unique pointer and (c) abort().

The point of the original C Standard was to make rules about these things AND not break existing implementations. They recognized that (a) and (b) were in existing implementations and were reasonable, and they chose not to break the existing implementations when writing the standard.

This is similar to the extremely unfortunate definition of the NULL macro. There were two existing styles of implementation (bare literal 0 and (void *) 0) and the Standard allows either style. Which means the NULL macro is not entirely safe to use in portable code.

commandlinefan

> return a valid unique pointer

A pointer to what, though? If the requester asked for 0 bytes of memory, you'd either be pointing to memory allocated for another purpose (!) or allocating a few bytes that weren't asked for.

> This makes people unhappy for various reasons

I read through all the links trying to figure out what those reasons might be and came up empty, I'm still curious why anybody would expect or rely on anything except a null pointer in this instance.

DSMan195276

> allocating a few bytes that weren't asked for.

FWIW the alignment guarantees of `malloc()` mean it often will have to allocate more than you ask for (before C23 anyway). You can't 'legally' use this space, but `malloc()` also can't repurpose it for other allocations because it's not suitably aligned.

That said I still agree it's a hack compared to just using `malloc(1)` for this purpose, it's well-defined and functionally equivalent if you're looking for a unique address. The fact that you don't know what `malloc(0)` is going to do makes it pretty useless anyway.

tedunangst

You can copy from a zero sized pointer with memcpy, but not NULL.

snickerbockers

It's not a valid pointer because you can't use the indirection operator on it. Returning a value other than NULL makes sense because an allocation of size zero is still an allocation.

Additionally the actual amount of memory malloc allocates is implementation-defined so long as it is not less than the amount requested, but accessing this extra memory is undefined behavior since processes don't know if it exists or not. a non-NULL return could be interpreted as malloc(0) allocating more than zero bytes.

Some implementations don't actually perform the allocation until theres a pagefault from the process writing to or reading from that memory so in that sense a non-NULL return is valid too.

I'd argue that malloc(0)==NULL makes less sense because there's no distinction between failure and success.

The only real problem is specifying two alternate behaviors and declaring them both to be equally valid.

Joker_vD

For instance, because you are prohibited from passing NULL to e.g. memcpy and lots of other library functions from memory.h/string.h, even when you explicitly specify a size of 0.

Another use was to use it to mint unique cookies/addresses, but malloc(1) works for this just as well.

TZubiri

Mmmmh, cookies