pointer conversion

J

junky_fellow

guys,

I have a question regarding pointer conversion. Please look at the
following code snippet.

char *cptr;
int *iptr;

/* Some code here that initializes "iptr" */

cptr = (char *)iptr; /* Line 1
*/
cptr = cptr + sizeof(int); /* Line 2 */
iptr = (int *)cptr; /* Line 3 */

Now as per the pointer conversion rule:
Page 47: n1124.pdf

A pointer to an object or incomplete type may be converted to a
pointer to a different object or incomplete type. If the resulting
pointer is not correctly aligned57) for thepointed-to type, the
behavior is undefined. Otherwise, when converted back again, the
result shall compare equal to the original pointer.

According to above conversion rule, "Line 1" should be perfectly fine.
But, I want to know if the conversion done at "Line 3" is allowed or
not.
I have this doubt because the "cptr" has been changed.
But, it is properly aligned to point to an integer object.
 
M

Malcolm McLean

guys,

I have a question regarding pointer conversion. Please look at the
following code snippet.

char *cptr;
int *iptr;

/* Some code here that initializes "iptr" */

cptr = (char *)iptr; /* Line 1
*/
cptr = cptr + sizeof(int); /* Line 2 */
iptr = (int *)cptr; /* Line 3 */

Now as per the pointer conversion rule:
Page 47: n1124.pdf

A pointer to an object or incomplete type may be converted to a
pointer to a different object or incomplete type. If the resulting
pointer is not correctly aligned57) for thepointed-to type, the
behavior is undefined. Otherwise, when converted back again, the
result shall compare equal to the original pointer.

According to above conversion rule, "Line 1" should be perfectly fine.
But, I want to know if the conversion done at "Line 3" is allowed or
not.
I have this doubt because the "cptr" has been changed.
But, it is properly aligned to point to an integer object.
You will be OK.
char is always 1 byte. So casting an arbitrary pointer to a char *, adding
an exact multiple of the size of the original type, and casting back is
guaranteed to preserve alignment.
 
C

Charlton Wilbur

M> You will be OK. char is always 1 byte. So casting an arbitrary
M> pointer to a char *, adding an exact multiple of the size of
M> the original type, and casting back is guaranteed to preserve
M> alignment.

I am not so sure about that; would you care to cite C&V, please, if
you claim that it's guaranteed by the standard?

Charlton
 
M

Malcolm McLean

Charlton Wilbur said:
M> You will be OK. char is always 1 byte. So casting an arbitrary
M> pointer to a char *, adding an exact multiple of the size of
M> the original type, and casting back is guaranteed to preserve
M> alignment.

I am not so sure about that; would you care to cite C&V, please, if
you claim that it's guaranteed by the standard?
Arrays have to be contiguous in memory. No padding bytes may be inserted
between items.
The rest follows from that.
 
R

Richard Tobin

M> You will be OK. char is always 1 byte. So casting an arbitrary
M> pointer to a char *, adding an exact multiple of the size of
M> the original type, and casting back is guaranteed to preserve
M> alignment.
I am not so sure about that; would you care to cite C&V, please, if
you claim that it's guaranteed by the standard?
He's wrong: it's not guaranteed. Simple example:

int target = 42;
int *ptr = &target + 1; /* "an arbitrary pointer" */
ptr = (int*)((char*)ptr + sizeof *ptr); /* U.B. */

If the original pointer points at an actual object of its
type (so it's not "arbitrary"), the conversion is safe.[/QUOTE]

That seems to be excessive pedantry. He said it "preserves
alignment", not that it's legal. Presumably you would deny that
adding 2 to an int preserves it odd/even parity, because you might
choose INT_MAX.

-- Richard
 
E

Eric Sosman

Charlton said:
M> You will be OK. char is always 1 byte. So casting an arbitrary
M> pointer to a char *, adding an exact multiple of the size of
M> the original type, and casting back is guaranteed to preserve
M> alignment.

I am not so sure about that; would you care to cite C&V, please, if
you claim that it's guaranteed by the standard?

He's wrong: it's not guaranteed. Simple example:

int target = 42;
int *ptr = &target + 1; /* "an arbitrary pointer" */
ptr = (int*)((char*)ptr + sizeof *ptr); /* U.B. */

If the original pointer points at an actual object of its
type (so it's not "arbitrary"), the conversion is safe.
 
C

CBFalconer

Malcolm said:
.... snip ...

Arrays have to be contiguous in memory. No padding bytes may be
inserted between items. The rest follows from that.

Nonsense. Consider:

struct foo {int iv; char ch} arr[N];

A struct foo, on a machine with sizeof int == 4, will be occupy 5
bytes. However, in order to make addressable arrays of these,
sizeof struct foo == 8 (assuming ints need to be aligned modulo
4). I.E. there are 3 padding bytes per item.
 
M

Malcolm McLean

He's wrong: it's not guaranteed. Simple example:

int target = 42;
int *ptr = &target + 1; /* "an arbitrary pointer" */
ptr = (int*)((char*)ptr + sizeof *ptr); /* U.B. */

If the original pointer points at an actual object of its
type (so it's not "arbitrary"), the conversion is safe.

That seems to be excessive pedantry. He said it "preserves
alignment", not that it's legal. Presumably you would deny that
adding 2 to an int preserves it odd/even parity, because you might
choose INT_MAX.
[/QUOTE]
Yes, to be a successful pedant you've got to be absolutely, precisely
accurate.

Clearly if the pointer is misaligned, adding sizeof(*ptr) will preserve the
(mis) alignment.
 
E

Eric Sosman

Richard said:
He's wrong: it's not guaranteed. Simple example:

int target = 42;
int *ptr = &target + 1; /* "an arbitrary pointer" */
ptr = (int*)((char*)ptr + sizeof *ptr); /* U.B. */

If the original pointer points at an actual object of its
type (so it's not "arbitrary"), the conversion is safe.

That seems to be excessive pedantry. He said it "preserves
alignment", not that it's legal. Presumably you would deny that
adding 2 to an int preserves it odd/even parity, because you might
choose INT_MAX.[/QUOTE]

Adding two to an "arbitrary" integer need not preserve
parity, although adding two to most integers does. For the
two problematic integers you get undefined behavior, after
which assertions about what is and isn't preserved -- or
pickled -- just evaporate.

Adding sizeof *ptr to an "arbitrary" value of ptr need
not preserve alignment, although adding it to a restricted
class of values certainly does. But once U.B. occurs, the
Standard abdicates and no longer supports arguments that
formerly relied on it. It is wrong to claim that alignment
preservation is "guaranteed" in light of U.B.; "guaranteed"
by whom or by what?

BTW, "excessive pedantry" is unnecessarily redundant. ;-)
 
R

Richard Tobin

Malcolm McLean said:
Yes, to be a successful pedant you've got to be absolutely, precisely
accurate.

Clearly if the pointer is misaligned, adding sizeof(*ptr) will preserve the
(mis) alignment.

No. If you're a real pedant, you will observe that the undefined
behaviour of creating the misaligned pointer could be that the addition
produces an aligned one.

But the example wasn't a misaligned pointer - it was that the addition
produced a pointer outside of the object.

-- Richard
 
M

Malcolm McLean

Richard Tobin said:
No. If you're a real pedant, you will observe that the undefined
behaviour of creating the misaligned pointer could be that the addition
produces an aligned one.
I surrender. Yes, the misaligned pointer can only be assigned to a pointer
of that type with UB, after which any futher operations are allowed to
produce any results whatsoever.
But the example wasn't a misaligned pointer - it was that the addition
produced a pointer outside of the object.
That's an example of useful pedantry. I should have specified that the
operation is not safe, if the new pointer cannot be contained in the old
object.
 
R

Richard Tobin

Arrays have to be contiguous in memory. No padding bytes may be
inserted between items. The rest follows from that.
[/QUOTE]
Nonsense. Consider:

struct foo {int iv; char ch} arr[N];

A struct foo, on a machine with sizeof int == 4, will be occupy 5
bytes. However, in order to make addressable arrays of these,
sizeof struct foo == 8 (assuming ints need to be aligned modulo
4). I.E. there are 3 padding bytes per item.

But these padding bytes are part of the items, not between them.
sizeof(struct foo) has to be 8 regardless of whether the struct is in
an array. If it's in another struct, the 3 padding bytes have to be
there even if the next item in the struct is a char. Even if it's
just a plain automatic variable, the bytes have to be present and
not used for another object, because you could memcpy() 8 bytes
to it.

-- Richard
 
C

CBFalconer

Eric said:
.... snip ...

Adding two to an "arbitrary" integer need not preserve parity,
although adding two to most integers does. For the two problematic
integers you get undefined behavior, after which assertions about
what is and isn't preserved -- or pickled -- just evaporate.

Oh? When I was in primary school:

4 had 1 bit
4 + 2 is 6, with 2 bits
6 + 2 is 8, with 1 bit

which doesn't seem to preserve parity.
 
K

Keith Thompson

CBFalconer said:
Malcolm McLean wrote:
... snip ...
Arrays have to be contiguous in memory. No padding bytes may be
inserted between items. The rest follows from that.

Nonsense. Consider:

struct foo {int iv; char ch} arr[N];

A struct foo, on a machine with sizeof int == 4, will be occupy 5
bytes. However, in order to make addressable arrays of these,
sizeof struct foo == 8 (assuming ints need to be aligned modulo
4). I.E. there are 3 padding bytes per item.

No, there are 3 padding bytes *within* each item.

Assumptions:
sizeof(int) == 4
int requires 4-byte alignment.
The implementation inserts no more padding in structs than is
necessary to meet alignment requirements.

Then sizeof(struct foo) == 8, which includes 3 padding bytes (probably
after ch, but they could be before it or both given a sufficiently
whimsical compiler).

There is no padding between elements of an array. In this example,
each element occupies a full 8 bytes, with no additional padding
either necessary or permitted.

I've used languages which do allow padding between array elements. In
such a language, sizeof(struct foo) might be 5 bytes, but
sizeof(struct foo[1]) might be 8 bytes. In C, though, a single object
of type struct foo must occupy 8 bytes, whether it's an array element
or not. (Actually, any single object is effectively an array
element.)
 
R

Richard Tobin

CBFalconer said:
Oh? When I was in primary school:

4 had 1 bit
4 + 2 is 6, with 2 bits
6 + 2 is 8, with 1 bit

which doesn't seem to preserve parity.

Oops. I was using the term "parity" in the mathematical sense of
whether a number is odd or even; not in the computer sense of whether
it has an odd or even number of bits set. I used the phrase "odd/even
parity" to emphasize the kind of parity I meant, but of course it does
nothing of the kind.

-- Richard
 
C

CBFalconer

Richard said:
Nonsense. Consider:

struct foo {int iv; char ch} arr[N];

A struct foo, on a machine with sizeof int == 4, will be occupy 5
bytes. However, in order to make addressable arrays of these,
sizeof struct foo == 8 (assuming ints need to be aligned modulo
4). I.E. there are 3 padding bytes per item.

But these padding bytes are part of the items, not between them.
sizeof(struct foo) has to be 8 regardless of whether the struct is in
an array. If it's in another struct, the 3 padding bytes have to be
there even if the next item in the struct is a char. Even if it's
just a plain automatic variable, the bytes have to be present and
not used for another object, because you could memcpy() 8 bytes
to it.

If you think about it for about 2 1/4 secs I am sure you can come
up with a struct definition that puts the padding in the middle.
Don't forget that ALL items have to be aligned.
 
K

Keith Thompson

CBFalconer said:
Richard said:
CBFalconer said:
Arrays have to be contiguous in memory. No padding bytes may be
inserted between items. The rest follows from that.
Nonsense. Consider:

struct foo {int iv; char ch} arr[N];

A struct foo, on a machine with sizeof int == 4, will be occupy 5
bytes. However, in order to make addressable arrays of these,
sizeof struct foo == 8 (assuming ints need to be aligned modulo
4). I.E. there are 3 padding bytes per item.

But these padding bytes are part of the items, not between them.
sizeof(struct foo) has to be 8 regardless of whether the struct is in
an array. If it's in another struct, the 3 padding bytes have to be
there even if the next item in the struct is a char. Even if it's
just a plain automatic variable, the bytes have to be present and
not used for another object, because you could memcpy() 8 bytes
to it.

If you think about it for about 2 1/4 secs I am sure you can come
up with a struct definition that puts the padding in the middle.
Don't forget that ALL items have to be aligned.

So what? The point is that padding may be inserted between members of
a struct, but padding *may not* be inserted between elements of an
array.

Any padding in struct foo, whether it's between members, after the
last member, or both, is *part of struct foo*.

Given
struct foo arr[N];
any padding between arr[0] and arr[1] is guaranteed to be nonexistent.

If this weren't the case, then the common idiom
sizeof arr / sizeof arr[0]
to determine the length of an array would not work.

The statement that you disputed, quoted above, was

Arrays have to be contiguous in memory. No padding bytes may be
inserted between items. The rest follows from that.

That statement was perfectly correct.
 
B

Barry Schwarz

M> You will be OK. char is always 1 byte. So casting an arbitrary
M> pointer to a char *, adding an exact multiple of the size of
M> the original type, and casting back is guaranteed to preserve
M> alignment.

I am not so sure about that; would you care to cite C&V, please, if
you claim that it's guaranteed by the standard?

It follows directly from paragraph 2 in section 6.5.2.1 and the first
few sentences of paragraph 8 in section 6.5.6 of n1124.

While the arithmetic is guaranteed to preserve alignment, it can still
invoke undefined behavior. If the resulting address is not within (or
just 1 past the end of) the original object, the next to last sentence
of paragraph 8 of section 6.5.6 applies.


Remove del for email
 
C

CBFalconer

Keith said:
CBFalconer said:
Richard said:
Arrays have to be contiguous in memory. No padding bytes may be
inserted between items. The rest follows from that.

Nonsense. Consider:

struct foo {int iv; char ch} arr[N];

A struct foo, on a machine with sizeof int == 4, will be occupy 5
bytes. However, in order to make addressable arrays of these,
sizeof struct foo == 8 (assuming ints need to be aligned modulo
4). I.E. there are 3 padding bytes per item.

But these padding bytes are part of the items, not between them.
sizeof(struct foo) has to be 8 regardless of whether the struct is in
an array. If it's in another struct, the 3 padding bytes have to be
there even if the next item in the struct is a char. Even if it's
just a plain automatic variable, the bytes have to be present and
not used for another object, because you could memcpy() 8 bytes
to it.

If you think about it for about 2 1/4 secs I am sure you can come
up with a struct definition that puts the padding in the middle.
Don't forget that ALL items have to be aligned.

So what? The point is that padding may be inserted between members of
a struct, but padding *may not* be inserted between elements of an
array.

Any padding in struct foo, whether it's between members, after the
last member, or both, is *part of struct foo*.

Given
struct foo arr[N];
any padding between arr[0] and arr[1] is guaranteed to be nonexistent.

If this weren't the case, then the common idiom
sizeof arr / sizeof arr[0]
to determine the length of an array would not work.

The statement that you disputed, quoted above, was

Arrays have to be contiguous in memory. No padding bytes may be
inserted between items. The rest follows from that.

That statement was perfectly correct.

No, because you are considering the structure to be a monolith,
while I am looking at it as consisting of some sort of combination
of items, possibly separated by padding. If your quoted line said
"between array items" I would have to agree.

It really is counting angels on pinheads, and not important.
Meanwhile we amuse ourselves.
 

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,992
Messages
2,570,220
Members
46,805
Latest member
ClydeHeld1

Latest Threads

Top