Jack said:
Richard Bos wrote:
Santhosh wrote:
All the major contributors to clc have said that any approach to do
what OP asked without using sizeof is silly.
[...]
printf("%u\n", (char *)(ptr+1) - (char *)ptr);
Its not silly.Its plain UB.
Is it? Yes, if size_t is not an unsigned int, but that's hardly
relevant, is it? If we correct that, how is this UB?
printf("%lu\n", (unsigned long) ((char *)(ptr+1)-(char *)ptr));
Richard
What it has to do with size_t or unsigned type? Pointer subtraction
gives you value of type ptrdiff_t which is signed integer type.
Krishanu
And in this case it is known to be positive, and thus it is safe to cast
it to an unsigned type. Given the use of an unsigned type, even overflow
can't cause UB.
Don't be silly, of course it can. The subtraction of the two pointer
types yields a ptrdiff_t result, if it is within the range of a
ptrdiff_t. If not it results in undefined behavior. Only after the
subtraction is performed and the result fits, then the cast to an
unsigned type is well-defined, even if the result is greater than
ULONG_MAX.
I am sufficiently confused now to warrant further explanation.
Is the following strictly speaking, okay then?
printf("%u\n", (char *)(ptr+1) - (char *)ptr);
Or am I missing something here?
It's not OK. To expand on Jack's explanation:
Firstly there's potential UB on the pointer subtraction (it's only defined
if the result <= PTRDIFF_MAX):
N1124, 6.5.6#9
| When two pointers are subtracted... If the result is not
| representable in an object of [type ptrdiff_t], the behavior is
| undefined.
Secondly, ptrdiff_t is a signed type whereas %u denotes an unsigned
argument (this is only a style argument; by 6.2.5#9 a positive signed
integer is interchangeable with an unsigned integer of the same type and
we know that the difference is positive).
Thirdly there's potential UB if the subtraction results in a value
greater than UINT_MAX. This is possible if e.g. ptrdiff_t is of type
long, and has larger positive range and size than type unsigned int. It
could be UB when the integer promotions (6.3.1.1) for a large difference
result in a long type, which is larger than the unsigned int that printf
is expecting.
Casting the pointer difference to unsigned int and changing the specifier
to %u solves the second and third problems and ensures that overflow is
defined, but not the first.
A more complete solution that avoids wrapping on overflow is to cast to
unsigned long and use %lu under C90; under C99 unsigned long long and %llu
would be required (this still doesn't avoid the potential for the pointer
subtraction to result in UB).