Related
This is the code I'm testing:
int value = 0;
void* addyvoid = static_cast<void*>(&value); // C++-style cast.
it works perfectly, but I could use uintptr_t / intptr_t. But they are not good for holding pointers as people said here because they are too big. So, is this true? If yes, however, for holding pointers using void* would be better, but will there be a loss of data?
The purpose of intptr_t and uintptr_t is that in some applications, you actually do need to do some sort of numeric computation on pointer values, perhaps by flipping individual bits, perhaps by XORing them, etc. In those cases, when you need to work with the numeric value of a pointer, intptr_t and uintptr_t are integer types that (if they exist) are guaranteed to be large enough to hold any pointer. This is not true of, say, int, since int's size relative to pointer sizes isn't specified.
Because it's fundamentally unsafe to do these conversions, C++ requires that you use reinterpret_cast to convert to and from intptr_t and uintptr_t and pointer types.
If all that you're doing is storing "a pointer to something," and provided that pointer isn't a function pointer or a member function pointer, you can just cast it to void*. That cast is guaranteed to work and the conversion from void* back to the original type only requires a static_cast and is guaranteed to be safe.
The size of intptr_t and uintptr_t isn't a good reason to avoid them. They're just for different applications. If you need to do numeric computations on pointers, use those types. Otherwise, if you just need to store "a pointer to something," use a void*.
I refereed the following link,
Link1
Link 2
In the above link1 it was mentioned in answer that "Pointers are of pointer type".
I just need to know is pointer is a data type or not.
No one has answered that question in single word. It is datatype or not?
This is the second link which i refered says that
Pointers are simply a variable that hold an address so one could argue that a pointer is a data type, but it is not defined as a data type (per "The C Programming Language". Kernighan & Ritchie).
Yes, a pointer is a data type. The purest form of which (mainly talking about C here) is void *. A void * can be used to pass a memory address around (which is what a pointer is), but it can't be dereferenced. Dereferencing a pointer is what you do to get at the data contained at the memory location the pointer is pointing at, which implies that you know what type of data you're reading from the memory. The type determines how much memory will be read, and because a void is "nothing". A void * can be set to point at any block of memory, that can contain any type, so you can cast a void * to any other pointer type (int *, for example), and dereference that, instead.
Each type we have is used to store a specific piece of data (value), we use a char to store a single character, an int to store an integer, double to store double precision decimals and so on. None of these types are used to store locations in memory, apart from pointers. So just like the other types, a pointer is used to store a specific piece of data. And the mother of all pointers is void *.
Sadly, this void * is rather restricted: you can't dereference it, you can't use it for pointer arithmetic (not according to the standard anyway). So C provides you with a series of derived pointer types, that make life easier: char *, int *, double * and so on.
What they are, really, is short-hand for: (char *) void * my_ptr;
Some more attempts at making my point as clearly as possible:
Pointers have their own size, irrespective of the type they're said to point at:
char a_character = 'a'; //type: a char
char *a_char_ptr = &a_character; //memory address, in this case, the one holding a_charachter
The distinction is probably best seen by looking at the sizes of both these vars:
printf("%zu <> %zu\n", sizeof a_character, sizeof a_char_ptr);
The code above will give you something like "1 <> 8" or "1 <> 4", depending on what system you're on. Numbers represent the size, in bytes.
Pointers also have their own printf format specifier: %p:
printf("%c is the value stored at %p\n", *a_char_ptr, (void *) a_char_ptr);
To print the actual memory address (the actual value of a pointer), you are required to cast the pointer to the generic void * type. A void pointer is sort of the generic pointer; it's the pointer that makes no assumptions as to the data it is pointing at. This is what malloc, calloc and realloc return, a generic pointer, that can be set to point at any other type. So what is a char *? It's a generic pointer type, set to point at blocks of memory of 1 byte in size (sizeof(char)). In a sense, a typed pointer, then, is a derived type, but think of it like this: char * is short for (char *) void *my_ptr;
But really, what is a type? The gist of it is that a type is way to determine how data in memory is supposed to be interpreted. A variable of the type char represents a character. A variable of the type int represents an integer. Same applies to pointers: char *x is not of the type char, it's of the type char * (pointer to char). This means that char *x itself is a location in memory we can use to read one or more char values.
I could rant on for a while but TL;TR:
Yes, a pointer is a data type (void * in its purest form). The pure form is quite unusable (because you can't dereference it). Instead of having to cast the pointer every time you decide to use it, C offers the convenience of derived pointer types (like char *, int * and so on). But really, they're pointers, and therefore a data-type in their own right.
You've asked two different questions.
Your title asks "What is the data type of pointer variables?". The answer is simple: a pointer variable is of some pointer type. For example, given:
int *ptr;
ptr is a pointer object, and its type is int*, which is a pointer type.
The body of your question asks whether "a pointer is a data type or not". By any reasonable definition of the phrase "data type", pointer types are data types.
The C standard never defines the phrase "data type", but it does use it (informally) in several places. It happens that none of the uses of the phrase "data type" in the standard refer to pointer types, but that doesn't tell us anything.
The standard says that all types are either function types or object types. Object types are further divided into a number of categories: integer types, array types, structure types, union types, pointer types, etc. A pointer type can be a pointer to an object type or a pointer to a function type. (It can be a pointer to an incomplete object type; as of the 2011 standard, incomplete types are classified as object types.)
Another ambiguity in your question is your use of the word "pointer". The word "pointer" by itself commonly refers to an object of pointer type, but it can also refer to a value of pointer type (for example, the standard says that malloc returns a pointer). It's better to use "pointer" as an adjective rather than as a noun, so you can have:
a pointer type;
a pointer object (an object of pointer type);
a pointer expression (an expression that yields a result of pointer type); or
a pointer value (the value, of pointer type, yielded by a pointer expression).
A pointer type is an object type. A pointer object is an object; an object is defined by the standard as a "region of data storage in the execution environment, the contents of which can represent values". So a pointer object is a region of data storage.
Following your Link 2, some random person on the Internet wrote that "Pointers are simply a variable that hold an address so one could argue that a pointer is a data type, but it is not defined as a data type (per "The C Programming Language". Kernighan & Ritchie)". I don't know whether K&R defines the term "data type"; since this person didn't provide a specific citation, it's difficult to tell without searching the book. But it's the standard, not K&R, that defines the language.
I'm curious: why would you think that a pointer type wouldn't be considered a data type?
For example in the C Standard there is no formal definition of the term data type. There are object types and function types. At the same time pointers are derived types constructed from object and function types.
Thus in general case pointers are data types that is they are data types that are constructed from object and function types.
Also there is definition of term object in the C Standard
3.15
1 object
region of data storage in the execution environment, the contents of which can represent
values
So there is some contradiction in the Standard. On the one hand pointers are objects because they occupy memory and the memory represents their values. So we may say that pointers are object types. On the other hand pointers are considered as derived types from object types.
In my opinion it would be better if there would be explicitly written in the Standard that pointers are derived object types or derived function types.
In any case you may bravely say that pointers are data types!:)
Yes, pointer is a data type and a pointer variable store that pointer data type.
Pointer is a data-type. So we can create pointer variables which can hold the address of memory location.
Pointer types are data types; they store pointer values.
There is no one single pointer type; a pointer to int is a different type from a pointer to char, which is a different type from a pointer to double, which is a different type from a pointer to a 10-element array of int, which is a different type from a pointer to an 11-element array of int, etc.
Different pointer types may have different sizes and representations; the only pointer types that are guaranteed to have the same sizes and representations are void * and char *.
Your question probably refers to the "data type of a pointer", in contrast to the data type of the pointed-to data, which is what one would understand in the first place.
Based on this assumption, then please look at type uintptr_t or void*.
To quote Drew Dorman's answer: "uintptr_t is an unsigned integer type that is capable of storing a pointer. Which typically means that it's the same size as a pointer"
Of course, its size is platform-dependant: 32 bit or 64 bit. So don't transport this variable across platforms of different size.
Please note, to assign it you have to cast from the 'specific' pointer type, to the 'generic' one:
int var = 1;
int* addrOfVar = &var; // pointer to variable
uintptr_t pVar = (uintptr_t)&var;
uintptr_t pVar2 = reinterpret_cast<uintptr_t>(&var); // alternative cast
See a pointer variable stores the address of another variable. And when we access this pointer variable it points to the address of variable which eventually directs us to the data stored inside that variable. Now depending upon the type of data stored inside the original variable we can specify the data type of that pointer variable.
I just need to know is pointer is a data type or not.
All pointer types are data types, but plain "pointer" is not a data type. More precisely, "pointer" is a type modifier that you can apply to absolutely any type. So you can have pointer-to-char, and pointer-to-long, and pointer-to-double, not to mention pointer-to-pointer-to-char, etc.
There are three of these type modifiers in C: pointer-to, array-of, and function-returning. These can be combined in almost any combination. (But, to be sure, a few combinations are invalid. You can't have arrays of functions, or functions returning arrays.)
The thing is Pointer itself is a data type of it's own. It basically store a memory address.
Now it can be used to store a memory address of any variable too.
Now we know that doing out-of-bounds-pointer-arithmetic has undefined behavior as described in this SO question.
My question is: can we workaround such restriction by casting to std::uintptr_t for arithmetic operations and then cast back to pointer? is that guaranteed to work?
For example:
char a[5];
auto u = reinterpret_cast<std::uintptr_t>(a) - 1;
auto p = reinterpret_cast<char*>(u + 1); // OK?
The real world usage is for optimizing offsetted memory access -- instead of p[n + offset], I want to do offset_p[n].
EDIT To make the question more explicit:
Given a base pointer p of a char array, if p + n is a valid pointer, will reinterpret_cast<char*>(reinterpret_cast<std::uintptr_t>(p) + n) be guaranteed to yield the same valid pointer?
No, uintptr_t cannot be meaningfully used to avoid undefined behavior when performing pointer arithmetic.
For one thing, at least in C there is no guarantee that uintptr_t even exists. The requirement is that any value of type void* may be converted to uintptr_t and back again, yielding the original value without loss of information. In principle, there might not be any unsigned integer type wide enough to hold all pointer values. (I presume the same applies to C++, since C++ inherits most of the C standard library and defines it by reference to the C standard.)
Even if uintptr_t does exist, there is no guarantee that a given arithmetic operation on a uintptr_t value does the same thing as the corresponding operation on a pointer value.
For example, I've worked on systems (Cray vector systems, T90 and SV1) on which byte pointers are implemented in software. A native address is a 64-bit address that refers to a 64-bit word; there is no hardware support for byte addressing. A char* or void* pointer consists of a word pointer with a 3-bit offset stored in the otherwise unused high-order bits. Conversion between integers and pointers simply copies the bits. So incrementing a char* would advance it to point to the next 8-bit byte in memory; incrementing a uintptr_t obtained by converting a char* would advance it to point to the next 64-bit word.
That's just one example. More generally, conversions between pointers and integers are implementation-defined, and the language standard makes no guarantee about the semantics of those conversions (other than, in some cases, converting back to a pointer).
So yes, you can convert a pointer value to uintptr_t (if that type exists) and perform arithmetic on it without risking undefined behavior -- but the result may or may not be meaningful.
It happens that, on most systems, the mapping between pointers and integers is simpler, and you probably can get away with that kind of game. But you're better off using pointer arithmetic directly, and just being very careful to avoid any invalid operations.
Yes, that is legal, but you must reinterpret_cast exactly the same uintptr_t value back to char*.
(Therefore, what it you're intending to do is illegal; that is, converting a different value back to a pointer.)
5.2.10 Reinterpret cast
4 . A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is
implementation-defined.
5 . A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted
to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type
will have its original value;
(Note that there'd be no way, in general, for the compiler to know that you subtracted one and then added it back.)
Is this statement correct? Can any "TYPE" of pointer can point to any other type?
Because I believe so, still have doubts.
Why are pointers declared for definite types? E.g. int or char?
The one explanation I could get was: if an int type pointer was pointing to a char array, then when the pointer is incremented, the pointer will jump from 0 position to the 2 position, skipping 1 position in between (because int size=2).
And maybe because a pointer just holds the address of a value, not the value itself, i.e. the int or double.
Am I wrong? Was that statement correct?
Pointers may be interchangeable, but are not required to be.
In particular, on some platforms, certain types need to be aligned to certain byte-boundaries.
So while a char may be anywhere in memory, an int may need to be on a 4-byte boundary.
Another important potential difference is with function-pointers.
Pointers to functions may not be interchangeable with pointers to data-types on many platforms.
It bears repeating: This is platform-specific.
I believe Intel x86 architectures treat all pointers the same.
But you may well encounter other platforms where this is not true.
Every pointer is of some specific type. There's a special generic pointer type void* that can point to any object type, but you have to convert a void* to some specific pointer type before you can dereference it. (I'm ignoring function pointer types.)
You can convert a pointer value from one pointer type to another. In most cases, converting a pointer from foo* to bar* and back to foo* will yield the original value -- but that's not actually guaranteed in all cases.
You can cause a pointer of type foo* to point to an object of type bar, but (a) it's usually a bad idea, and (b) in some cases, it may not work (say, if the target types foo and bar have different sizes or alignment requirements).
You can get away with things like:
int n = 42;
char *p = (char*)&n;
which causes p to point to n -- but then *p doesn't give you the value of n, it gives you the value of the first byte of n as a char.
The differing behavior of pointer arithmetic is only part of the reason for having different pointer types. It's mostly about type safety. If you have a pointer of type int*, you can be reasonably sure (unless you've done something unsafe) that it actually points to an int object. And if you try to treat it as an object of a different type, the compiler will likely complain about it.
Basically, we have distinct pointer types for the same reasons we have other distinct types: so we can keep track of what kind of value is stored in each object, with help from the compiler.
(There have been languages that only have untyped generic pointers. In such a language, it's more difficult to avoid type errors, such as storing a value of one type and accidentally accessing it as if it were of another type.)
Any pointer can refer to any location in memory, so technically the statement is correct. With that said, you need to be careful when reinterpreting pointer types.
A pointer basically has two pieces of information: a memory location, and the type it expects to find there. The memory location could be anything. It could be the location where an object or value is stored; it could be in the middle of a string of text; or it could just be an arbitrary block of uninitialised memory.
The type information in a pointer is important though. The array and pointer arithmetic explanation in your question is correct -- if you try to iterate over data in memory using a pointer, then the type needs to be correct, otherwise you may not iterate correctly. This is because different types have different sizes, and may be aligned differently.
The type is also important in terms of how data is handled in your program. For example, if you have an int stored in memory, but you access it by dereferencing a float* pointer, then you'll probably get useless results (unless you've programmed it that way for a specific reason). This is because an int is stored in memory differently from the way a float is stored.
Can any "TYPE" of pointer can point to any other type?
Generally no. The types have to be related.
It is possible to use reinterpret_cast to cast a pointer from one type to another, but unless those pointers can be converted legally using a static_cast, the reinterpret_cast is invalid. Hence you can't do Foo* foo = ...; Bar* bar = (Bar*)foo; unless Foo and Bar are actually related.
You can also use reinterpret_cast to cast from an object pointer to a void* and vice versa, and in that sense a void* can point to anything -- but that's not what you seem to be asking about.
Further you can reinterpret_cast from object pointer to integral value and vice versa, but again, not what you appear to be asking.
Finally, a special exception is made for char*. You can initialize a char* variable with the address of any other type, and perform pointer math on the resulting pointer. You still can't dereference thru the pointer if the thing being pointed to isn't actually a char, but it can then be casted back to the actual type and used that way.
Also keep in mind that every time you use reinterpret_cast in any context, you are dancing on the precipice of a cliff. Dereferencing a pointer to a Foo when the thing it actually points to is a Bar yields Undefined Behavior when the types are not related. You would do well to avoid these types of casts at all costs.
Some pointers are more equal than others...
First of all, not all pointers are necessarily the same thing. Function pointers can be something very different from data pointers, for instance.
Aside: Function pointers on PPC
On the PPC platform, this was quite obvious: A function pointer was actually two pointers under the hood, so there was simply no way to meaningfully cast a function pointer to a data pointer or back. I.e. the following would hold:
int* dataP;
int (*functionP)(int);
assert(sizeof(dataP) == 4);
assert(sizeof(functionP) == 8);
assert(sizeof(dataP) != sizeof(functionP));
//impossible:
//dataP = (int*)functionP; //would loose information
//functionP = (int (*)(int))dataP; //part of the resulting pointer would be garbage
Alignment
Furthermore, there is problems with alignment: Depending on the platform some data types may need to be aligned in memory. This is especially common with vector data types, but could apply to any type larger than a byte. For instance, if an int must be 4 byte aligned, the following code might crash:
char a[4];
int* alias = (int*)a;
//int foo = *alias; //may crash because alias is not aligned properly
This is not an issue if the pointer comes from a malloc() call, as that is guaranteed to return sufficiently aligned pointers for all types:
char* a = malloc(sizeof(int));
int* alias = (int*)a;
*alias = 0; //perfectly legal, the pointer is aligned
Strict aliasing and type punning
Finally, there are strict aliasing rules: You must not access an object of one type through a pointer to another type. Type punning is forbidden:
assert(sizeof(float) == sizeof(uint32_t));
float foo = 42;
//uint32_t bits = *(uint32_t*)&foo; //type punning is illegal
If you absolutely must reinterpret a bit pattern as another type, you must use memcpy():
assert(sizeof(float) == sizeof(uint32_t));
float foo = 42;
uint32_t bits;
memcpy(&bits, &foo, sizeof(bits)); //bit pattern reinterpretation is legal when copying the data
To allow memcpy() and friends to actually be implementable, the C/C++ language standards provide for an exception for char types: You can cast any pointer to a char*, copy the char data over to another buffer, and then access that other buffer as some other type. The results are implementation defined, but the standards allow it. Use cases are mostly general data manipulation routines like I/O, etc.
TL;DR:
Pointers are much less interchangeable than you think. Don't reinterpret pointers in any other way than to/from char* (check alignment in the "from" case). And even that does not work for function pointers.
Is it safe to cast pointer to int and later back to pointer again?
How about if we know if the pointer is 32 bit long and int is 32 bit long?
long* juggle(long* p) {
static_assert(sizeof(long*) == sizeof(int));
int v = reinterpret_cast<int>(p); // or if sizeof(*)==8 choose long here
do_some_math(v); // prevent compiler from optimizing
return reinterpret_cast<long*>(v);
}
int main() {
long* stuff = new long(42);
long* ffuts = juggle(stuff);
std::cout << "Is this always 42? " << *ffuts << std::endl;
}
Is this covered by the Standard?
No.
For instance, on x86-64, a pointer is 64-bit long, but int is only 32-bit long. Casting a pointer to int and back again makes the upper 32-bit of the pointer value lost.
You may use the intptr_t type in <cstdint> if you want an integer type which is guaranteed to be as long as the pointer. You could safely reinterpret_cast from a pointer to an intptr_t and back.
Yes, if... (or "Yes, but...") and no otherwise.
The standard specifies (3.7.4.3) the following:
A pointer value is a safely-derived pointer [...] if it is the result of a well-defined pointer conversion or reinterpret_cast of a safely-derived pointer value [or] the result of a reinterpret_cast of an integer representation of a safely-derived pointer value
An integer value is an integer representation of a safely-derived pointer [...] if its type is at least as large as std::intptr_t and [...] the result of a reinterpret_cast of a safely-derived pointer value [or]
the result of a valid conversion of an integer representation of a safely-derived pointer value [or] the result of an additive or bitwise operation, one of whose operands is an integer representation of a
safely-derived pointer value
A traceable pointer object is [...] an object of an integral type that is at least as large as std::intptr_t
The standard further states that implementations may be relaxed or may be strict about enforcing safely-derived pointers. Which means it is unspecified whether using or dereferencing a not-safely-derived pointer invokes undefined behavior (that's a funny thing to say!)
Which alltogether means no more and no less than "something different might work anyway, but the only safe thing is as specified above".
Therefore, if you either use std::intptr_t in the first place (the preferrable thing to do!) or if you know that the storage size of whatever integer type you use (say, long) is at least the size of std::intptr_t, then it is allowable and well-defined (i.e. "safe") to cast to your integer type and back. The standard guarantees that.
If that's not the case, the conversion from pointer to integer representation will probably (or at least possibly) lose some information, and the conversion back will not give a valid pointer. Or, it might by accident, but this is not guaranteed.
An interesting anecdote is that the C++ standard does not directly define std::intptr_t at all; it merely says "the same as 7.18 in the C standard".
The C standard, on the other hand, states "designates a signed integer type with the property that any valid
pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer".
Which means, without the rather complicated definitions above (in particular the last bit of the first bullet point), it wouldn't be allowable to convert to/from anything but void*.
Yes and no.
The language specification explicitly states that it is safe (meaning that in the end you will get the original pointer value) as long as the size of the integral type is sufficient to store the [implementation-dependent] integral representation of the pointer.
So, in general case it is not "safe", since in general case int can easily turn out to be too small. In your specific case it though it might be safe, since your int might be sufficiently large to store your pointer.
Normally, when you need to do something like that, you should use the intptr_t/uintptr_t types, which are specifically introduced for that purpose. Unfortunately, intptr_t/uintptr_t are not the part of the current C++ standard (they are standard C99 types), but many implementations provide them nevertheless. You can always define these types yourself, of course.
In general, no; pointers may be larger than int, in which case there's no way to reconstruct the value.
If an integer type is known to be large enough, then you can; according to the Standard (5.2.10/5):
A pointer converted to an integer of sufficient size ... and back to the same pointer type will have its original value
However, in C++03, there's no standard way to tell which integer types are large enough. C++11 and C99 (and hence in practice most C++03 implementations), and also Boost.Integer, define intptr_t and uintptr_t for this purpose. Or you could define your own type and assert (preferably at compile time) that it's large enough; or, if you don't have some special reason for it to be an integer type, use void*.
Is it safe? Not really.
In most circumstances, will it work? Yes
Certainly if an int is too small to hold the full pointer value and truncates, you won't get your original pointer back (hopefully your compiler will warn you about this case, with GCC truncating conversions from pointer to integers are hard errors). A long, or uintptr_t if your library supports it, may be better choices.
Even if your integer type and pointer types are the same size, it will not necessarily work depending on your application runtime. In particular, if you're using a garbage collector in your program it might easily decide that the pointer is no longer outstanding, and when you later cast your integer back to a pointer and try to dereference it, you'll find out the object was already reaped.
Absolutely not. Doing some makes a bad assumption that the size of an int and a pointer are the same. This is almost always no the case on 64 bit platforms. If they are not the same a precision loss will occur and the final pointer value will be incorrect.
MyType* pValue = ...
int stored = (int)pValue; // Just lost the upper 4 bytes on a 64 bit platform
pValue = (MyType*)stored; // pValue is now invalid
pValue->SomeOp(); // Kaboom
No, it is not (always) safe (thus not safe in general). And it is covered by the standard.
ISO C++ 2003, 5.2.10:
A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined.
A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined.
(The above emphases are mine.)
Therefore, if you know that the sizes are compatible, then the conversion is safe.
#include <iostream>
// C++03 static_assert.
#define ASSURE(cond) typedef int ASSURE[(cond) ? 1 : -1]
// Assure that the sizes are compatible.
ASSURE(sizeof (int) >= sizeof (char*));
int main() {
char c = 'A';
char *p = &c;
// If this program compiles, it is well formed.
int i = reinterpret_cast<int>(p);
p = reinterpret_cast<char*>(i);
std::cout << *p << std::endl;
}
Use uintptr_t from "stdint.h" or from "boost/stdint.h". It is guaranteed to have enough storage for a pointer.
No it is not. Even if we rule out the architecture issue, size of a pointer and an integer have differences. A pointer can be of three types in C++ : near, far, and huge. They have different sizes. And if we talk about an integer its normally of 16 or 32 bit. So casting integer into pointers and vice-verse is not safe. Utmost care has to be taken, as there very much chances of precision loss. In most of the cases an integer will be short of space to store a pointer, resulting in loss of value.
If your going to be doing any system portable casting, you need to use something like Microsofts INT_PTR/UINT_PTR, the safety after that relies on the target platforms and what you intend doing to the INT_PTR. generally for most arithmatic char* or uint_8* works better while being typesafe(ish)
To an int ? not always if you are on a 64 bit machine then int is only 4 bytes, however pointers are 8 bytes long and thus you would end up with a different pointer when you cast it back from int.
There are however ways to get around this. You can simply use an 8 byte long data type ,which would work whether or not you are on 32/64 bit system, such as unsigned long long unsigned because you don't want sign extension on 32-bit systems.
It is important to note that on Linux unsigned long will always be pointer size* so if you are targeting Linux systems you could just use that.
*According to cppreference and also tested it myself but not on all Linux and Linux like systems
If the issue is that you want to do normal math on it, probably the safest thing to do would be to cast it to a pointer to char (or better yet, * uint8_t), do your math, and then cast it back.