Reputation: 473
I need to tag the address of an int pointer with a 1 in the rightmost bit and store that address somewhere. For example the address at 0x10 would become 0x11.
I have tried doing
int* addr = some_pointer;
int* tagged = (int*) (addr | 0x00000001);
but I get error: invalid operands to binary expression ('int *' and 'int'). How could I go about doing this?
Upvotes: 1
Views: 279
Reputation: 153447
How can I set the rightmost bit of a pointer to 1?
C99/C11 provides optional integer types (u)intptr_t
to covert a void*
pointer to an integer and then back to a pointer. This round trip results in a pointer that is equal in value to the original. It may or may not have the same bit pattern as the original pointer.
#include <stdint.h>
int i = 42;
int* addr = &i;
void *vptr = addr;
uintptr_t uptr = (uintptr_t) vptr;
// Set least significant bit.
uintptr_t uptr1 = uptr | 1;
// or one-liner
uintptr_t uptr1 = ((uintptr_t) (void *) &i) | 1;
There is no print specifier for code can cast to the widest type for display purposes(u)intptr_t
, so
printf("%p --> %ju --> %ju\n", vptr, (uintmax_t) uptr, (uintmax_t) uptr1);
// or use macros from <inttypes.h>
printf("%p --> %" PRIuPTR " --> %" PRIuPTR "\n", vptr, uptr, uptr1);
As long as OP only needs to store the value as an integer, no problem. (u)intptr_t
values that originate from a valid pointer can convert back to a matching pointer type with no problem.
So far so good, yet OP may want to use the manipulated value.
Now the trick is that uptr1
may or may not covert to a valid void *
pointer. Even if it does convert to a valid void *
pointer, the conversion to an int *
may or may not work. These two steps are beyond the C spec - undefined behavior.
// UB
void *vptr1 = (void *) uptr1;
printf("%p\n", vptr1);
int *iptr1 = vptr1;
If code makes it this far, attempting to de-reference this new pointer, it is also UB.
printf("%d\n", *iptr1); // UB, good luck
Upvotes: 7
Reputation: 393
As stated in comments, you need to convert the pointer to integral type explicitly:
int* addr = some_pointer;
int* tagged = (int*) ((uintptr_t)addr | 0x00000001);
Or shorter:
int* tagged = (int*) ((uintptr_t)some_pointer | 0x1);
You may need to include <stdint.h>
in order to use macro define uintptr_t
.
First, bit or operator |
only allows Integral Types as operands.
A bit about the type categories of C in C89 draft. (I didn't check C99, C11)
Integral Type = char, signed and unsigned integer types, enumerated types.
Arithmetic Type = Integral Type and Floating Type
- Scalar Type = Arithmetic Type and Pointer Type
So pointer is not an Integral Type. However, this is not enough to explain the error.
If there is an implicit conversion
rule that automatically converts a pointer type to an integral type, you will be ok. (FYI: implicit conversion
is a set of type conversion rules that will be applied to source automatically)
However, no such rule exists. So, you saw the error.
But sometimes, we really need to do math operations on a pointer variable. This is why we need explicit conversion
.
Upvotes: 1
Reputation: 135
Simple way to do this that will likely annoy your compiler is to use address of operator int* ptr = &var;
.
Below is my program:
#include <stdio.h>
int main(int argc, char *argv[]) {
int val = 5;
int* ptr = &val;
int* addr = (int*)((int)&ptr | 0x00000001);
printf("address val: %x, ptr: %x, &ptr: %x, addr: %x \n", &val, ptr, &ptr, addr);
return 0;
}
The output of which is:
address val: 5880dacc, ptr: 5880dacc, &ptr: 5880dac0, addr: 5880dac1
Upvotes: 0