在C中,如果我有:
char *ptr;
如果,假设ptr
指向地址0xbfc70753
,我怎样才能将其转换为char数组,以便最终得到一个char addr
数组,含有:
char addr[4] = "\x53\x07\xc7\xbf"
也就是说,将ptr
指向的地址转换为char数组并使其成为little-endian。
(Kali Linux,32位,英特尔)
答案 0 :(得分:4)
在C中,不保证指针宽度(即用于表示指针的字节数)。您了解自己的平台,因此您可以为此编写代码,但您的代码很可能无法移植。
其中一个答案中显示的技术可能有效,但它有一些缺点,
char *ptr = ...
char adr[4]; //<< Not a great idea! See below...
*(uint32_t*)adr = (uint32_t)ptr; //<< Not a great idea! See below...
第一个缺点是指针的大小可能不是4个字节。例如,如果您在64位系统上运行代码,该怎么办?要解决此问题,最好使用uintptr_t
。 C99标准(第7.18.1.4节)将此类型定义为......
...一个无符号整数类型,其属性为any 有效的void指针可以转换为这种类型,然后转换 返回指向void的指针,结果将比较等于 原始指针...
This article非常好地解释了为什么指针宽度可能因架构而异,以及uintptr_t
,size_t
等类型可以提供帮助。
下一个可能的问题是类型别名。当两个变量指向相同的内存位时会发生这种情况。这是一个问题,因为C编译器会假设具有不同有效类型的两个对象不会引用重叠的内存位置。
有效类型在第6.5节中定义为:
访问其存储值的对象的有效类型是声明的类型 对象,如果有的话
类型转换和类型惩罚的问题出现在标准所说的位置(我已经解释了一下以缩短它)......
对象的存储值只能由左值访问 具有以下类型之一的表达式:
- 与对象的有效类型兼容的(合格的a版本)类型
- 与对象的(合格)有效类型对应的有符号或无符号类型,
- 包含上述类型之一的聚合或联合类型 在其成员中,或
- 字符类型。
在上面的例子中,adr
的类型是指向字符数组的指针。 32位整数不带有char的compatible类型。因此,这违反了第一条规则,因此可能导致UB(未定义的行为)。
为什么这会成为问题?在某些系统上,答案在于数据对齐。 This article对此主题非常有用。某些系统可能会假定并要求在4字节对齐的边界上完成对uint32_t
类型的访问。但是,您的char
数组没有此类限制。因此,如果数组在非4字节对齐的位置上启动,则使用别名无符号整数指针访问它可能会导致硬件异常。 The following article更深入地讨论了类型别名的其他问题,更具体地说是类型惩罚。
好的,那么你能做些什么来绕过这个?以下内容可为您提供解决方案......
#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;
}
现在数组adr
的大小总是与系统上的指针大小一致。它还会存储指针little endian的值,因为地址的最低有效字节存储在第一个数组位置(最低地址),依此类推。
答案 1 :(得分:0)
我是这样做的,可能是UB的地方,但它在我的机器上工作。 我想这个想法很清楚,只需将指针打印到char数组中即可。
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);
答案 2 :(得分:0)
如果您使用的是小端机器,这是一个很好的方法:
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);
}
这个想法是32位无符号整数已经是小端的,所以当你将它们复制到char数组时,每个字节都会转到你想要的位置。
答案 3 :(得分:0)
我想这样的事情应该有效:(我没有测试过它)
unsigned int mask = 0xFF;
for(unsigned int i=0; i<4; i++)
{
addr[i] = (ptr & mask)>>(8*i);
mask = mask<<8;
}