Where do pointers point to?

M

Michael Wojcik

You can pass a void* to a function requiring any form of pointer,
and the conversion is automatic.

Not if the function is variadic, as is execl, the specific example
here (or if there is no prototype in scope).

We're agreed that, given the (POSIX) declaration of execl:

extern int execl(const char *, ...);
/* All parameters must be (possibly const-qualified) char *, and
the last parameter must be a char* null pointer. */

then:

execl("foo", NULL);

is not safe, because NULL might be defined as having integer type;
and that:

execl("foo", (char *)0);

is safe, because that correctly passes a char* null pointer constant
as the last variable parameter; but we're undecided on:

execl("foo", (void *)0);

because, as Lawrence points out, the "same size and representation"
clause might leave some room for a conforming implementation to fail,
and the footnote is non-normative.

I *think* it'd be conforming, albeit excruciatingly perverse, for an
implementation to say that, although char* and void* have the same
representation, char* parameters are passed in, say, registers, while
void* parameters are passed on the stack; and that this applies even
to, say, the first few parameters to a variadic function. But I could
well be wrong.

Perhaps the committee should have defined an "object pointer promotion"
for variadic functions: just as integer and floating-point promotions
happen for variable parameters, all object pointers could be promoted
to void* when passed as variable parameters (or in any case when no
prototype is in scope). But they didn't.
 
X

x-pander

int v;
(void*)(char*)&v == (void*)&v;

I still don't see it (maybe it's obvious, but it's still early morning
for me and a coffee didn't help). The Standard int 6.3.2.3 (Conversions
...
Pointers) only demands that (subject to alignment) conversions between
pointer types are reversible. However I don't see how you can infer
from it that single int*->void* conversion must yield the same value
as double conversion int*->char*->void*. How do you do it?

Where exactly is it written that the result of pointer conversion
(provided it doesn't invoke UB) must point to the same location
as the original pointer?

Consider the following function:

void memset1(char *p, int c, int n)
{
while(n-->0) *p++ = c;
}

Now the call:
memset1((char *)&v, 0, sizeof(v));
is guaranteed to work as desired, by 6.3.2.3 p7 (C99).

Now consider:

void memset2(void *p, int c, int n)
{
while(n-->0) *((char *)p)++ = c;
}

memset2(&v, 0, sizeof(v));

What happens here is:
1. 'int *' is automatically converted to 'void *' on function call
2. 'void *' is converted to 'char *' inside a function

Which part(s) of the C99 standard (if any?) guarantee that (char *)(void
*)&v equals to (char *)&v, that is to the lowest addresed byte of object v ?
 
E

Eric Sosman

x-pander said:
[...]
Now consider:

void memset2(void *p, int c, int n)
{
while(n-->0) *((char *)p)++ = c;
}

When my compiler considered this, it said

"foo.c", line 4: warning: a cast does not
yield an lvalue

See also Question 4.5 in the comp.lang.c Frequently
Asked Question (FAQ) list

http://www.eskimo.com/~scs/C-faq/top.html
What happens here is: [...]

.... entirely up to the implementation. After issuing
the required diagnostic for the illegal construct, it
can then choose to interpret that construct in any way
it pleases.
 
X

x-pander

void memset2(void *p, int c, int n)
When my compiler considered this, it said

"foo.c", line 4: warning: a cast does not
yield an lvalue

yea, my bad, but that was not what the question was all about
let me change the implemetation to:

void memset2(void *p, int c, int n)
{
while(n-->0) ((char *)p)[n] = c;
}

now it should be valid and the original question remains unchanged
 
E

Eric Sosman

x-pander said:
let me change the implemetation to:

void memset2(void *p, int c, int n)
{
while(n-->0) ((char *)p)[n] = c;
}

now it should be valid and the original question remains unchanged

For our reference, the original question was
Which part(s) of the C99 standard (if any?) guarantee that
(char *)(void *)&v equals to (char *)&v, that is to the
lowest addresed byte of object v ?

6.3.2.3/7 covers the conversion to `char*' and the "lowest
addressed" piece. 6.3.2.3/1 covers the conversion to and
from `void*'. Or have I overlooked a subtlety?
 
X

x-pander

Which part(s) of the C99 standard (if any?) guarantee that
6.3.2.3/7 covers the conversion to `char*' and the "lowest
addressed" piece. 6.3.2.3/1 covers the conversion to and
from `void*'. Or have I overlooked a subtlety?

True, but I dont see how those can answer "the question". Here is how i read
that:

6.3.2.3/7: (char *)&v -> points to the lowest addressed byte of an object
pointed by '&v' which is of type 'int'

6.3.2.3/1: (void *)&v -> valid conversion, only guarantee: if this pointer
is converted back to (int *) it equals to '&v'
6.3.2.3/7 (char *)(void *)&v -> valid conversion, points to the lowest
addresed byte of an object pointed by '(void *)&v' which is of type 'void'

Now, what is the lowest addressed byte of an object of type 'void' ?
6.2.5/19: "The void type comprises an empty set of values; it is an
incomplete type that cannot be completed"

From that definition I would say that an object of type void does not have
any storage or representation associated with it, so the concept of it's
lowest addressed byte is not quite sensible.

So, where do I go from here to find an answer to the qustion at the top of
this post?
 
C

Christian Bau

It points to the "first" byte. The C Standard doesn't say anything about
addresses.

Actually it does:
C99: 6.3.2.3.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. Successive
increments of the result, up to the size of the object, yield pointers to
the remaining bytes of the object."
You need to make it run on littleendian hardware.
You have the source code to the compiler. Instead of changing 10 million
lines of code, change the compiler so that the "first" byte is at a
higher address than the last byte; where "p++;" would usually be
translated into a hardware instruction that adds sizeof (*p), it will be
translated to a hardware instruction that subtracts sizeof (*p). Pointer
comparisons say that p < q if a hardware compare instruction says p > q
and so on. Now a pointer always points to the highest addressed byte,
and it is completely conforming and invisible to the programmer.

An example situation in described system could be:

int x;
&x == 0x4003
(void *)&x == 0x4003
(char *)&x == 0x4000
(void *)(char *)&x = 0x4000[/QUOTE]

No; (char *)&x would be == 0x4003. The first byte would be at address
0x4003, the second byte at address 0x4002, and so on. ((char *) &x) + 1
would be == 0x4002. If you compare ((char *) &x) < ((char *) &x) + 1,
the result is true (1).
 
C

Christian Bau

Michael Mair said:
Lawrence Kirby wrote:

[snip: void * vs char *]
Yes, void * uses the same representation as pointers to character types.

Are you sure of that? It is IMO logical but not necessary.

I am not sure where but a couple of months ago there was a
discussion where the example function execl() from the FAQ 5.2
( http://www.eskimo.com/~scs/C-faq/q5.2.html ) was cited and
where some regulars stressed how important the cast (char *)
is because execl is a function with variable argument list
and so we need to make sure that we get (char *) NULL instead
of plain (that is, (void *)) NULL.

The problem here is probably that NULL itself could be defined as 0 or
0L or 0LL or ((void *) 0), for example, so it could have type int, long,
long long, or void*. If you assign NULL to a pointer variable, it will
be converted to a null pointer of the right type, no problem. But if you
pass NULL to a function with variable argument list, you have no idea
what type it actually is. (char *) NULL or (void *) NULL should both
work fine.
 
K

Keith Thompson

I *think* it'd be conforming, albeit excruciatingly perverse, for an
implementation to say that, although char* and void* have the same
representation, char* parameters are passed in, say, registers, while
void* parameters are passed on the stack; and that this applies even
to, say, the first few parameters to a variadic function. But I could
well be wrong.

I think you're right.

C99 6.3.5p26 says:

A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type.

and a footnote says:

The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values
from functions, and members of unions.

But since footnotes are non-normative, an excruciatingly perverse
implementation could ignore it.
 
S

S.Tobias

Eric Sosman said:
x-pander wrote:
For our reference, the original question was

I claim that in this sub-thread my question was "more original": :)

Where exactly is it written that the result of pointer conversion
(provided it doesn't invoke UB) must point to the same location
as the original pointer?
6.3.2.3/7 covers the conversion to `char*' and the "lowest
addressed" piece. 6.3.2.3/1 covers the conversion to and
from `void*'. Or have I overlooked a subtlety?

Only of char* the Standard says that explicitly.


Suppose an implementation on which alignment is not an issue, and pointers
are represented as unsigned integers, conversion between pointer and
integer type is natural (ie. (unsigned)ptr0xABCD == 0xABCD),
and representation of a pointer to any type is also natural (ie.
a pointer to any type that has a value 0xABCD points to an object
whose lowest addressed byte is at the address 0xABCD). Null pointer
for any type is represented as 0x0.

Now, with each pointer conversion associate an arbitrary number in
such a way that a reverse conversion has an associated opposite number.
With conversions involving char* the associated number(s) is 0 (zero).

Example:
(int*)->(long*) 3 (long*)->(int*) -3
(short*)->(int*) -2 (int*)->(short*) 2
(void*)->(long*) 5 (long*)->(void*) -5
(char*)->(int*) 0 (int*)->(char*) 0
(char*)->(void*) 0 (void*)->(char*) 0

Now, the pointer conversion is defined so that in addition to
changing the type, the associated conversion number is added
to the pointer (integer) value (in arithmetic sense, so for example,
when converting from long* to int*, the result will point three
bytes before the original pointer).

When the "shift" hits the upper or lower boundary, it wraps around,
skipping zero. (This way all null pointers are converted into null
pointers; though I'm not sure it's really necessary.)

Would such an implementation break the Standard?
 
J

Jack Klein

You can pass a void* to a function requiring any form of pointer,
and the conversion is automatic. That is one of the points of
having the void* type.

No, you can pass a void* to a function requiring any form of pointer
TO AN OBJECT TYPE. There is no automatic or even defined conversion
between pointer to any object type and any pointer to function type.

void* is a pointer to object type, even though it happens to point to
an incomplete type that can't be completed.
 
C

CBFalconer

Jack said:
No, you can pass a void* to a function requiring any form of pointer
TO AN OBJECT TYPE. There is no automatic or even defined conversion
between pointer to any object type and any pointer to function type.

void* is a pointer to object type, even though it happens to point to
an incomplete type that can't be completed.

Thanks for the accurate correction. function pointers are a
different breed. I never think of excluding them in this sort of
reply - after all, what idiot would try to mix them up?

The basic reason is that a function pointer _may_ be far more
complex than an object pointer and thus require much more storage
space. Then the conversions cannot be made.
 
S

S.Tobias

Jack Klein said:
void* is a pointer to object type, even though it happens to point to
an incomplete type that can't be completed.

Nit-pick: While I perfectly understand the intention, it doesn't make
sense reading strictly. Object and incomplete types are disjoint groups
(ie. object types are not divided into complete and incomplete types
- I fell into this trap recently), although both describe objects (6.2.5#1).
IMHO it'd be better to say: "void* is an object pointer".
 
L

Lawrence Kirby

The part of 6.3.2.3/7 about the lowest addressed byte of an object must
also work for void * or else the standard functions like memcpy() wouldn't
work. Consider also 6.2.5p26

"A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type."

This means that void * is able to store addresses for individual bytes and
void * and all pointers to character types use the same representation for
a pointer to a particular byte. Couple that with 6.3.2.3p1 and it is
difficult to see how conversions between character pointers and void *
could do anything but preserve the existing representation. I guess they
could use an alternative representation for the same pointer value if such
existed but that could happen when no type conversion occurred.

Lawrence
 
P

pete

S.Tobias said:
Nit-pick: While I perfectly understand the intention, it doesn't make
sense reading strictly.
Object and incomplete types are disjoint groups
(ie. object types are not divided into complete and incomplete types
- I fell into this trap recently),
although both describe objects (6.2.5#1).
IMHO it'd be better to say: "void* is an object pointer".

Also, void* can point to other incomplete types.

/* BEGIN new.c */

extern int array[];

int main(void)
{
void *pointer = &array;

return 0;
}

/* END new.c */
 
L

Lawrence Kirby

You can pass a void* to a function requiring any form of pointer,
and the conversion is automatic. That is one of the points of
having the void* type.

The issue being discussed here applies only to calls to unprototyped
functions or arguments in a variable part of an argument list. Otherwise
you simply can't pass a void * argument to a function that requies a
char *, as you say the compiler will convert it to char * first.

Lawrence
 
E

Eric Sosman

S.Tobias said:
Suppose an implementation on which alignment is not an issue, and pointers
are represented as unsigned integers, conversion between pointer and
integer type is natural (ie. (unsigned)ptr0xABCD == 0xABCD),
and representation of a pointer to any type is also natural (ie.
a pointer to any type that has a value 0xABCD points to an object
whose lowest addressed byte is at the address 0xABCD). Null pointer
for any type is represented as 0x0.

Now, with each pointer conversion associate an arbitrary number in
such a way that a reverse conversion has an associated opposite number.
With conversions involving char* the associated number(s) is 0 (zero).

Example:
(int*)->(long*) 3 (long*)->(int*) -3
(short*)->(int*) -2 (int*)->(short*) 2
(void*)->(long*) 5 (long*)->(void*) -5
(char*)->(int*) 0 (int*)->(char*) 0
(char*)->(void*) 0 (void*)->(char*) 0

Now, the pointer conversion is defined so that in addition to
changing the type, the associated conversion number is added
to the pointer (integer) value (in arithmetic sense, so for example,
when converting from long* to int*, the result will point three
bytes before the original pointer).

When the "shift" hits the upper or lower boundary, it wraps around,
skipping zero. (This way all null pointers are converted into null
pointers; though I'm not sure it's really necessary.)

Would such an implementation break the Standard?

You must be on the design team for the DeathStation 9900
(the C99 successor to the C90-based DeathStation 9000). ;-)

I think this scheme might run afoul of the rules involving
pointers to structs and unions and to their elements. For
example,

union { long l; char c; } u;
long *lp = &u.l;
char *cp = &u.c;
assert ((void*)lp == (void*)cp);

looks like it would run into trouble with the "conversion
numbers" mentioned above.
 
L

Lawrence Kirby

Nit-pick: While I perfectly understand the intention, it doesn't make
sense reading strictly. Object and incomplete types are disjoint groups
(ie. object types are not divided into complete and incomplete types
- I fell into this trap recently), although both describe objects (6.2.5#1).
IMHO it'd be better to say: "void* is an object pointer".

Don't confuse the type of a pointer and what it points to. Usually there
is a direct relation but not with pointers to incomplete types. It is
perfectly reasonable for a pointer to an incomplete type to point at an
object, there isn't much else it can point at. It just doesn't specify the
details of the object. Pointers to incomplete structure types can point at
structures, however you can't access the contents of the structure without
supplying more information. The same is true for void * in a more general
sense. The "more information" is supplied by converting the pointer to an
appropriate pointer to object type, or by supplying a size and treating
the object as a character array.

So say that void * is a pointer to an incomplete type and as such it can
point at objects in an opaque fashion.

Lawrence
 
M

Mark Piffer

Christian Bau said:
An example situation in described system could be:

int x;
&x == 0x4003
(void *)&x == 0x4003
(char *)&x == 0x4000
(void *)(char *)&x = 0x4000

No; (char *)&x would be == 0x4003. The first byte would be at address
0x4003, the second byte at address 0x4002, and so on. ((char *) &x) + 1
would be == 0x4002. If you compare ((char *) &x) < ((char *) &x) + 1,
the result is true (1).[/QUOTE]

Are you really really sure about that? What about functions that
iteratively address an object which they only "know" through an char*?
And additionally, how could e.g. memcpy do it's work on a char[] and
on an int[] without knowing more about the object? Please don't pull
some unrealistic big-history-bookkeeping pointer monster out of the
conforming-implementations-cylinder ;). The situation of a big-endian
implementation on little-endian HW is IMHO a very valid one (which the
committee surely has considered more deeply than the aforementioned
pointers). To me it seems that

is the wrong assumption, and that

(void *)&x == (char*)&x == 0x4000

would solve it seamlessly. It looks like choosing void* in the
interface of most standard functions has already sealed the tomb: if
(char*)&x != (char*)(void*)&x should be true then the lib would really
need "magic" (or monsters) to do it's work.

Mark
 
S

S.Tobias

[snipped convoluted conversion rules
summary: pointers get "shifted" during conversion by an arbitrary
"conversion number"; except (char*). ]
You must be on the design team for the DeathStation 9900
(the C99 successor to the C90-based DeathStation 9000). ;-)

:)
Natural generalization would be to associate a permutation with each
conversion - but for simplicity let's stay with conversion numbers.

The above is not such a strange idea as it might look like. Conversion
is an operation which may change a value. It is very natural in C++,
where conversions between pointers to base and derived class (in
sense of static_cast or dynamic_cast; implicit conversions behave
this way) must adjust the value of the pointer. I'm pretty sure
that in C conversions between pointers should behave like in
reinterpret_cast, however I'd like to see it decreed somehow.
I think this scheme might run afoul of the rules involving
pointers to structs and unions and to their elements. For
example,

Oh yes, thanks, I forgot about unions and structs. Well, not everything
is lost yet!

# 6.7.2.1 Structure and union specifiers
#
# 13 Within a structure object, the non-bit-field members and the units
# in which bit-fields reside have addresses that increase in the
# order in which they are declared. A pointer to a structure object,
# suitably converted, points to its initial member (or if that member
# is a bit-field, then to the unit in which it resides), and vice
# versa. There may be unnamed padding within a structure object,
# but not at its beginning.
#
# 14 The size of a union is sufficient to contain the largest of its
# members. The value of at most one of the members can be stored in
# a union object at any time. A pointer to a union object, suitably
# converted, points to each of its members (or if a member is a bit-
# field, then to the unit in which it resides), and vice versa.

The first doubt I have here is what "suitably converted" means. Is it
a direct conversion to pointer to type of its first member(s), or a
chain of conversions, or cast to (char*)? Let's give the Standard
a favour and assume the first possibility (direct cast), which seems
to be the worst case for us.

So, to the rules I layed out above let's throw in this one:

With conversions involving type pointer to struct or union the associated
number is 0 (zero), except when the second type is (void*).

Example:
(struct A*)->(int*) 0 (int*)->(struct A*) 0
(struct A*)->(void*) 7 (void*)->(struct A*) -7
(struct B*)->(void*) -22 (void*)->(struct A*) 22

I don't think we need to handle pointers to array in a special way.

And _Complex types shouldn't be a problem either. If they were,
we could add something there too, but I refuse to believe that the
whole pointer system would rely on this type.
union { long l; char c; } u;
long *lp = &u.l;
char *cp = &u.c;
assert ((void*)lp == (void*)cp);

No, the Standard demands these (taking the direct cast to be "suitably
converted"):
assert ((long*)&u == &u.l);
assert ( &u == (union*)&u.l);
assert ((char*)&u == &u.c);
assert ( &u == (union*)&u.c);
(void*)&u == &u.l; //no guarantee, might evaluate to 0
looks like it would run into trouble with the "conversion
numbers" mentioned above.

I believe it's repared now.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top