Converting a pointer into an integer

192,728

Solution 1

Use intptr_t and uintptr_t.

To ensure it is defined in a portable way, you can use code like this:

#if defined(__BORLANDC__)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
    typedef unsigned long uintptr_t;
#elif defined(_MSC_VER)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
#else
    #include <stdint.h>
#endif

Just place that in some .h file and include wherever you need it.

Alternatively, you can download Microsoft’s version of the stdint.h file from here or use a portable one from here.

Solution 2

I'd say this is the modern C++ way:

#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);

EDIT:

The correct type to the the Integer

So the right way to store a pointer as an integer is to use the uintptr_t or intptr_t types. (See also in cppreference integer types for C99).

These types are defined in <stdint.h> for C99 and in the namespace std for C++11 in <cstdint> (see integer types for C++).

C++11 (and onwards) Version

#include <cstdint>
std::uintptr_t i;

C++03 Version

extern "C" {
#include <stdint.h>
}

uintptr_t i;

C99 Version

#include <stdint.h>
uintptr_t i;

The correct casting operator

In C there is only one cast and using the C cast in C++ is frowned upon (so don't use it in C++). In C++ there are different types of casts, but reinterpret_cast is the correct cast for this conversion (see also here).

C++11 Version

auto i = reinterpret_cast<std::uintptr_t>(p);

C++03 Version

uintptr_t i = reinterpret_cast<uintptr_t>(p);

C Version

uintptr_t i = (uintptr_t)p; // C Version

Related Questions

Solution 3

'size_t' and 'ptrdiff_t' are required to match your architecture (whatever it is). Therefore, I think rather than using 'int', you should be able to use 'size_t', which on a 64 bit system should be a 64 bit type.

This discussion unsigned int vs size_t goes into a bit more detail.

Solution 4

Use uintptr_t as your integer type.

Solution 5

Several answers have pointed at uintptr_t and #include <stdint.h> as 'the' solution. That is, I suggest, part of the answer, but not the whole answer. You also need to look at where the function is called with the message ID of FOO.

Consider this code and compilation:

$ cat kk.c
#include <stdio.h>
static void function(int n, void *p)
{
    unsigned long z = *(unsigned long *)p;
    printf("%d - %lu\n", n, z);
}

int main(void)
{
    function(1, 2);
    return(0);
}
$ rmk kk
        gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith \
            -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \
            -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk 
kk.c: In function 'main':
kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast
$

You will observe that there is a problem at the calling location (in main()) — converting an integer to a pointer without a cast. You are going to need to analyze your function() in all its usages to see how values are passed to it. The code inside my function() would work if the calls were written:

unsigned long i = 0x2341;
function(1, &i);

Since yours are probably written differently, you need to review the points where the function is called to ensure that it makes sense to use the value as shown. Don't forget, you may be finding a latent bug.

Also, if you are going to format the value of the void * parameter (as converted), look carefully at the <inttypes.h> header (instead of stdint.hinttypes.h provides the services of stdint.h, which is unusual, but the C99 standard says [t]he header <inttypes.h> includes the header <stdint.h> and extends it with additional facilities provided by hosted implementations) and use the PRIxxx macros in your format strings.

Also, my comments are strictly applicable to C rather than C++, but your code is in the subset of C++ that is portable between C and C++. The chances are fair to good that my comments apply.

Share:
192,728

Related videos on Youtube

PierreBdR
Author by

PierreBdR

Updated on February 18, 2022

Comments

  • PierreBdR
    PierreBdR over 2 years

    I am trying to adapt an existing code to a 64 bit machine. The main problem is that in one function, the previous coder uses a void* argument that is converted into suitable type in the function itself. A short example:

    void function(MESSAGE_ID id, void* param)
    {
        if(id == FOO) {
            int real_param = (int)param;
            // ...
        }
    }
    

    Of course, on a 64 bit machine, I get the error:

    error: cast from 'void*' to 'int' loses precision
    

    I would like to correct this so that it still works on a 32 bit machine and as cleanly as possible. Any idea ?

    • jww
      jww over 7 years
      I know this is digging up an old post, but it seems like the accepted answer is not quite correct. A concrete example of size_t not working is i386 segmented memory. Though a 32-bit machine, sizeof returns 2 for size_t. Alex answer below appears correct. Alex's answer and uintptr_t works just about everywhere and its now standard. It provides a C++11 treatment, and it even gives the C++03 header guards.
  • Michael Burr
    Michael Burr over 15 years
    See stackoverflow.com/questions/126279/… for info on how to get a stdint.h that works with MSVC (and possibly Borland).
  • Michael Burr
    Michael Burr over 15 years
    While size_t is usually large enough to hold a pointer, it's not necessarily the case. It would be better to locate a stdint.h header (if your compiler doesn't already have one) and use uintptr_t.
  • Antoine
    Antoine over 10 years
    Unfortunately the only constraint on size_t is that it must hold the result of any sizeof(). This doesn't necessarily make it 64 bits on x64. see also
  • PierreBdR
    PierreBdR over 9 years
    I think you missed the point on my question. The code is storing the value of an integer in a pointer. And that part of the code is doing the opposite (e.g. extracting the value of the integer that was written as a pointer).
  • plasmacel
    plasmacel almost 9 years
    the only answer which properly mentions reinterpret_cast
  • linleno
    linleno over 8 years
    If you meant to include <cstdint>, you probably also want to use std::uintptr_t instead.
  • AndyJost
    AndyJost about 8 years
    size_t can safely store the value of a non-member pointer. See en.cppreference.com/w/cpp/types/size_t.
  • yyny
    yyny about 8 years
    @AndyJost No it cannot. Even your own link confirms that.
  • jww
    jww over 7 years
    Awesome... The cast is what I was looking for. If we are told to use uintptr_t instead of size_t, then why does it require reinterpret_cast? It seems like a simple static_cast should do since the standard specifically provides the compatible data types...
  • Alexander Oh
    Alexander Oh over 7 years
    @jww read: en.cppreference.com/w/cpp/language/static_cast my understanding here is that static_cast might convert the type or if it's a pointer might do pointer adjustments if the type needs it. reinterpret_cast is really just changing the type of the underlying memory pattern (no mutations). to clarify: static_cast does behave identical here.
  • Antonio
    Antonio about 6 years
    Both links broken!
  • PierreBdR
    PierreBdR about 6 years
    If you do not have uintptr_t, then uintmax_t is not an answer either: there is no garanty you can store the value of a pointer in it! There might be no integer type that does that.
  • Haseeb Mir
    Haseeb Mir about 6 years
    This answer is related to C but the language is tagged C++ so it not the answer what i was looking for.
  • Haseeb Mir
    Haseeb Mir about 6 years
    this should be marked as selected answer instead, as it provides all the details how to cast in C and C++.
  • slashmais
    slashmais almost 6 years
    @YoYoYonnY: "On many platforms (an exception is systems with segmented addressing) std::size_t can safely store the value of any non-member pointer, in which case it is synonymous with std::uintptr_t." - whatever are you talking about?
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica over 5 years
    @HaSeeBMiR An appropriate fix is to switch to <cstdint>, or to download the appropriate cstdint if you download a stdint.h.
  • Haseeb Mir
    Haseeb Mir over 5 years
    @JustinTime But it would still remain c++ , adding <cstdint> or any c-header file doesnt make language c. this is an extension is compilers to support old .h legacy header files in modern c++ compilers thats why c++ had to support this and add all .h C-Header files as <c-header-file> . still i would prefer people answer according to tags related to what OP asked.
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica over 5 years
    @HaSeeBMiR The only reason the answer is related to C instead of C++ is that it uses a C header instead of the equivalent C++ header. The C preprocessor is a part of C++, and cstdint is a part of the C++ standard, as are all of the type names defined there. It is indeed appropriate for the specified tags. ...I do disagree with defining the types manually, but it may be necessary when working with compilers that don't do so.
  • Pryftan
    Pryftan over 4 years
    So there are people who write C++ who have the * by the variable name! Lovely. I cringe every time I see code like long* j; and even worse when it's in C. As for C casts in C++ (for pointers) I believe that more recent versions of GCC complain about it or maybe even restrict it? Or maybe it was the standard? Otherwise it's my imagination - which is entirely possible I suppose.
  • Pryftan
    Pryftan over 4 years
    @PierreBdR Nevertheless he makes a very valid point. It's not always so simple as to look at code (including when compilers warn about it) that uses a signed int but is used for a size and think it okay to change it to unsigned. Unfortunately it's not always that simple. You have to look at each case explicitly unless you want to cause potential bugs - and subtle bugs at that.
  • Konrad Rudolph
    Konrad Rudolph over 3 years
    This answer is incredibly outdated. These days, just #include <cstdint>.
  • Human-Compiler
    Human-Compiler almost 3 years
    @Pryftan It's fine to have a personal preference, but it's quite strange to "cringe" at something that the C++ language is moving towards. Modern C++ (11 onward) has a clear preference for type-aligned qualifiers. For example, you simply cannot align *, &, or && with the name of a variadic packs since it comes before the .... Plus, of course, everything is increasingly left-ot-right with auto: int *get_ptr() becomes auto get_ptr() -> int*, int *p = &q becomes auto p = &q, etc.
  • zjyhjqs
    zjyhjqs almost 2 years
    size_t is a type that can hold any array index, not pointer. It generally works but there’s exception. See also: size_t vs uintptr_t