Robin Haigh said:
6.3.2.3 p5 says an integer may be converted to a pointer type. The result
is implementation-defined.
This doesn't mean undefined behaviour: it means the implementation is
required to make it work and tell you how.
Right, but here's the whole of 6.3.2.3p5:
An integer may be converted to any pointer type. Except as
previously specified, the result is implementation-defined, might
not be correctly aligned, might not point to an entity of the
referenced type, and might be a trap representation.
and a footnote:
The mapping functions for converting a pointer to an integer or an
integer to a pointer are intended to be consistent with the
addressing structure of the execution environment.
The document where you saw this is discussing the multithreading extension
in a particular implementation. The code relies on other features of the
implementation, but that doesn't make it any more non-portable than it
already is.
I suspect there's no intention of using the return value as a pointer. It
probably does something like
retp = func();
if (retp == (void *)-1) {
/* handle special case */
} else {
/* retp is a normal pointer */
}
This isn't as elegant as some people might like, but it's fine
If retp happens to be a trap representation, evaluating it for the
purpose of comparing it to (void*)-1 invokes undefined behavior. For
that matter, assigning the result of func() to retp in the first place
probably invokes undefined behavior.
The code is portable only to systems on which the conversion doesn't
yield a trap representation (which may well be all the systems on
which the code is expected to run).
<OFF_TOPIC>
I think POSIX has at least one function that uses -1 converted to a
pointer type as a special return value.
</OFF_TOPIC>
Let's look at the original problem. You have a function that normally
returns a pointer value (void*), but sometimes you want an integer
value. There are at least two cases to consider.
If you want a single, or a small number of, distinctive values other
than NULL, using (void*)-1 can work on many platforms, but strictly
speaking it's non-portable (and frankly it's ugly).
A portable solution is to declare an otherwise-unused object and use
its address as a special value.
If you just want to return either a pointer or a arbitrary integer
value, then you probably have a poorly designed interface. The
function can return a record (or a union) containing both a pointer
and an integer, and some indication of which is valid. Or it can
return extra information indirectly via a parameter. And so forth.
Or if the caller can know in advance whether the result is going to be
a pointer or an integer, perhaps there should be two separate
functions.