Size of Stucture without sizeof()

J

Jack Klein

Richard said:
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.

But first the subtraction has to succeed.
 
J

Jack Klein

You don't need two. You need one plus one byte.

Don't even need the "plus one byte", since the address is legal as
long as it is not dereferenced. And it won't be.
 
S

Suman

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?
 
K

Keith Thompson

Suman said:
Jack Klein wrote: [...]
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?

The subtraction could overflow if the size of *ptr exceeds the maximum
value of ptrdiff_t (which should be possible only for a *very* large
object).

The "%u" format expects an unsigned int; the second argument is of
type ptrdiff_t. If ptrdiff_t happens to be the same size as int, and
the subtraction didn't overflow, it's likely to work; if unsigned int
and ptrdiff_t are of different sizes, arbitrarily bad things can
happen.
 
N

Netocrat

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).
 
C

Christian Bau

Flash Gordon said:
I knew, of course, that you were allowed to generate the address and use
it in pointer arithmetic. It was merely that there had to be a byte I
queried. e.g. I was thinking that on a machine without virtual memory
for one object the address could be one beyond the end of physical
memory, so you could argue there was no byte although there was an address.

There is another problem: If size_t = 32 bit unsigned int, and ptrdiff_t
= 32 bit signed int, then you are in trouble if you have a single object
of three Gigabyte. sizeof will work, but the pointer difference will
invoke undefined behavior.
 
S

Skarmander

Netocrat wrote:
[casting ptrdiff_t for printing]
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).
In C99, don't cast the result at all and print it with the "%tu" specifier.
It is, however, not unlikely that your standard library implementation
doesn't support it, in which case 'unsigned long long' is a good compromise.
(You could cast to uintmax_t too, but this doesn't buy you anything since
such an implementation will not support the requisite "%ju" specifier either.)

S.
 
T

Tim Rentsch

Keith Thompson said:
Flash Gordon said:
Small additional point. Why do you need an additional byte seeing as
there is one exception to pointer arithmetic staying within an object?
:)

(Referring to constructing, but not dereferencing, a pointer just past
the end of an array object.)

The Rationale says:

An important endorsement of widespread practice is the requirement
that a pointer can always be incremented to just past the end of
an array, with no fear of overflow or wraparound:

[example snipped]

This stipulation merely requires that every object be followed by
one byte whose address is representable. That byte can be the
first byte of the next object declared for all but the last object
located in a contiguous segment of memory.

I think the Rationale doesn't quite get this right. [...]

The statement in the Rationale is probably meant to be read as
"This stipulation can be satisfied merely by requiring ...".

Clearly the stipulation can be satisfied in other ways as well.
 
T

Tim Rentsch

Jack Klein said:
Undefined behavior, because even though ptrdiff_t is an
implementation-defined type, it is a signed type and therefore can't
be an unsigned int suitable for the "%u" conversion specifier. And it
might be larger than int.

The ptrdiff_t type might be larger than int; but if it isn't then the
"%u" conversion specifier is suitable (and defined behavior) as long
as the value is between 0 and UINT_MAX. See 7.15.1.1 p2.
 
T

Tim Rentsch

Keith Thompson said:
Suman said:
Jack Klein wrote: [...]
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?

The subtraction could overflow if the size of *ptr exceeds the maximum
value of ptrdiff_t (which should be possible only for a *very* large
object).

The "%u" format expects an unsigned int; the second argument is of
type ptrdiff_t. If ptrdiff_t happens to be the same size as int, and
the subtraction didn't overflow, it's likely to work; if unsigned int
and ptrdiff_t are of different sizes, arbitrarily bad things can
happen.

If ptrdiff_t is no larger than int (and the subtraction didn't
overflow), it will work as long as the value is between 0 and
UINT_MAX. 6.5.2.2 p6,p7; 7.15.1.1 p2.
 
N

Netocrat

Netocrat wrote:
[casting ptrdiff_t for printing]
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).
In C99, don't cast the result at all and print it with the "%tu" specifier.

Even better.
It is, however, not unlikely that your standard library implementation
doesn't support it, in which case 'unsigned long long' is a good
compromise.

Yes, 'unsigned long' is not guaranteed to work in C99 but I've just
noticed that Defect Report #204 caused 7.17#4 to be added:

| The types used for size_t and ptrdiff_t should not have an integer
| conversion rank greater than that of signed long unless the
| implementation supports objects large enough to make this necessary.

[...]
 
S

Skarmander

Netocrat said:
Netocrat wrote:
[casting ptrdiff_t for printing]
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).
In C99, don't cast the result at all and print it with the "%tu" specifier.

Even better.
It is, however, not unlikely that your standard library implementation
doesn't support it, in which case 'unsigned long long' is a good
compromise.

Yes, 'unsigned long' is not guaranteed to work in C99 but I've just
noticed that Defect Report #204 caused 7.17#4 to be added:

| The types used for size_t and ptrdiff_t should not have an integer
| conversion rank greater than that of signed long unless the
| implementation supports objects large enough to make this necessary.

Yes, but of course it is actually possible for an implementation to have
32-bit longs while supporting over 2 GiB of memory through bigger pointers
and bigger size_t and ptrdiff_t types (If I'm not mistaken there already are
such implementations.) A program can't count on it. This recommendation was
only added to ensure programs don't gratuitously break on platforms that
don't strictly need bigger types.

But obviously, if you know all your pointer subtractions will fit in 32 bits
(which is often the case) you can "get away" with 'unsigned long' all the
time. That's why you can hardly call it "wrong".

S.
 
T

Tim Rentsch

Robotnik said:
Hello All,

I want to know if we could know the size of a structyure
without the use of sizeof().
Any hints.

I'd like to offer a suggestion, namely, to add this question to the
FAQ. Then in response to such postings, the rejoinder could be simply
"It's in the FAQ; please read that." It would save time in
responding about CLC'ers not doing homework, and would also result in
the OP becoming more informed about C in many cases.

Naturally, responses saying it's in the FAQ normally shouldn't include
the section number for questions thought to be homework questions.
 
E

Emmanuel Delahaye

Robotnik a écrit :
I want to know if we could know the size of a structyure
without the use of sizeof().

Nope. And BTW, why such a question ? sizeof is a built-in C-operator.
 
R

Robotnik

Robotnik a écrit :
Nope. And BTW, why such a question ? sizeof is a built-in C-operator.
Hello All,
Between you and me we coulnt find the answer it is a
pity.This question was asked to me in a wipro interview( www.wipro.com
),I dwelled on it for 6 months together before putting it across to you
all. Does "size_t" provide a solution.if somebody wants to build a
colpiler right from assembly language what should he do.Do reply.
 
K

Keith Thompson

Robotnik said:
Robotnik a écrit :
Hello All,
Between you and me we coulnt find the answer it is a
pity.This question was asked to me in a wipro interview( www.wipro.com
),I dwelled on it for 6 months together before putting it across to you
all.

I thought that several valid answers were posted on this very thread.
It's not possible to create something that can be used in place of
sizeof in all contexts, but it's certainly possible (though useless)
to determine the size of an object without sizeof. Look for "pointer
arithmetic" in this thread.
Does "size_t" provide a solution.

size_t is a type. It happens to be the size yielded by the sizeof
operator, and it should probably be used in any silly non-sizeof
solution. Beyond that, I don't even know what the question means.
if somebody wants to build a
colpiler right from assembly language what should he do.Do reply.

What should he do about what?

If you're writing your own compiler, the compiler gets to decide how
big a type needs to be (within the constraints imposed by the
language). If it's a C compiler, it will of course have to implement
the sizeof operator. If you're asking how sizeof is implemented in a
C compiler, that's going to depend strongly on the internal details of
how the compiler is implemented.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top