Converting a pointer into an integer
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.h
— inttypes.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.
Related videos on Youtube
PierreBdR
Updated on February 18, 2022Comments
-
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 over 7 yearsI 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
returns2
forsize_t
. Alex answer below appears correct. Alex's answer anduintptr_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 over 15 yearsSee stackoverflow.com/questions/126279/… for info on how to get a stdint.h that works with MSVC (and possibly Borland).
-
Michael Burr over 15 yearsWhile 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 over 10 yearsUnfortunately the only constraint on
size_t
is that it must hold the result of anysizeof()
. This doesn't necessarily make it 64 bits on x64. see also -
PierreBdR over 9 yearsI 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 almost 9 yearsthe only answer which properly mentions reinterpret_cast
-
linleno over 8 yearsIf you meant to include <cstdint>, you probably also want to use std::uintptr_t instead.
-
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 about 8 years@AndyJost No it cannot. Even your own link confirms that.
-
jww over 7 yearsAwesome... The cast is what I was looking for. If we are told to use
uintptr_t
instead ofsize_t
, then why does it requirereinterpret_cast
? It seems like a simplestatic_cast
should do since the standard specifically provides the compatible data types... -
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 about 6 yearsBoth links broken!
-
PierreBdR about 6 yearsIf 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 about 6 yearsThis answer is related to C but the language is tagged C++ so it not the answer what i was looking for.
-
Haseeb Mir about 6 yearsthis should be marked as selected answer instead, as it provides all the details how to cast in C and C++.
-
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 over 5 years@HaSeeBMiR An appropriate fix is to switch to
<cstdint>
, or to download the appropriatecstdint
if you download astdint.h
. -
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 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 over 4 yearsSo there are people who write C++ who have the
*
by the variable name! Lovely. I cringe every time I see code likelong* 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 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 over 3 yearsThis answer is incredibly outdated. These days, just
#include <cstdint>
. -
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 withauto
:int *get_ptr()
becomesauto get_ptr() -> int*
,int *p = &q
becomesauto p = &q
, etc. -
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
vsuintptr_t