Casting from pointer types to non-pointer types

J

jois.de.vivre

I am interfacing with a third party API (written in C, if that
matters) that has an "event handler" function with the following
definition:

void event_handler(int event_code, unsigned long user_data);

The function takes an event code along with the user data but does not
act on or change the user data.
In my application, I want to pass a pointer as the user data. This
pointer is to an array allocated with new, say, as follows:

char *array = new char[100];

and passed to the event handler as follows:

event_handler(1, reinterpret_cast<unsigned long>(array));

My question is if it's safe to cast this to an unsigned long type if I
want to reuse it later? Specifically will doing something like this
cause me any trouble?

void some_function(unsigned long pointer)
{
char *array = reinterpret_cast<char*>(pointer);
/* do stuff with array */
delete [] array;
}

On my machine both sizeof(char*) and sizeof(unsigned long) is 8 so
while this is a pretty ugly solution, at first glance it would seem
safe (at least from overflow).
 
V

Victor Bazarov

I am interfacing with a third party API (written in C, if that
matters) that has an "event handler" function with the following
definition:

void event_handler(int event_code, unsigned long user_data);

The function takes an event code along with the user data but does not
act on or change the user data.
In my application, I want to pass a pointer as the user data. This
pointer is to an array allocated with new, say, as follows:

char *array = new char[100];

and passed to the event handler as follows:

event_handler(1, reinterpret_cast<unsigned long>(array));

My question is if it's safe to cast this to an unsigned long type if I
want to reuse it later? Specifically will doing something like this
cause me any trouble?

It is safe and well-defined _if_ 'unsigned long' has the same size as
the pointer to which you're casting it.
void some_function(unsigned long pointer)
{
char *array = reinterpret_cast<char*>(pointer);
/* do stuff with array */
delete [] array;
}

On my machine both sizeof(char*) and sizeof(unsigned long) is 8 so
while this is a pretty ugly solution, at first glance it would seem
safe (at least from overflow).

It is.

V
 
J

Jacek Dziedzic

I am interfacing with a third party API (written in C, if that
matters) that has an "event handler" function with the following
definition:

void event_handler(int event_code, unsigned long user_data);

The function takes an event code along with the user data but does not
act on or change the user data.
In my application, I want to pass a pointer as the user data. This
pointer is to an array allocated with new, say, as follows:

char *array = new char[100];

and passed to the event handler as follows:

event_handler(1, reinterpret_cast<unsigned long>(array));

My question is if it's safe to cast this to an unsigned long type if I
want to reuse it later? Specifically will doing something like this
cause me any trouble?

void some_function(unsigned long pointer)
{
char *array = reinterpret_cast<char*>(pointer);
/* do stuff with array */
delete [] array;
}

On my machine both sizeof(char*) and sizeof(unsigned long) is 8 so
while this is a pretty ugly solution, at first glance it would seem
safe (at least from overflow).

I think it would be "generally unsafe", but if your unsigned longs
really are 8 byte, then it will work. Still, an assert() that
sizeof(unsigned long)==sizeof(char*) somewhere in your code
won't hurt (and it will help others in 20 years' time track
the bug as they port your code to machines using "__further",
16-byte pointers :>).

HTH,
- J.
 
G

gaojb

It should be safe. I believe in all current compiler, long and pointer
have same size. Please google "LP64" for more information.
 
T

Thomas J. Gritzan

Please quote some context. See my signature.

It should be safe. I believe in all current compiler, long and pointer
have same size. Please google "LP64" for more information.

Not safe.
On a 64bit Windows system, a pointer has 64bit, but long has 32bit like an int.
 
V

Victor Bazarov

It should be safe. I believe in all current compiler, long and pointer
have same size. Please google "LP64" for more information.

Win64 - BAM! - Pointers are 64 bit and longs are 32 bit.
 
J

jois.de.vivre

I am interfacing with a third party API (written in C, if that
matters) that has an "event handler" function with the following
definition:
void event_handler(int event_code, unsigned long user_data);
The function takes an event code along with the user data but does not
act on or change the user data.
In my application, I want to pass a pointer as the user data. This
pointer is to an array allocated with new, say, as follows:
char *array = new char[100];
and passed to the event handler as follows:
event_handler(1, reinterpret_cast<unsigned long>(array));
My question is if it's safe to cast this to an unsigned long type if I
want to reuse it later? Specifically will doing something like this
cause me any trouble?

It is safe and well-defined _if_ 'unsigned long' has the same size as
the pointer to which you're casting it.


void some_function(unsigned long pointer)
{
char *array = reinterpret_cast<char*>(pointer);
/* do stuff with array */
delete [] array;
}
On my machine both sizeof(char*) and sizeof(unsigned long) is 8 so
while this is a pretty ugly solution, at first glance it would seem
safe (at least from overflow).

It is.

V



If the sizes differ, will the cast fail with a "loses precision"
error, such as attempting to cast from a pointer to say, an int?

char *array = new char[100];
int ptr_val = reinterpret_cast<int>(array);

on gcc:
error: cast from 'char*' to 'int' loses precision

not sure if this is true with all compilers ...
 
V

Victor Bazarov

[..]
If the sizes differ, will the cast fail with a "loses precision"
error, such as attempting to cast from a pointer to say, an int?

The Standard doesn't say what error message will be displayed. It
is up to the implementation.
char *array = new char[100];
int ptr_val = reinterpret_cast<int>(array);

on gcc:
error: cast from 'char*' to 'int' loses precision

not sure if this is true with all compilers ...

It is not.

V
 
J

Jim Langston

I am interfacing with a third party API (written in C, if that
matters) that has an "event handler" function with the following
definition:

void event_handler(int event_code, unsigned long user_data);

The function takes an event code along with the user data but does not
act on or change the user data.
In my application, I want to pass a pointer as the user data. This
pointer is to an array allocated with new, say, as follows:

char *array = new char[100];

and passed to the event handler as follows:

event_handler(1, reinterpret_cast<unsigned long>(array));

My question is if it's safe to cast this to an unsigned long type if I
want to reuse it later? Specifically will doing something like this
cause me any trouble?

void some_function(unsigned long pointer)
{
char *array = reinterpret_cast<char*>(pointer);
/* do stuff with array */
delete [] array;
}

On my machine both sizeof(char*) and sizeof(unsigned long) is 8 so
while this is a pretty ugly solution, at first glance it would seem
safe (at least from overflow).

Are you sure you are doing what you think you are doing?

array is a char pointer. It contains the address of the 100 characters that
new allocated. Your reinterpret_cast<unsigned long>( array ) is
reinterpreting this *pointer* to an integer. I suspect you actually wanted
to use the buffer itself to hold the integer. Am I correct?

This program for one run on my system outputs:
4
3288656

Which is it you wanted to use? Notes follow the code.

#include <iostream>

void event_handler(int event_code, unsigned long user_data)
{
std::cout << user_data << "\n";
}

int main()
{
char *array = new char[100];

array[0] = 4;
array[1] = 0;
array[2] = 0;
array[3] = 0;
array[4] = 0;
array[5] = 0;
array[6] = 0;
array[7] = 0;

event_handler(1, *reinterpret_cast<unsigned long*>( array ));
event_handler(1, reinterpret_cast<unsigned long>( array ));

return 0;
}

This can be dangerous on some systems (treating the contents of some other
type as an integer) becasue of byte alignment. On some systems if an
integer is not correctly byte aligned the program can run slower,
malfunction or crash. Generally a integral type needs to be aligned on it's
size. A 4 byte integer on a byte evenly divisible by 4. An 8 byte integer
on a byte evenly divisible by 8, etc... So in that case, if you need to use
the buffer itself as the integer, it may not be safe depending on your
platform.

Notice how I'm passing the *contents* of the buffer as an unsigned long. I
cast the char pointer to an unsigned long pointer, then derefence it (asking
for it's contents).
 
J

James Kanze

[..]
If the sizes differ, will the cast fail with a "loses precision"
error, such as attempting to cast from a pointer to say, an int?
The Standard doesn't say what error message will be displayed. It
is up to the implementation.
char *array = new char[100];
int ptr_val = reinterpret_cast<int>(array);
on gcc:
error: cast from 'char*' to 'int' loses precision
not sure if this is true with all compilers ...
It is not.

Are you sure. According to the standard, "A pointer can be
explicitly converted to any integral type large enough to hold
it." (From the section on reinterpret_cast.) Converting a
pointer to an integral type which is not large enough to hold it
cannot be done using reinterpret_cast. A compiler is required
to emit a diagnostic.

According to the C++ standard, the same thing holds for a C
style cast. In C, however, it is undefined behavior, and
C compilers historically have supported it, so that I expect a
lot of C++ compilers will accept it unless you request strict
conformance. Both Sun CC and g++ give an error regardless of
the type of cast, in default mode. Using a C-style cast in C
provokes a warning from gcc, and nothing from Sun cc, however.
This is a point where the two languages differ.

The other direction is fine, however, and there is no problem
using reinterpret_cast to convert a char to a pointer.

In the case in question, the poster needed a round trip
conversion pointer to integral type back to pointer. This
obviously can only work if the integral type is large enough.
Most of the time, however, it is the reverse that is needed; the
de facto standard "user data" type is void*, and if all you need
is a char... char->void*->char is forbidden by the standard, you
have to use char->void*->intptr_t->char. (And if the system
doesn't have an intptr_t, then you're out of luck.)
 
J

James Kanze

I am interfacing with a third party API (written in C, if that
matters) that has an "event handler" function with the following
definition:
void event_handler(int event_code, unsigned long user_data);
The function takes an event code along with the user data
but does not act on or change the user data. In my
application, I want to pass a pointer as the user data.
This pointer is to an array allocated with new, say, as
follows:
char *array = new char[100];
and passed to the event handler as follows:
event_handler(1, reinterpret_cast<unsigned long>(array));
My question is if it's safe to cast this to an unsigned long
type if I want to reuse it later? Specifically will doing
something like this cause me any trouble?
void some_function(unsigned long pointer)
{
char *array = reinterpret_cast<char*>(pointer);
/* do stuff with array */
delete [] array;
}
On my machine both sizeof(char*) and sizeof(unsigned long) is 8 so
while this is a pretty ugly solution, at first glance it would seem
safe (at least from overflow).
I think it would be "generally unsafe", but if your unsigned longs
really are 8 byte, then it will work. Still, an assert() that
sizeof(unsigned long)==sizeof(char*) somewhere in your code
won't hurt (and it will help others in 20 years' time track
the bug as they port your code to machines using "__further",
16-byte pointers :>).

If the integral type isn't long enough, the code shouldn't
compile, at least according to the C++ standard (and the
compilers I have at hand).
 
J

James Kanze

It should be safe. I believe in all current compiler, long and pointer
have same size. Please google "LP64" for more information.

It's certainly not required, and in the past, I've used
compilers with 6 byte pointers and 4 byte longs. I've also
heard rumors that some 64 bit Windows compilers have 32 bit
longs---it seems stupid, but you never know. I would certainly
never assume that an unsigned long is big enough to hold a
pointer on all systems, or even that there is an integral type
large enough to hold a pointer. (If I'm not mistaken, at the
hardware level, pointers can be 80 bits on a 64 bit Intel or
AMD, even though the largest integral type is only 64 bits.)
 

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,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top