Re: Size of Stucture without sizeof()



On Thu, 01 Dec 2005 21:01:34 -0800, Suman wrote:
> Jack Klein wrote:
>> On Thu, 1 Dec 2005 18:16:04 +0000 (UTC), Jordan Abel
>> <jmabel@xxxxxxxxxx> wrote in comp.lang.c:
>> > On 2005-12-01, Krishanu Debnath <krishanu@xxxxxxxxxxxxxxxxxx> wrote:
>> > > Richard Bos wrote:
>> > >> "Suman" <skarpio@xxxxxxxxx> 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).

--
http://members.dodo.com.au/~netocrat
.



Relevant Pages