Reputation: 12510
In C, if I have a:
char *ptr;
and if, let's say, ptr
points to address 0xbfc70753
, how can I convert it to an array of char so that I end up with an array of char addr
that will contain:
char addr[4] = "\x53\x07\xc7\xbf"
That is, convert the address pointed at by ptr
to an array of char and make it little-endian.
(Kali Linux, 32 bit, intel)
Upvotes: 0
Views: 5450
Reputation: 4515
In C, pointer width (ie number of bytes used to represent a pointer) is not guaranteed. You know your platform, so you could code just for this, but your code will not be portable in all likelihood.
The technique shown in one of the answers may work, but it has a couple of weaknesses,
char *ptr = ...
char adr[4]; //<< Not a great idea! See below...
*(uint32_t*)adr = (uint32_t)ptr; //<< Not a great idea! See below...
The first weakness is that the size of a pointer may not be 4 bytes. What if, for example, you ever ran you code on a 64-bit system? To get around this problem you will be best using uintptr_t
. The C99 standard (Sect. 7.18.1.4) defines this type as a...
...an unsigned 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...
This article gives a pretty good explanation of why pointer width may vary between architectures and how types such as uintptr_t
, size_t
etc can help out.
The next possible problem is type aliasing. This occurs when two variables point to the same bit of memory. This is a problem because the C compiler will assume that two objects with different effective types will not reference overlapping memory locations.
Effective type is defined in Sect 6.5 as:
The effective type of an object for an access to its stored value is the declared type of the object, if any
Where the issue of type casting and type punning comes in is where the standard says (I've paraphrased a bit to shorten it)...
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a (qualified version of a) type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the (qualified) effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members, or
— a character type.
In the above example the type of adr
is pointer-to-character-array. A 32-bit integer is not a compatible type with char. Therefore this violates the first rule so could result in UB (undefined behavior).
Why should this be a problem? The answer, on some systems, lies in data alignment. This article is very useful on this subject. Some systems may assume and require that access to a uint32_t
type is done on a 4-byte aligned boundary. But, your char
array has no such restriction. Therefore if the array started on a non-4-byte-aligned location, accessing it with the aliasing unsigned integer pointer could cause a hardware exception, for example. The following article talks more about other issues with type aliasing in much greater depth, more specifically to do with type punning too.
Okay, so what can you do to get around this? Something like the following would provide you a solution...
#include <stdio.h>
#include <stdint.h>
int main(void)
{
size_t i;
char const *ptr = "Some string";
char adr[sizeof(void *) + 1]; // Note the +1 to make room for NULL terminator
uintptr_t ptrAddress = (uintptr_t)ptr;
printf("Pointer address is %p\n", ptr);
printf("Converting to 0x");
for(i = 0; i < sizeof(void *); ++i)
{
adr[i] = ptrAddress & 0xff;
printf("%2.2X", (unsigned int)(unsigned char)adr[i]);
ptrAddress >>= 8;
}
printf("\n");
adr[sizeof(void *)] = '\0';
return 0;
}
Now the array adr
will always have the correct size for the size of pointers on your system. It will also store the value of the pointer little endian because the least significant byte of the address is stored in the first array location (lowest address) and so on.
See working example of the above on oneide...
Upvotes: 4
Reputation: 3496
I guess something like that should work: (I didn't test it)
unsigned int mask = 0xFF;
for(unsigned int i=0; i<4; i++)
{
addr[i] = (ptr & mask)>>(8*i);
mask = mask<<8;
}
Upvotes: 0
Reputation: 2138
If you are on a little-endian machine, this is a nice way to do it:
void foo(uint32_t* adr, uint32_t value){*adr = value;}
int main(int argc, char** argc)
{
char *ptr = ...
char adr[4];
foo((uint32_t*)adr,(uint32_t)ptr);
}
The idea is that 32-bit unsigned integers are already in little-endian, so when you copy them to a char array, each byte goes to the place you want.
Upvotes: 1
Reputation: 2373
I did it this way, probably there is a place for UB, but it worked on my machine. The idea is clear i guess, just print the pointer into char array.
int i =9,j;
int *p = &i;
char* pstr = malloc(sizeof(*p)*4);
sprintf(pstr,"%p",p);
for(j=0; j<sizeof(*p)*4;++j)
printf("%c ",pstr[j]);
printf("\n%p",p);
Upvotes: 0