[c++] Converting a pointer into an integer

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 ?

This question is related to c++ gcc casting 64-bit 32-bit

The answer is


Use uintptr_t as your integer type.


Since uintptr_t is not guaranteed to be there in C++/C++11, if this is a one way conversion you can consider uintmax_t, always defined in <cstdint>.

auto real_param = reinterpret_cast<uintmax_t>(param);

To play safe, one could add anywhere in the code an assertion:

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");

I think the "meaning" of void* in this case is a generic handle. It is not a pointer to a value, it is the value itself. (This just happens to be how void* is used by C and C++ programmers.)

If it is holding an integer value, it had better be within integer range!

Here is easy rendering to integer:

int x = (char*)p - (char*)0;

It should only give a warning.


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.


I came across this question while studying the source code of SQLite.

In the sqliteInt.h, there is a paragraph of code defined a macro convert between integer and pointer. The author made a very good statement first pointing out it should be a compiler dependent problem and then implemented the solution to account for most of the popular compilers out there.

#if defined(__PTRDIFF_TYPE__)  /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__)       /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X)  ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X)  ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H)   /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X)  ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(intptr_t)(X))
#else                          /* Generates a warning - but it always works     */
# define SQLITE_INT_TO_PTR(X)  ((void*)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(X))
#endif

And here is a quote of the comment for more details:

/*
** The following macros are used to cast pointers to integers and
** integers to pointers.  The way you do this varies from one compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
**
** Ticket #3860:  The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct.  But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/

Credit goes to the committers.


'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.


The best thing to do is to avoid converting from pointer type to non-pointer types. However, this is clearly not possible in your case.

As everyone said, the uintptr_t is what you should use.

This link has good info about converting to 64-bit code.

There is also a good discussion of this on comp.std.c


  1. #include <stdint.h>
  2. Use uintptr_t standard type defined in the included standard header file.

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 is different casts. 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


Examples related to c++

Method Call Chaining; returning a pointer vs a reference? How can I tell if an algorithm is efficient? Difference between opening a file in binary vs text How can compare-and-swap be used for a wait-free mutual exclusion for any shared data structure? Install Qt on Ubuntu #include errors detected in vscode Cannot open include file: 'stdio.h' - Visual Studio Community 2017 - C++ Error How to fix the error "Windows SDK version 8.1" was not found? Visual Studio 2017 errors on standard headers How do I check if a Key is pressed on C++

Examples related to gcc

Can't compile C program on a Mac after upgrade to Mojave Compiling an application for use in highly radioactive environments Make Error 127 when running trying to compile code How to Install gcc 5.3 with yum on CentOS 7.2? How does one set up the Visual Studio Code compiler/debugger to GCC? How do I set up CLion to compile and run? CMake error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found How to printf a 64-bit integer as hex? Differences between arm64 and aarch64 Fatal error: iostream: No such file or directory in compiling C program using GCC

Examples related to casting

Subtracting 1 day from a timestamp date Cast object to interface in TypeScript TypeScript enum to object array Casting a number to a string in TypeScript Hive cast string to date dd-MM-yyyy Casting int to bool in C/C++ Swift double to string No function matches the given name and argument types C convert floating point to int PostgreSQL : cast string to date DD/MM/YYYY

Examples related to 64-bit

Android Studio: /dev/kvm device permission denied How to printf a 64-bit integer as hex? Unable to install Android Studio in Ubuntu I cannot start SQL Server browser Class not registered Error Excel VBA Code: Compile Error in x64 Version ('PtrSafe' attribute required) MSOnline can't be imported on PowerShell (Connect-MsolService error) How to know installed Oracle Client is 32 bit or 64 bit? The application was unable to start correctly (0xc000007b) How to specify 64 bit integers in c

Examples related to 32-bit

Class not registered Error Running vbscript from batch file How do I detect whether 32-bit Java is installed on x64 Windows, only looking at the filesystem and registry? Skipping Incompatible Libraries at compile How do I run a VBScript in 32-bit mode on a 64-bit machine? Are 64 bit programs bigger and faster than 32 bit versions? How can I tell if I'm running in 64-bit JVM or 32-bit JVM (from within a program)? "An attempt was made to load a program with an incorrect format" even when the platforms are the same How to compile a 32-bit binary on a 64-bit linux machine with gcc/cmake Java 32-bit vs 64-bit compatibility