Typecasting Pointers on a 64 bit System

Q

Quentin Pope

What is the best way to handle this warning:

warning: cast from pointer to integer of different size

I am casting in and out of a function that requires a pointer type. I
am casting an integer as a pointer, but the pointer is 8 bytes while
the integer is only 4 bytes.

Here's an example function:

pthread_create(&tid, NULL, readit, (void *)(long)connfd)

I need to pass the file descriptor as a void * for pthread_create. But
I need the file descriptor as an integer to read and write to it. Is the
above the best approach to turn off the warning? I am considering just
typecasting to size_t before typecasting to the pointer. Is that a
better approach than typecasting to long since size_t should remain equal
to the size of pointers?

Of course, I run into the reverse problem when I have to close the
file descriptor.

close((int) arg);

I don't even know what to do in the above case. I shouldn't be
truncating anything, so I should be okay.

I originally wrote the code using a 32 bit version, so I am receiving
these errors when I ported it over to a 64 bit version.
 
L

Lauri Alanko

pthread_create(&tid, NULL, readit, (void *)(long)connfd)

I need to pass the file descriptor as a void * for pthread_create. But
I need the file descriptor as an integer to read and write to it. Is the
above the best approach to turn off the warning?

No.

Just pass a pointer to the fd:

pthread_create(&tid, NULL, readit, &connfd);

Then dereference the pointer in the readit function to get the
descriptor. If connfd is a local variable, you need to make sure that
connfd doesn't exit the scope before the new thread has read it. If
this is difficult, you can allocate a new copy for the thread:

int* fdp = malloc(sizeof(int));
*fdp = connfd;
pthread_create(&tid, NULL, readit, fdp);

void readit(void* fdp) {
int connfd = *(int*)fdp;
free(fdp);
...;
}


Lauri
 
B

Ben Pfaff

Quentin Pope said:
What is the best way to handle this warning:

warning: cast from pointer to integer of different size

The best way may be to avoid casting between a pointer and an
integer.

Another way is to use (u)intptr_t from <stdint.h>, which is
ordinarily defined as an integer type that is the same width as a
pointer.
 
J

James Kuyper

On 11/10/2011 03:32 PM, Ben Pfaff wrote:
....
Another way is to use (u)intptr_t from <stdint.h>, which is
ordinarily defined as an integer type that is the same width as a
pointer.

More accurately, it's defined to be big enough for the conversion to be
reversible. In practice, it's likely to be the same size, but that's not
part of the definition. It could easily be larger. If there's any
redundancy in the pointer representation, and the conversion removes
that redundancy, it could even be smaller.
 
K

Kaz Kylheku

What is the best way to handle this warning:

warning: cast from pointer to integer of different size

Use an integer of a suitable size.
I am casting in and out of a function that requires a pointer type. I
am casting an integer as a pointer, but the pointer is 8 bytes while
the integer is only 4 bytes.

In the C99 language, there is the intptr_t type for this sort of thing.

For older dialects of C, you have to cob together some kind of compile
time configuration. Define the type as a typedef and change it based
on target compiler and platform.

I've written a configure script which detects what integral type is
suitable for holding pointers (based on size) and throws the info
into a "config.h" header.

This is BSD licensed so you can borrow it.

http://www.kylheku.com/cgit/txr/tree/configure

The approach taken in this script is to compile a sample translation unit and
then inspect the symbols (using the POSIX-standard nm command).

This creates an int_ptr_t typedef in "config.h" along with macros
giving the minimum and maximum value.

It's only been tested on various GNU/Linux architectures, Cygwin and MinGW.

It supports cross-compiling because the test programs are only compiled, never
executed.
 
K

Kaz Kylheku

No.

Just pass a pointer to the fd:

pthread_create(&tid, NULL, readit, &connfd);

Ths is an inferior approach because now the object &connfd has
to remain stable for as long as the created thread wants to touch
that object.
Then dereference the pointer in the readit function to get the
descriptor. If connfd is a local variable, you need to make sure that
connfd doesn't exit the scope before the new thread has read it.

You need to ensure that AND that the object is not overwritten with a different
value (e.g. in a loop that launches more than one thread).

In this case, why use threads? Just call "readit" directly from
this thread.

If the parent thread has to be confined while a thread executes, there is no
point. Just thread creation and context switching overhead.

What you can do is implement some little "mousetrap" protocol
wherby the thread indicates "I have consumed the argument, you can
trash it now". E.g condition variable, etc.

Or you could just cast the darn value to a pointer!
this is difficult, you can allocate a new copy for the thread:

int* fdp = malloc(sizeof(int));
*fdp = connfd;
pthread_create(&tid, NULL, readit, fdp);

Or just cast the darn value to a pointer. Save this trick for when the argument
list grows to include more than one value.
 
K

Keith Thompson

Ben Pfaff said:
The best way may be to avoid casting between a pointer and an
integer.

Another way is to use (u)intptr_t from <stdint.h>, which is
ordinarily defined as an integer type that is the same width as a
pointer.

But (u)intptr_t won't exist on systems where no integer type is
wide enough to hold a converted pointer value without loss of
information, so code that depends on it is non-portable. But if
you insist on passing an integer value in a pointer argument,
uintptr_t or intptr_t is the way to go.

In this case, the intent of the "arg" argument to pthread_create()
is that it should be a pointer to data needed by the start routine.
 
K

Kaz Kylheku

Or just cast the darn value to a pointer. Save this trick for when the argument
list grows to include more than one value.

P.S. what is more important: having an allergic reaction to
pointer-integer casts? Or checking the return value of malloc.

:)
 
K

Kaz Kylheku

Use an integer of a suitable size.


In the C99 language, there is the intptr_t type for this sort of thing.

For older dialects of C, you have to cob together some kind of compile
time configuration. Define the type as a typedef and change it based
on target compiler and platform.

I've written a configure script which detects what integral type is
suitable for holding pointers (based on size) and throws the info
into a "config.h" header.

But, by the way, I would not bother with this for something so trivial.

Here, your pointer type is larger than the integer you are trying to
pass through, so it doesn't matter.

Just kill the warning for that source file.
 
J

James Kuyper

Ths is an inferior approach because now the object &connfd has
to remain stable for as long as the created thread wants to touch
that object.
....
Or you could just cast the darn value to a pointer!

Unless you're worried about the possibility that the result of the
conversion is a trap representation, as is explicitly allowed by the C
standard. Does POSIX say something to guarantee that this is not a
possibility?
 
K

Kaz Kylheku

Unless you're worried about the possibility that the result of the
conversion is a trap representation, as is explicitly allowed by the C
standard. Does POSIX say something to guarantee that this is not a
possibility?

This type of concern has no economic consequence.
 
J

Joe Pfeiffer

Quentin Pope said:
What is the best way to handle this warning:

warning: cast from pointer to integer of different size

I am casting in and out of a function that requires a pointer type. I
am casting an integer as a pointer, but the pointer is 8 bytes while
the integer is only 4 bytes.

Here's an example function:

pthread_create(&tid, NULL, readit, (void *)(long)connfd)

I need to pass the file descriptor as a void * for pthread_create. But
I need the file descriptor as an integer to read and write to it. Is the
above the best approach to turn off the warning? I am considering just
typecasting to size_t before typecasting to the pointer. Is that a
better approach than typecasting to long since size_t should remain equal
to the size of pointers?

Of course, I run into the reverse problem when I have to close the
file descriptor.

close((int) arg);

I don't even know what to do in the above case. I shouldn't be
truncating anything, so I should be okay.

I originally wrote the code using a 32 bit version, so I am receiving
these errors when I ported it over to a 64 bit version.

Why do you need to pass the file descriptor as a void*? Why not pass a
pointer to the file descriptor?
 
J

James Kuyper

This type of concern has no economic consequence.

If trap representations are in fact possible, programs can radically
malfunction (if they can't malfunction, it isn't really a trap
representation). That often can have economic consequences, depending
upon the nature of the malfunction. Therefore, your response, if
accurate, would seem to be a pointlessly complicated way of saying
"Yes". If you meant something else, it needs more (and clearer) explanation.
 
I

Ian Collins

What is the best way to handle this warning:

warning: cast from pointer to integer of different size

I am casting in and out of a function that requires a pointer type. I
am casting an integer as a pointer, but the pointer is 8 bytes while
the integer is only 4 bytes.

Here's an example function:

pthread_create(&tid, NULL, readit, (void *)(long)connfd)

I need to pass the file descriptor as a void * for pthread_create. But
I need the file descriptor as an integer to read and write to it. Is the
above the best approach to turn off the warning?

No. just pass the address of the descriptor.
I am considering just
typecasting to size_t before typecasting to the pointer. Is that a
better approach than typecasting to long since size_t should remain equal
to the size of pointers?

Pointers aren't actors.
Of course, I run into the reverse problem when I have to close the
file descriptor.

close((int) arg);

I don't even know what to do in the above case. I shouldn't be
truncating anything, so I should be okay.

I originally wrote the code using a 32 bit version, so I am receiving
these errors when I ported it over to a 64 bit version.

Hacks invariably come back and bite you in the arse!
 
K

Kaz Kylheku

If trap representations are in fact possible, programs can radically
malfunction (if they can't malfunction, it isn't really a trap
representation). That often can have economic consequences, depending
upon the nature of the malfunction.

Before you can discover that this POSIX-threaded program malfunctions in this
way, you have to get a POSIX implementation running on a chip which has these
trap representations.

The economic consequence is that there will be so many problems with this,
leading to so many delays, that the platform will be left without customers,
and soon without investors.

Where can order an evaluation board for a chip with trap representations, and a
POSIX runtime and toolchain?
 
K

Kaz Kylheku

On MacOS X 64 bit, an int or unsigned int cast to a pointer will
_never_ produce a valid pointer. So the warning is very much
justified.

The warning is about a different size, not about never producing
a valid pointer.
Now imagine you wanted to pass a long double.

Then you'd be writing a different program, requiring a different
approach.

Conversions between pointer and long double are not even a standard feature of
the C language; a diagnostic is required.
Or a long
long on a system with 32 bit pointers.

In what situation would you need a POSIX file descriptor to be held in a long
long? Descriptor values are allocated into the smallest available position, so
if your program is ever to achieve a file descriptor value of N, it must at
least momentarily create a situation where there N+1 descriptors simultaneously
open.

Single Unix Spec Issue 6, on the subject of the open function:

``Upon successful completion, the function shall open the file and return a
non-negative integer representing the lowest numbered unused file descriptor.
Otherwise, -1 shall be returned and errno set to indicate the error. No files
shall be created or modified if the function returns -1.''
Your approach just cannot work
in these cases.

Casting a small long long value to a 32 bit pointer and back to recover
that value is something that is /widely/ portable.
The safe thing to do is to allocate some memory with malloc, store
whatever you want to pass in the allocated memory, and pass the
pointer, so someone else can then safely pick up the data.

There is nothing unsafe about the conversions when they work.

They're only unsafe on some fictitious machine where you get a trap
representation, etc.

"Less than maximally portable" and "unsafe" are totally different concepts.

Getting it running on the weird machine is the porter's responsibility. He
hopes to make X dollars by porting the program to a weird platform Y, guessing
that he can do so with an investment of Z dollars in an amount of time T and
takes his chances accordingly.
 
R

Richard Damon

Before you can discover that this POSIX-threaded program malfunctions in this
way, you have to get a POSIX implementation running on a chip which has these
trap representations.

The economic consequence is that there will be so many problems with this,
leading to so many delays, that the platform will be left without customers,
and soon without investors.

Where can order an evaluation board for a chip with trap representations, and a
POSIX runtime and toolchain?

Look at the 16 bit protected mode x86 architecture, admittedly a bit old
now, but was a mainstay architecture, and I do believe did have POSIX
available (and in fact was probably at one time a dominate architecture
for it). In this model, far pointers (which were the default for some
memory models) consisted of a 16 bit selector and a 16 bit offset. The
typical instruction to manipulate a data pointer would load the pointer
into the registers in a way that caused the processor to check the
validity of the selector. (An all 0 selector would always be valid, so a
NULL pointer didn't flag, so small integral values would be safe).
Converting an arbitrary 32 bit value gave you a very good chance of
generating a Segment Fault when you accessed the pointer.

You actually can run into this same issue with 32 bit x86 processors, it
is just that "far" pointers are so much rarer that you aren't apt to
misuse it this way, and my guess the current x64 mode would do the same
if you used a far pointer.
 
K

Keith Thompson

Kaz Kylheku said:
The warning is about a different size, not about never producing
a valid pointer.

Ok, but casting to a different size could well be a problem *because*
it produces an invalid pointer (among other reasons).
Then you'd be writing a different program, requiring a different
approach.

Conversions between pointer and long double are not even a standard feature of
the C language; a diagnostic is required.

Are you sure about that? What constraint does such a conversion
(which must be written as a cast) violate?

C99 6.5.4 "Cast operators" says that the operand and target type must
both have scalar types (which covers pointers and long double).

6.3.2.3 (pointer conversions) doesn't define the behavior of converting
between pointers and floating-point types.

I believe the behavior of a conversion from long double to void* is
undefined by omission.

(Data point: gcc rejects such a cast with an error message; this is a
valid response to undefined behavior according to the note in 3.4.3).
In what situation would you need a POSIX file descriptor to be held in a long
long? Descriptor values are allocated into the smallest available position, so
if your program is ever to achieve a file descriptor value of N, it must at
least momentarily create a situation where there N+1 descriptors simultaneously
open.

Obviously that would be in a situation where you're passing something
other than a POSIX file descriptor.

[...]
 
L

Lauri Alanko

on
MacOS X 64 bit and probably on other 64 bit systems casting an int to
a pointer will _always_ invoke undefined behavior unless the int has a
value of 0, and that therefore the result is always a null pointer.

On MacOS X the result may indeed be a null pointer, but, as discussed
previously (the thread of <[email protected]>),
this behavior does not seem to be guaranteed by the standard.


Lauri
 

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,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top