Casting from pointer types to non-pointer types

Discussion in 'C++' started by jois.de.vivre, Jun 6, 2007.

  1. 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).
     
    jois.de.vivre, Jun 6, 2007
    #1
    1. Advertisements

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

    V
     
    Victor Bazarov, Jun 6, 2007
    #2
    1. Advertisements

  3. 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.
     
    Jacek Dziedzic, Jun 6, 2007
    #3
  4. jois.de.vivre

    gaojb Guest

    It should be safe. I believe in all current compiler, long and pointer
    have same size. Please google "LP64" for more information.
     
    gaojb, Jun 6, 2007
    #4
  5. Please quote some context. See my signature.

    Not safe.
    On a 64bit Windows system, a pointer has 64bit, but long has 32bit like an int.
     
    Thomas J. Gritzan, Jun 6, 2007
    #5
  6. Win64 - BAM! - Pointers are 64 bit and longs are 32 bit.
     
    Victor Bazarov, Jun 6, 2007
    #6


  7. 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 ...
     
    jois.de.vivre, Jun 6, 2007
    #7
  8. The Standard doesn't say what error message will be displayed. It
    is up to the implementation.
    It is not.

    V
     
    Victor Bazarov, Jun 6, 2007
    #8
  9. jois.de.vivre

    Jim Langston Guest

    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).
     
    Jim Langston, Jun 7, 2007
    #9
  10. jois.de.vivre

    James Kanze Guest

    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.)
     
    James Kanze, Jun 7, 2007
    #10
  11. jois.de.vivre

    James Kanze Guest

    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).
     
    James Kanze, Jun 7, 2007
    #11
  12. jois.de.vivre

    James Kanze Guest

    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.)
     
    James Kanze, Jun 7, 2007
    #12
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.