casting unsigned int to void*

F

Fred K

The Xpm pixmap library contains code that casts an unsigned int
to a (void *), passing that as an argument to a function
that expects a (void *):

/* Function prototype: */
xpmHashIntern (xpmHashTable *table, char *tag, void *data));

/* Calling it, casting 3rd argument: */
int a = <something>;
xpmHashIntern(hashtable, color->string, (void *)a );

On a 64-bit platform where pointers are 64 bits and ints
are 32 bits, is this safe?
 
E

Eric Sosman

The Xpm pixmap library contains code that casts an unsigned int
to a (void *), passing that as an argument to a function
that expects a (void *):

/* Function prototype: */
xpmHashIntern (xpmHashTable *table, char *tag, void *data));

/* Calling it, casting 3rd argument: */
int a = <something>;
xpmHashIntern(hashtable, color->string, (void *)a );

On a 64-bit platform where pointers are 64 bits and ints
are 32 bits, is this safe?

For suitable definitions of "safe," yes. Presumably the goal
is to be able to convert the `void*' back to an `unsigned(?) int'
and recover the original "<something>". If so,

- The C language does not guarantee that this will work (no,
not even if you use `intptr_t' or `uintptr_t'), but

- It will work on every 64-bit machine I've ever encountered,
and I haven't heard of any where it wouldn't.
 
J

James Kuyper

The Xpm pixmap library contains code that casts an unsigned int
to a (void *), passing that as an argument to a function
that expects a (void *):

/* Function prototype: */
xpmHashIntern (xpmHashTable *table, char *tag, void *data));

/* Calling it, casting 3rd argument: */
int a = <something>;
xpmHashIntern(hashtable, color->string, (void *)a );

On a 64-bit platform where pointers are 64 bits and ints
are 32 bits, is this safe?


The fact that pointers are 64 bits and ints are 32 bits makes it more
difficult for this to work, but not impossible. On a platform where both
are the same size, it's more likely to work, but that's still not
guaranteed. The precise requirements are as follows:

Converting an integer constant expression with a value of 0 to void* is
guaranteed by the standard to result in a null pointer value.

Converting a pointer to void into either intptr_t or uintptr_t produces
a value that can be converted back to void*, resulting in a pointer that
is equivalent to the original pointer. Those two types are optional; if
supported, they are defined in <stdint.h>, which was introduced in C99.
You can check whether they are supported by checking whether INTPTR_MAX
or UINTPTR_MAX are #defined.

In all other cases, while converting an integer value to a pointer is
permitted, "the result is implementation-defined, might not be correctly
aligned, might not point to an entity of the referenced type, and might
be a trap representation."

Whether or not this code works correctly is therefore
implementation-specific. It would never have been written and released
unless it actually did work, somewhere, but that's no guarantee that it
will work anywhere else.
 
F

Fred K

On 07/30/2012 01:33 PM, Fred K wrote: > The Xpm pixmap library contains code that casts an unsigned int > to a (void *), passing that as an argumentto a function > that expects a (void *): > > /* Function prototype: */ > xpmHashIntern (xpmHashTable *table, char *tag, void *data)); > > /* Calling it, casting 3rd argument: */ > int a = <something>; > xpmHashIntern(hashtable, color->string, (void *)a ); > > On a 64-bit platform where pointers are 64 bits and ints > are 32 bits, is this safe? The fact that pointers are64 bits and ints are 32 bits makes it more difficult for this to work, butnot impossible. On a platform where both are the same size, it's more likely to work, but that's still not guaranteed. The precise requirements are as follows: Converting an integer constant expression with a value of 0 to void* is guaranteed by the standard to result in a null pointer value. Converting a pointer to void into either intptr_t or uintptr_t produces a value that can be converted back to void*, resulting in a pointer that is equivalent to the original pointer. Those two types are optional; if supported, they are defined in <stdint.h>, which was introduced in C99. You can check whether they are supported by checking whether INTPTR_MAX or UINTPTR_MAX are #defined. In all other cases, while converting an integer value to a pointer is permitted, "the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation." Whether or not this code works correctly is therefore implementation-specific. It would never have been written and released unless it actually did work, somewhere, but that's no guarantee that it will work anywhere else.

Well, it was written a long time ago, and for platforms where the size of an int was the same size as a pointer. So far, I haven't found a problem with it on an HP-UX, SGI, Sun, AIX; I'm now porting it to several flavors of (64-bit) Linux, and iPad. Time will tell...
 
A

Alan Curry

There was some really unforgivable formatting in here, especially the more
deeply quoted parts, so I chopped them out. Google Groups sucks! Get a
real newsreader!

Fred K said:
Well, it was written a long time ago, and for platforms where the size
of an int was the same size as a pointer. So far, I haven't found a
problem with it on an HP-UX, SGI, Sun, AIX; I'm now porting it to
several flavors of (64-bit) Linux, and iPad. Time will tell...

Xpm isn't some dead artifact you just dug up that nobody else remembers. It has
been continuously maintained and is still distributed by x.org.

Why can't you use their version? It should already be bundled with your
Linux distribution.
 
E

Edward A. Falk

On 7/30/2012 1:33 PM, Fred K wrote:

For suitable definitions of "safe," yes. Presumably the goal
is to be able to convert the `void*' back to an `unsigned(?) int'
and recover the original "<something>". If so,

- The C language does not guarantee that this will work (no,
not even if you use `intptr_t' or `uintptr_t'), but

- It will work on every 64-bit machine I've ever encountered,
and I haven't heard of any where it wouldn't.

Agreed. The spec may not support it, but it should be safe on
most machines, as long as the size of void* is >= the size of int.

It would fail on a 286 with the wrong compiler options, and maybe a
PDP-11, but would be safe on any other machine I've ever worked on.
 
F

Fred K

There was some really unforgivable formatting in here, especially the more deeply quoted parts, so I chopped them out. Google Groups sucks! Get a real newsreader! In article <[email protected]>, Fred K <[email protected]> wrote: [about xpm] > >Well, it was written a long time ago, and for platforms where the size >of an intwas the same size as a pointer. So far, I haven't found a >problem with iton an HP-UX, SGI, Sun, AIX; I'm now porting it to >several flavors of (64-bit) Linux, and iPad. Time will tell... Xpm isn't some dead artifact you just dug up that nobody else remembers. It has been continuously maintained and is still distributed by x.org. Why can't you use their version? It should already be bundled with your Linux distribution. -- Alan Curry

I just downloaded the current version (including source code) and it still has that code in it, so they are currently using code that may be problematic.
 
B

Ben Bacarisse

Fred K said:
There was some really unforgivable formatting in here, especially the more deeply quoted parts, so I chopped them out. Google Groups sucks! Get a real newsreader! In article <[email protected]>, Fred K <[email protected]> wrote: [about xpm] > >Well, it was written a long time ago, and for platforms where the size >of an int was the same size as a pointer. So far, I haven't found a >problem with it on an HP-UX, SGI, Sun, AIX; I'm now porting it to >several flavors of (64-bit) Linux, and iPad. Time will tell... Xpm isn't some dead artifact you just dug up that nobody else remembers. It has been continuously maintained and is still distributed by x.org. Why can't you use their version? It should already be bundled with your Linux distribution. -- Alan Curry

There is something odd with your quoting! Is this another Google groups
issue?
I just downloaded the current version (including source code) and it
still has that code in it, so they are currently using code that may
be problematic.

The "may" is key. It may be problematic, but I doubt it. It's a very
common trick, and every implementation I've used defines the conversion
in a way that makes recovery of the int (the actual purpose here) safe.

You are doing the porting, are you not? Is it actually a problem?
 
J

James Kuyper

On 07/30/2012 04:04 PM, China Blue [Tor], Meersburg wrote:
....
intptr_t is an integer which is large enough to hold a void* pointer encoded in
an integer. This is an ancient bug dating back to the days when an integer was
60 bits, would always be 60 bits, and an address was 18 bits, and would always

Your descriptions seem highly platform-specific; I'm not familiar with a
platform that had those particular characteristics. Could you identify it?
 
J

James Kuyper

James Kuyper said:
On 07/30/2012 04:04 PM, China Blue [Tor], Meersburg wrote:
...
intptr_t is an integer which is large enough to hold a void* pointer
encoded in
an integer. This is an ancient bug dating back to the days when an integer
was
60 bits, would always be 60 bits, and an address was 18 bits, and would
always

Your descriptions seem highly platform-specific; I'm not familiar with a
platform that had those particular characteristics. Could you identify it?

http://en.wikipedia.org/wiki/CDC_6600

Can you give us any particular reason to believe that this particular
processor had a major role to play in setting up expectations, relevant
to the design of C, that were later violated by the Intel 8086 family of
processors, as implied by your earlier message? Your wording gave the
impression that 60-bit integers were so widely used that many people
thought they were universal. The article you linked to refers to the
60-bit words as "very large", belying that implication.
 
B

Ben Bacarisse

China Blue [Tor] said:
Fred K said:
The Xpm pixmap library contains code that casts an unsigned int
to a (void *), passing that as an argument to a function
that expects a (void *):

/* Function prototype: */
xpmHashIntern (xpmHashTable *table, char *tag, void *data));

/* Calling it, casting 3rd argument: */
int a = <something>;
xpmHashIntern(hashtable, color->string, (void *)a );

On a 64-bit platform where pointers are 64 bits and ints
are 32 bits, is this safe?

intptr_t is an integer which is large enough to hold a void* pointer
encoded in an integer. This is an ancient bug dating back to the days
when an integer was 60 bits, would always be 60 bits, and an address
was 18 bits, and would always be 18 bits. The 80x86s had addresses (20
bits) larger than ints (16 bits), exposing this bad assumption.

It's never been an assumption made by C -- even pre-ANSI C.

<snip>
 
K

Keith Thompson

Fred K said:
The Xpm pixmap library contains code that casts an unsigned int
to a (void *), passing that as an argument to a function
that expects a (void *):

/* Function prototype: */
xpmHashIntern (xpmHashTable *table, char *tag, void *data));

/* Calling it, casting 3rd argument: */
int a = <something>;
xpmHashIntern(hashtable, color->string, (void *)a );

On a 64-bit platform where pointers are 64 bits and ints
are 32 bits, is this safe?

Others have addressed the safety (or lack thereof) of doing this.

The "void *data" parameter is intended for passing arbitrary information
into the function. A safer way to pass an int value is to pass the
address of an int object:

int a = <something>;
int *p = malloc(sizeof *p);
/* check for allocation error */
xpmHashIntern(hashtable, color->string, p);

I used malloc to ensure that the object continues to exist as long as
it's needed; if it's a one-off object, declaring "a" as static would
also work.

This assumes, of course, that the code that retrieves the value from the
hash table knows to treat it as a pointer to an int object.
 
E

Eric Sosman

intptr_t is an integer which is large enough to hold a void* pointer encoded in
an integer.

intptr_t and uintptr_t are both irrelevant to the O.P.'s
question. The fact that a void* can be converted to a intptr_t
which can be in turn converted to a void* with the two pointers
comparing equal does not imply that a intptr_t can be converted
to a void* and then back to a intptr_t with the two integers
equal.

P1 -> I -> P2, P2 == P1
I1 -> P -> I2, I2 ?= I1 (no guarantee)
This is an ancient bug

Makes no sense. It can't be any more ancient than C99, which
introduced the types. "Bug" can be a subjective judgement, but it's
certainly debatable whether the existence of intptr_t is a "bug."
People who want to convert pointers to integers and back probably
dispute your assessment.
dating back to the days when an integer was
60 bits, would always be 60 bits, and an address was 18 bits, and would always
be 18 bits. The 80x86s had addresses (20 bits) larger than ints (16 bits),
exposing this bad assumption.

Is this supposed to be an argument that intptr_t is a "bug?"
I cannot comprehend what it's supposed to be about.
The intptr_t was eventually introduced to provide
an integer that would always be safe to hold addresses.

Not quite. The intptr_t types are optional, meaning
that they are not "always" safe to hold addresses. Also, there
has been no introduction of addresses that are safe to hold
integers. Finally, none of this is "ancient" except to pre-teens.
 
M

Mark Bluemel

It was a joke illustrating a common mistake about assuming sizeofs.

Clearly an example of alternative comedy. Comedy is usually funny, this
is the alternative.
 
J

James Kuyper

intptr_t and uintptr_t are both irrelevant to the O.P.'s
question. ...

No, they are not.
... The fact that a void* can be converted to a intptr_t
which can be in turn converted to a void* with the two pointers
comparing equal does not imply that a intptr_t can be converted
to a void* and then back to a intptr_t with the two integers
equal.

P1 -> I -> P2, P2 == P1
I1 -> P -> I2, I2 ?= I1 (no guarantee)


Agreed. However, we were not told how the integer value was chosen, nor
what xpmHashIntern() did with it. If you're familiar with xpm, it might
seem obvious. However, for those of us who are not, the possibilities
are quite open-ended. One way that code similar to the code above could
have worked is as follows:

double d = 3.14;
intptr_t a = (intptr_t)(void*)&d;

xpmHashIntern(hashtable, color->string, (void*)a);

xpmHashIntern (xpmHashTable *table, char *tag, void *data))
{
/* ... stuff ... */
double *pd = data;
/* ... more stuff, making use of *pd ... */
}

Using int rather than intptr_t could therefore work on a particular
implementation, if the result of (intptr_t)(void*)d happened to be
representable as an int on that implementation. That's not something
that code intended to be portable should rely upon, but it does provide
one possible way that such non-portable code like that might have worked
on a particular implementation.
This is an ancient bug

Makes no sense. It can't be any more ancient than C99, which
introduced the types. "Bug" can be a subjective judgement, but it's
certainly debatable whether the existence of intptr_t is a "bug."


I didn't see anything that suggested that the existence of [u[intptr_t
was a bug. The "bug" he's referring to is the use of 'int' rather than
'intprt_t' in the original code.

....
dating back to the days when an integer was
60 bits, would always be 60 bits, and an address was 18 bits, and would always
be 18 bits. The 80x86s had addresses (20 bits) larger than ints (16 bits),
exposing this bad assumption.

Is this supposed to be an argument that intptr_t is a "bug?"
No.

I cannot comprehend what it's supposed to be about.


It's meant to be an explanation of the problem that intptr_t was
invented to solve. It fails primarily due to the fact that numbers given
were apparently chosen for humorous effect, rather than plausibility. It
would have been a lot more plausible if 60 and 18 had both been changed
to 16.
 
J

James Kuyper

The Xpm pixmap library contains code that casts an unsigned int

Here, you say that it was an unsigned int.
to a (void *), passing that as an argument to a function
that expects a (void *):

/* Function prototype: */
xpmHashIntern (xpmHashTable *table, char *tag, void *data));

/* Calling it, casting 3rd argument: */
int a = <something>;

But that's a signed int. The difference is irrelevant to the main point
of your question, but the discrepancy raises questions about the
accuracy of your description of the original code.
 
8

88888 Dihedral

James Kuyperæ–¼ 2012å¹´7月31日星期二UTC+8下åˆ7時26分39秒寫é“:
Here, you say that it was an unsigned int.









But that's a signed int. The difference is irrelevant to the main point

of your question, but the discrepancy raises questions about the

accuracy of your description of the original code.



James Kuyperæ–¼ 2012å¹´7月31日星期二UTC+8下åˆ7時26分39秒寫é“:
Here, you say that it was an unsigned int.









But that's a signed int. The difference is irrelevant to the main point

of your question, but the discrepancy raises questions about the

accuracy of your description of the original code.

As far as I know, the valid operators of pointers are ++, --, ==, =,
and various explicit type castings, ( Type *), in the C language standards..

All other operations of pointers are platform-dependent.

If the compiler can be sure that the higher 32 bits of a 64-bit pointer
is not to be changed after ++, and --, then only the lower 32 bits are needed to be loaded into a 32 bit register.

Do we need to add more operators of pointers to be in the C standards?

But I think some trival C macros in assembly can offer the same effects.

Uhn, a more sophiscated malloc might be needed, too.
 
I

Ike Naar

As far as I know, the valid operators of pointers are ++, --, ==, =,
and various explicit type castings, ( Type *), in the C language standards.

*, ->, &, +, +=, -, -=, -, [], <, <=, >, >=, !=, sizeof, (), &&, ||, ?:
 
P

Phil Carmody

Ike Naar said:
As far as I know, the valid operators of pointers are ++, --, ==, =,
and various explicit type castings, ( Type *), in the C language standards.

*, ->, &, +, +=, -, -=, -, [], <, <=, >, >=, !=, sizeof, (), &&, ||, ?:

Good list, but did you really need to mention the comma operator 18 times?

;-) Phil
--
I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top