这是我的代码
struct ukai { int val[1]; };
struct kai { struct ukai daddr; struct ukai saddr; };
struct kai *k, uk;
uk.saddr.val[0] = 5;
k = &uk;
k->saddr.val[0] = 6;
unsigned int *p = (unsigned int *)malloc(sizeof(unsigned int));
p[0] = k;
int *vp;
vp = ((uint8_t *)p[0] + 4);
printf("%d\n", *vp);
这会产生分段错误。但是,如果我们用printf("%u\n", vp)
替换最后一行,它会给出地址,即&(k->saddr.val[0])
。但是我无法使用p[0]
打印地址中的值,但能够使用k->saddr.val[0]
打印它。
我必须以某种方式使用p
指针来访问val[0]
的值,我不能使用指针k
。我在这里需要帮助,无论是否可能,请告诉我。
在这里完成的地址有很多“脏”的混乱。从标准C的角度来看,不推荐甚至禁止使用其中一些内容。
然而,当用户已知某些编译器实现细节时,这种指针/地址调整通常用于低级编程(嵌入式,固件等)。当然这样的代码不可移植。
无论如何,这里的问题(在评论部分获得更多细节之后)是运行此代码的机器是64位。因此,指针是64位宽度,而int
或unsigned int
是32位宽度。
所以当在k
存储p[0]
的地址时
p[0] = k;
虽然p[0]
是unsigned int
类型而k
是指向struct kai
的类型指针,但k
值的高32位被截断。
要解决此问题,最好的方法是使用uintptr_t
,因为此类型总是具有适当的宽度来保存完整的地址值。
uintptr_t *p = malloc(sizeof(uintptr_t));
注意:uintptr_t
是可选的,但很常见。对于void*
来说就足够了,但也许不是函数指针。对于兼容代码,正确使用uintptr_t
包括对象指针 - > void *
- > uintptr_t
- > void *
- >对象指针。
代码毫无意义:
p[0] = k;
将指针k
的值转换为int
,因为p
是指向int
的指针。这是实现定义,如果指针大于类型int
,则会丢失信息。vp = ((uint8_t *)p[0] + 4);
将int
指向的p
转换为指向unsigned char的指针,并使vp
指向超出此指针的4个字节的位置。如果指针大于int
,则具有未定义的行为。只是打印这个虚假指针的值可能没问题,但解除引用它有未定义的行为。printf("%u\n", vp)
使用不正确的格式指针vp
,再次这是未定义的行为,虽然它不太可能崩溃。问题很可能与指针和整数的大小有关:如果将此代码编译为64位,则指针大于整数,因此将一个指针转换为另一个指令会丢失信息。
这是一个更正版本:
struct ukai { int val[1]; };
struct kai { struct ukai daddr; struct ukai saddr; };
struct kai *k, uk;
uk.saddr.val[0] = 5;
k = &uk;
k->saddr.val[0] = 6;
int **p = malloc(sizeof *p);
p[0] = k;
int *vp = (int *)((uint8_t *)p[0] + sizeof(int));
printf("%d\n", *vp); // should print 6