void * and explicit cast

R

regis

Hello,
In the rationale, section [3.2.2.3 Pointers] states:

====
# A pointer to void may be converted
to a pointer to an object of any type.

# A pointer to any object of any type may be converted
to a pointer to void.

# If a pointer to an object is converted to a pointer to void and back
again to the original pointer type, the result compares equal to
original pointer.

# It is invalid to convert a pointer to an object of any type to a
pointer to an object of a different type without an explicit cast.
====

Does the last item imply that the following is invalid ?

int i= 666,
int * pi= & i;
void * pv= pi;
unsigned char * puc= pv;

At the sight of the first two items, I would say it is legal,
but the last item makes me doubt...
 
M

maverik

Hello,
In the rationale, section [3.2.2.3 Pointers] states:

====
# A pointer to void may be converted
to a pointer to an object of any type.

# A pointer to any object of any type may be converted
to a pointer to void.

# If a pointer to an object is converted to a pointer to void and back
again to the original pointer type, the result compares equal to
original pointer.

# It is invalid to convert a pointer to an object of any type to a
pointer to an object of a different type without an explicit cast.
====

Does the last item imply that the following is invalid ?

int i= 666,
int * pi= & i;
void * pv= pi;
unsigned char * puc= pv;

At the sight of the first two items, I would say it is legal,
but the last item makes me doubt...

No, it says that this:

int i = 10;
int *pInt = &i;
unsigned char *pUChar = pInt;

is invalid. You should use explicit cast:

unsigned char *pUChar = (unsigned char *)pInt;
 
R

Richard Tobin

Jujitsu Lizard said:
But MOST IMPORTANTLY they do not say that converting from (int *) to (void
*) to (unsigned char *) will result in a pointer that is properly aligned
for an unsigned char.

In C, chars are bytes. So character pointers are unrestricted in the
alignment of what they point to; all memory in C can be accessed
through character pointers.

Also, pointers to character types have the same representation
as pointers to void, so the conversion is a no-op.

-- Richard
 
J

jameskuyper

Jujitsu said:
regis said:
Hello,
In the rationale, section [3.2.2.3 Pointers] states:

====
# A pointer to void may be converted
to a pointer to an object of any type.

# A pointer to any object of any type may be converted
to a pointer to void.

# If a pointer to an object is converted to a pointer to void and back
again to the original pointer type, the result compares equal to original
pointer.

# It is invalid to convert a pointer to an object of any type to a pointer
to an object of a different type without an explicit cast.
====

Does the last item imply that the following is invalid ?

int i= 666,
int * pi= & i;
void * pv= pi;
unsigned char * puc= pv;

At the sight of the first two items, I would say it is legal,
but the last item makes me doubt...

You may need to add a cast on the last statement. But that isn't the most
important point about the code you posted ...

The paragraphs you cited say that you can convert from (int *) to (void *)
and back to (int *) and have the same value.

But they do not say that you can convert from (int *) to (void *) to
(unsigned char *) and have the same value.

True, but his code does not check or require that they have the same
value. Realistically, the standard is more vague on this point than
real-world compilers, which (almost?) universally would produce a
pointer pointing at the first byte of the object. In doing so, I
believe that they reflect the intent of the committee, even if the
committee failed to express that intent clearly in the standard
itself.
But MOST IMPORTANTLY they do not say that converting from (int *) to (void
*) to (unsigned char *) will result in a pointer that is properly aligned
for an unsigned char.

Those are important points to make, in general; however, unsigned
char* cannot have alignment restrictions.
Functions like malloc() are guaranteed to return a pointer that is suitable
for the alignment of any data type. However, taking the address of an
integer is not guaranteed to return a pointer suitable for alignment of any
other data type.

Except for those data types, like unsigned char, where sizeof(type)=1,
which are not allowed to have alignment restrictions; otherwise arrays
of those data types (and the corresponding rules for pointer
arithmetic) could not work as described by the standard.
 
J

jameskuyper

regis said:
Hello,
In the rationale, section [3.2.2.3 Pointers] states:

====
# A pointer to void may be converted
to a pointer to an object of any type.

# A pointer to any object of any type may be converted
to a pointer to void.

# If a pointer to an object is converted to a pointer to void and back
again to the original pointer type, the result compares equal to
original pointer.

# It is invalid to convert a pointer to an object of any type to a
pointer to an object of a different type without an explicit cast.
====

Does the last item imply that the following is invalid ?

int i= 666,
int * pi= & i;
void * pv= pi;
unsigned char * puc= pv;

At the sight of the first two items, I would say it is legal,
but the last item makes me doubt...

What the rationale says is only a simplified summary of what the
standard says. It is not authoritative. Since pv points at the
location of 'i', which is an object, and not an object of unsigned
char type, the third rule quoted might seem to apply, but you have to
check the actual standard to be sure.

You might expect that the rules governing implicit conversions would
be somewhere near the place where the term is defined. However, aside
from the "Usual Arithmetic Conversions" (6.3.1.8), and the "Default
Argument Promotions" (6.5.2.2p6) (neither of which are relevant to
this case), implicit conversions occur only when using the assignment
operator, during initialization of an object, when passing arguments
to a function, and when returning a value from a function. In those
last three cases, the phrase "as if by assignment" is used to cross-
reference the rules for simple-assignment.

Section 6.5.16.1p1 lists the constraints on using the simple
assignment operator:
"One of the following shall hold:
....
— both operands are pointers to qualified or unqualified versions of
compatible types,
and the type pointed to by the left has all the qualifiers of the type
pointed to by the
right;
— one operand is a pointer to an object or incomplete type and the
other is a pointer to a
qualified or unqualified version of void, and the type pointed to by
the left has all
the qualifiers of the type pointed to by the right;
...."

That section also lists three other options not relevant to your
question.

The first two rules you quoted correspond to the second option that I
quoted. The third rule that you quoted corresponds to the fact that
neither of the two options I've quoted covers the case when the two
pointers point to incompatible types.

Notice that the constraints I've quoted are not give in terms of what
location a pointer points at. They are based upon whether or not the
pointer points at an object type. pv points at 'void', which is not an
object type, it is an incomplete type. As such, puc=pv is covered by
the second option I quoted, and is therefore not a constraint
violation.
 
J

jameskuyper

pete said:
I think it *is* clear.

ISO/IEC 9899:1999 (E)
6.3.2.3 Pointers
7
When a pointer to an object is
converted to a pointer to a character type,
the result points to the lowest addressed byte of the object.

pv is a pointer to void. "void" is an incomplete type, not an object
type, so pv does not point to an object. It points to a location in
memory, but the standard doesn't specify which location it points at
(we all "know" where it points, but the standard fails to say so). If
the standard guaranteed some kind of chain rule for pointer
conversions: (A*)(B*)p == (A*)p, that would be the missing link in the
argument; but it doesn't. In general, two-step pointer conversions
have a standard-defined result only when they are conversion pairs
that bring a pointer value back to its original type. The exceptions
are conversion pairs where the intermediate type is a pointer to a
character type, or where one of the conversions converts a pointer to
a struct type into a pointer to the type of it's first member, or vice
versa.

I understand what most (all?) implementations do with such code, and
I'm sure that what those implementations do reflect the intent of the
committee; I just think that the committee failed to correctly express
that intent.
 
C

CBFalconer

blargg said:
.... snip ...

So does this mean it's not even possible to portably implement
memcpy?!? How would you get a pointer to the first bytes (char)
of the object addresses passed?

What's the problem?

void *memcpy(void * restrict s1,
const void * restrict s2,
size_t n) {
char *dest = s1;
const char * restrict src = s2; /* lo - all converted */

while (n--) *dest++ = *src++;
return s1;
} /* should do it */
 
H

Harald van Dijk

What's the problem?

void *memcpy(void * restrict s1,
const void * restrict s2,
size_t n) {
char *dest = s1;

The problem is that dest needn't point to the same object as what you
passed in, apparently. This was clear from the context, and enough was
properly quoted in the message you replied to, but you didn't keep it in.
 
J

James Kuyper

blargg said:
....
So does this mean it's not even possible to portably implement memcpy?!?

In principle, yes. As I said, I doubt very much that this was the intent.
 
C

CBFalconer

Harald said:
The problem is that dest needn't point to the same object as what
you passed in, apparently. This was clear from the context, and
enough was properly quoted in the message you replied to, but you
didn't keep it in.

I don't understand your problem. Whatever you did, dest is set to
point to the first byte of whatever object was pointed to by the
original of s1. Whew. I.e.:

struct foo { /* something or other */ } *foop;

... /* something initializing foop */

p = memcpy(foop, &something, sizeof(struct foo));

should copy something to foop. Types don't matter. Sizes do.
 
C

CBFalconer

blargg said:
That's what's in dispute, that dest in memcpy really points to the first
byte of what foop points to. That is,

struct foo f;
void* p = &f;
unsigned char* p2 = p;
assert( p2 == (unsigned char*) &f ); // guaranteed by standard?

Somewhere in the C standard it says that a pointer to an object,
converted to a char*, points to the lowest addressed byte in that
object. Guaranteed.
 
J

James Kuyper

CBFalconer said:
I don't understand your problem. Whatever you did, dest is set to
point to the first byte of whatever object was pointed to by the
original of s1.

The specification you gave is the one that the standard gives for char*,
not void*. While the standard specifies that char* and void* represent
memory locations in the same way, it fails to specify that the result of
converting a pointer to an object type into to void* produces a pointer
that points at the same location in memory as the original pointer value.

The point I was making is that, with very few exceptions (and this isn't
one of them) the standard doesn't say where a pointer points when it
converted into a pointer to a different type. Everyone "knows" where it
will point, but the standard doesn't actually specify that it points there.

If you disagree, please cite the text in the standard that identifies
where the void* pointer points.
 
R

Richard Tobin

struct foo f;
void* p = &f;
unsigned char* p2 = p;
assert( p2 == (unsigned char*) &f ); // guaranteed by standard?
[/QUOTE]
Somewhere in the C standard it says that a pointer to an object,
converted to a char*, points to the lowest addressed byte in that
object. Guaranteed.

And if you'd bothered to read the thread you're repling to, you would
realise that the question is whether that applies. In the code above,
p is *not* a pointer to an object.

-- Richard
 
C

CBFalconer

blargg said:
.... snip ...


Right, so you've got the last cast from &f to unsigned char*
covered. How about the conversion from foo* to void*, then from
void* to unsigned char*? A pointer to void is apparently NOT a
pointer to an object, since void is not an object type.

That is covered too. It is not guaranteed to do anything useful.
 
T

Tim Rentsch

jameskuyper said:
pete said:
[snip snip]

ISO/IEC 9899:1999 (E)
6.3.2.3 Pointers
7
When a pointer to an object is
converted to a pointer to a character type,
the result points to the lowest addressed byte of the object.

pv is a pointer to void. "void" is an incomplete type, not an object
type, so pv does not point to an object. It points to a location in
memory, but the standard doesn't specify which location it points at
(we all "know" where it points, but the standard fails to say so). If
the standard guaranteed some kind of chain rule for pointer
conversions: (A*)(B*)p == (A*)p, that would be the missing link in the
argument; but it doesn't. In general, two-step pointer conversions
have a standard-defined result only when they are conversion pairs
that bring a pointer value back to its original type. The exceptions
are conversion pairs where the intermediate type is a pointer to a
character type, or where one of the conversions converts a pointer to
a struct type into a pointer to the type of it's first member, or vice
versa.

This reasoning rests on the idea that a pointer to void (since void is
an incomplete type) is not a pointer to an object. That notion is not
consistent with other statements made in the Standard.

For example, (struct foo *) and (int (*)[]) are both pointers to
incomplete types (assuming that 'struct foo' hasn't had its contents
defined). But pointers of these types still point to objects,
wouldn't you agree?

Nor is (void *) special in this regard. 7.20.3, talking about calloc,
malloc, and realloc (all of which return type (void*)), says:

Each such allocation shall yield a pointer to an object
disjoint from any other object.

Furthermore, the term 'incomplete type' itself explicitly includes
object-ness, using the definition in 6.2.5 p 1:

Types are partitioned into object types (types that fully describe
objects), function types (types that describe functions), and
incomplete types (types that describe objects but lack information
needed to determine their sizes).

Incomplete types describe objects.

A pointer to an incomplete type is still a pointer to an object
(assuming the pointer value isn't null); and in particular a
non-null pointer to void is still a pointer to an object.

Converting a (void*) to a (char*) gives the lowest addressed byte
of the object the (void*) points to. Since (void*) carries no
information about the size of the object it points to, the only
place it can point is the lowest addressed byte of the object.
 
T

Tim Rentsch

blargg said:
Right, so you've got the last cast from &f to unsigned char* covered.
How about the conversion from foo* to void*, then from void* to unsigned
char*? A pointer to void is apparently NOT a pointer to an object, since
void is not an object type.

A (non-null) pointer to void is still a pointer to an object;
please see my last response in this thread.
 
T

Tim Rentsch

Somewhere in the C standard it says that a pointer to an object,
converted to a char*, points to the lowest addressed byte in that
object. Guaranteed.

And if you'd bothered to read the thread you're repling to, you would
realise that the question is whether that applies. In the code above,
p is *not* a pointer to an object.[/QUOTE]

Even though (void) is an incomplete type, the (void*) pointer p
still is a pointer to an object; see my previous response in
this thread.
 
T

Tim Rentsch

Tim Rentsch wrote:
[...]
A pointer to an incomplete type is still a pointer to an object
(assuming the pointer value isn't null); and in particular a
non-null pointer to void is still a pointer to an object.

Converting a (void*) to a (char*) gives the lowest addressed byte
of the object the (void*) points to. Since (void*) carries no
information about the size of the object it points to, the only
place it can point is the lowest addressed byte of the object.

What's to keep it from pointing to the last byte of the object?

int i;
void* vp = &i; // vp = (char*) &i + (sizeof(int) - 1)
int* ip = vp; // ip = (int*) ((uintptr_t) vp - (sizeof(int ) - 1))
char* cp = vp; // cp = (char*) ((uintptr_t) vp - (sizeof(char) - 1))
assert( ip == &i ); // OK
assert( cp == (char*) &i ); // would fail

Is there anything other than 6.3.2.3 that specifies what a conversion
to/from void* involves?

Consider the following expression:

&i == (void*) &i

I expect you'll agree that this expression is well-formed, and that it
must yield the value 1. According to 6.5.9 p 6, this means the two
pointers are pointers to the same object, or perhaps an object and
a subobject at its beginning. In either case, the two objects pointed
to have the same lowest addressed byte; ergo we can conclude that

(char*) &i == (char*) (void*) &i

Right?
 
C

CBFalconer

blargg said:
Tim Rentsch wrote:
[...]
A pointer to an incomplete type is still a pointer to an object
(assuming the pointer value isn't null); and in particular a
non-null pointer to void is still a pointer to an object.

Converting a (void*) to a (char*) gives the lowest addressed byte
of the object the (void*) points to. Since (void*) carries no
information about the size of the object it points to, the only
place it can point is the lowest addressed byte of the object.

What's to keep it from pointing to the last byte of the object?

The C standard.
 
K

Keith Thompson

CBFalconer said:
blargg said:
Tim Rentsch wrote:
[...]
A pointer to an incomplete type is still a pointer to an object
(assuming the pointer value isn't null); and in particular a
non-null pointer to void is still a pointer to an object.

Converting a (void*) to a (char*) gives the lowest addressed byte
of the object the (void*) points to. Since (void*) carries no
information about the size of the object it points to, the only
place it can point is the lowest addressed byte of the object.

What's to keep it from pointing to the last byte of the object?

The C standard.

Chapter and verse? (Actually, I think somebody else may already have
answered that, but I wasn't paying much attention.)
 

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

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top