Passing intptr_t to a function with void * prototype.

J

Jeroen Schot

Hello,

In a recent thread[1] on StackOverflow there was the question of passing
an int where void * was expected. Of course this is not well defined.
But what surprised me is that people remarked that passing an intptr_t
might not work either.

Given this example:

#include <stdint.h>

int foo(void *p)
{
intptr_t i = p;
...
}

...
intptr_t n = 5;
foo(n);
...

Is it true that i might not be equal to n because the standard only
specifies the conversion (void *) -> (intptr_t) -> (void *) and not
(intptr_t) -> (void *) -> (intptr_t)? If so, why?

[1]: http://stackoverflow.com/q/3811171/251122

Regards,
 
B

Ben Bacarisse

Jeroen Schot said:
In a recent thread[1] on StackOverflow there was the question of passing
an int where void * was expected. Of course this is not well defined.
But what surprised me is that people remarked that passing an intptr_t
might not work either.

Given this example:

#include <stdint.h>

int foo(void *p)
{
intptr_t i = p;
...
}

...
intptr_t n = 5;
foo(n);
...

Is it true that i might not be equal to n because the standard only
specifies the conversion (void *) -> (intptr_t) -> (void *) and not
(intptr_t) -> (void *) -> (intptr_t)? If so, why?

Yes, it's true.

Imagine a machine were all pointers have two unused zero bits at "the
bottom". The fastest way to convert a pointer to an intptr_t or back is
simply to copy the bits, but that means that the bit pattern for 5 is
not a valid pointer and if the hardware has pointer registers that
simply ignore the bottom bits, passing a value like 5 might well not
preserve it. The standard want to permit this sort of fast conversion.

There are lots of other scenarios: imagine the more likely case of a
machine with, say, 24 bit pointer registers and only 32 bit ints
available for intptr_t. There will be intptr_t values that can't be
converted (in all situations) to a pointer and back without loss of
information. The plain casts

intptr_t big_val = 0x1000000;
intptr_t other = (intptr_t)(void *)big_val;

might work, but the standard wants to permit the implementation free use
of the hardware and so the conversion is not guaranteed to work in all
cases.

<snip>
 
E

Eric Sosman

Hello,

In a recent thread[1] on StackOverflow there was the question of passing
an int where void * was expected. Of course this is not well defined.
But what surprised me is that people remarked that passing an intptr_t
might not work either.

Given this example:

#include<stdint.h>

int foo(void *p)
{
intptr_t i = p;
...
}

...
intptr_t n = 5;
foo(n);

A diagnostic is required here, because the argument type does
not match the parameter type and is not "silently convertible" to
it. `foo( (void*)n )' would stand a better chance.
...

Is it true that i might not be equal to n because the standard only
specifies the conversion (void *) -> (intptr_t) -> (void *) and not
(intptr_t) -> (void *) -> (intptr_t)? If so, why?

If intptr_t exists, any valid or null data pointer can be converted
to intptr_t and back. However, the converted integer values might not
cover the entire possible range of intptr_t values; there could be
valid intptr_t's that do not convert to valid pointers.

For example, imagine a system where void* and intptr_t both occupy
64 bits, but void* really only spans a 48-bit address space, with the
other 16 bits as "must be zero." If you set some of those bits in the
intptr_t, they may get wiped out in conversion to void*, and then show
up as zeroes upon the conversion back again.
 
J

Jeroen Schot

[...]
Given this example:

#include<stdint.h>

int foo(void *p)
{
intptr_t i = p;
...
}

...
intptr_t n = 5;
foo(n);

A diagnostic is required here, because the argument type does
not match the parameter type and is not "silently convertible" to
it. `foo( (void*)n )' would stand a better chance.

Yes, I almost wrote this as an additional question. My compiler did warn
about this, but with GCC it is not always obvious whether a warning is a
required diagnostic or a 'helpful' reminder.
[...]
For example, imagine a system where void* and intptr_t both occupy
64 bits, but void* really only spans a 48-bit address space, with the
other 16 bits as "must be zero." If you set some of those bits in the
intptr_t, they may get wiped out in conversion to void*, and then show
up as zeroes upon the conversion back again.

This confirms my suspicions and gives a clear example of when/why this
fails. Thank you (and Ben) for your answers.

Regards,
 
M

Marcin Grzegorczyk

Jeroen said:
[...]
...
intptr_t n = 5;
foo(n);

A diagnostic is required here, because the argument type does
not match the parameter type and is not "silently convertible" to
it. `foo( (void*)n )' would stand a better chance.

Yes, I almost wrote this as an additional question. My compiler did warn
about this, but with GCC it is not always obvious whether a warning is a
required diagnostic or a 'helpful' reminder.

You might try the -pedantic-errors option.
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top