A zero-sized object is immutable, since it has no bits to mutate. The pointer cannot be dereferenced. It may be compared to other pointers. So the question is: do you want to obtain unique zero-sized objects, or are you okay with there being one representative instance of all of them? If you're okay with one, you can just call SANE_REALLOC_EMPTY that object. I called it EMPTY on purpose; it represents an empty object with no bits.
If you want unique zero-sized objects, you can always simulate them with objects of size 1. The allocator API doesn't have to make that choice internally for you.
In a Lisp implementation, I could use unique, zero sized objects to implement symbols. The gensym function would create a new one.
Zero-sized distinguishable objects can have properties attached to them via hashes. At the API level, you cannot tell how a property (e.g. symbol-name) is attached to an object.
Properties, like the symbol name, would be attached to these objects via hashes.
It's not an ideal representation for symbols when all symbols have a certain property, like name.
If you have objects such that they have certain properties, but the properties are rare (only a few objects have a value of the property that requires storing space, and for the rest it is some "undefined" value, you can achieve a sparse representation by making the objects as small as possible, attaching the properties externally.
If you want unique zero-sized objects, you can always simulate them with objects of size 1. The allocator API doesn't have to make that choice internally for you.