结构体的指针转换成void*的时候……数据丢失?

softwarewander 2006-12-14 11:15:26
问题如下:

比如结构体的定义
typedef struct tag_st
{
char id[10];
float fa[2];
}ST;

我在程序里面这样使用的

ST * P=(STU *)malloc(sizeof(STU));
strcpy(P->id,"hello!");
P->fa[0]=1.1;
P->fa[1]=2.1;

ST * Q=(STU *)malloc(sizeof(STU));
strcpy(Q->id,"world!");
Q->fa[0]=3.1;
Q->fa[1]=4.1;

void ** plink=(void **)P;
*plink =(void *)Q; //执行完此句之后,发现p当中的id变成了垃圾字符,而fa中的数据完整无损? 为什么?



...全文
1733 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
msgsnd 2006-12-16
  • 打赏
  • 举报
回复
通信协议中经常用这种转换//笑话
数据已经被破坏了,典型的指针使用不当。
msgsnd 2006-12-16
  • 打赏
  • 举报
回复
我觉得这个问题的关键在不同级别的指针的转换
void ** plink=(void **)P;//一级指针转换成二级指针
*plink =(void *)Q; //一级指针的转换
这就相当于:
p=&Q;
能不出问题吗?
softwarewander 2006-12-16
  • 打赏
  • 举报
回复
楼上各位说都是可以解释通的。谢谢各位。但我还是在某个地方卡住了,总觉得有点别扭
如果按照各位所说的,那么就和我原来的一些基本的东西的理解有冲突了

现在所有的问题就在于,执行此句的时候,void ** plink=(void **)P;编译器到底做了些什么?


P不是左值,怎么会将P提升成为二级指针呢? 也就是说如果下一句是**P的话绝对会错误的。

我觉得应该是把P的自身地址作为plink的内容存放了。只有这样才能保证对plink作两次间接寻址后得到的内容和 对P作一次得到的内容相同。***** 呵呵不过当我调试的时候发现我的这认为是错的。


看来钻牛角尖出不来了 呵呵




left_zxp 2006-12-16
  • 打赏
  • 举报
回复
void ** plink=(void **)P;
*plink =(void *)Q;
P原来是一个ST的指针吧(ST *),那么void ** plink=(void **)P是把P强制从一级指针转换为二级指针了,这样*plink应该是等于*P,而*P是对P进行间接寻址,也就是那个ST的对象,
*plink =(void *)Q;
让一个ST对象的前4个字节变成Q的内容(也就是一个ST指针的内容)当然是覆盖了原来的值了。
msgsnd 2006-12-16
  • 打赏
  • 举报
回复
#include "stdlib.h"
#include "string.h"

typedef struct
{
char id[10];
float fa[2];
}ST;

void main()
{
ST * P=(ST*)malloc(sizeof(ST));
strcpy(P->id,"hello!");
P->fa[0]=1.1;
P->fa[1]=2.1;

ST * Q=(ST*)malloc(sizeof(ST));
strcpy(Q->id,"world!");
Q->fa[0]=3.1;
Q->fa[1]=4.1;

void ** plink=(void **)P;
*plink =(void *)Q;

int a1=(int)Q;
int a2=0;
int* temp=&a2;
memcpy(temp, P->id, 4);
if(a1==a2)
printf("error");
else
printf("ok");
}
hy_number_one 2006-12-15
  • 打赏
  • 举报
回复
晕,看的我也头晕
softwarewander 2006-12-15
  • 打赏
  • 举报
回复
以前还没有怎么注意。 再看了调试时候的内存值,
void ** plink=(void **)P执行之后确实 plink=0x20000。即plink=P

但是仔细想想, 好像这个时候的plink好像从二级指针退化成了一级指针了阿。 按照 plink的定义 *plink还应该是一个指针(指向st1的地址)才对啊。 对于2级指针在解除引用的时候应该是2次间接寻址的。但是这样呢似乎 就有点不对了。 哎! 不细想还不晕,这样反倒头大了……
短歌如风 2006-12-15
  • 打赏
  • 举报
回复
> void ** plink=(void **)P// plink == 0x20000 那么此时的 *plink呢? 是多少?

这时的*plink就是*P,也就是那个ST1。至于把ST1解释为(void*)类型之后是多少,取决于编译器。在目前的常用的经x86 32位编译器下,一般应该是'l' * 0x1000000+ 'l' * 0x10000 + 'e' * 0x100 + 'h'
kouzhongling 2006-12-15
  • 打赏
  • 举报
回复
void ** plink=(void **)P 可以理解成 void ** plink=void **P (我认为加不加括号都一样)

因此

* plink等价于*P

* plink=0xXXXX20040 等价于*P = 0xXXXX20040

等价于
ST1前32位等于0xXXXX20040

softwarewander 2006-12-15
  • 打赏
  • 举报
回复
void ** plink=(void **)P// plink == 0x20000 那么此时的 *plink呢? 是多少?
短歌如风 2006-12-15
  • 打赏
  • 举报
回复
void ** plink=(void **)P// plink == 0x20000
*plink =(void *)Q; // [0x20000] == 0x20040 覆盖发生于此
softwarewander 2006-12-15
  • 打赏
  • 举报
回复
32位平台指针就是一个32位 无符号int
void ** plink=(void **)P;//不考虑指针类型这行玩了后 plink和p指向同一个地方假设ST1

*plink =(void *)Q; //这行将ST1开始的32位用(void *)Q覆盖掉

(void *)Q是指针=一个32位int并且它是随机的
///////////////////////////////////////////


我的理解是这样的, 32位指针。
类型 变量名 变量自身地址 内容
****************************************************************************
结构体st ST1 0X20000 **** 可以为空没有赋值
结构体st ST2 0x20040 **** 可以为空没有赋值

二级void指针 plink 0x100 未初始化

结构体st的指针 P 0X500, 一个结构体ST1的地址0x20000。
结构体st的指针 Q 0X504, 一个结构体ST2的地址0x20040。



以下是plink在执行完各句之后的情况;
先将语句编号:
void ** plink=(void **)P// (1)
*plink =(void *)Q; //(2)

执行语句 plink plink的内容(*plink) *plink的内容(**plink)
(1) 0x100 0X500 0x20000

(2) 0x100 0x20040 0x20040开始的4个字节算出的一个32位指针
假设为0xf2000

并没有覆盖阿。所有的操作都没有对0x20000处的内容进行修改阿。但调试的时候确实是发生了变化。 我还是有点想不通。

我在程序中只对plink 和*plink的反复赋值来将他们所指的结构体串起来而已。













短歌如风 2006-12-14
  • 打赏
  • 举报
回复
> void ** plink=(void **)P;
> *plink =(void *)Q; //执行完此句之后,发现p当中的id变成了垃圾字符,
>而fa中的数据完整无

P是一个ST*
强制类型转换为void **
然后消引用,给*P赋了一个void*
也就是把一个ST当作一个void*赋值
其结果……

在目前的x86 32位编译器下的结果,一般会用Q这个指针覆盖P->id[0]、P->id[1]、P->id[2]、P->id[3]
DonaldKnuth 2006-12-14
  • 打赏
  • 举报
回复
//你不就是想把p指向的内存的内容用q指向的内容覆盖掉吗
//不是丢失,使覆盖的问题,应该这样改
#include<iostream>
using namespace std;

typedef struct tag_st
{
float fa[2];
char id[10];
}ST;

void main()
{
ST * P=(ST *) malloc(sizeof(ST));
strcpy(P->id,"hello!");
P->fa[0]=1.1;
P->fa[1]=2.1;

ST * Q=(ST *)malloc(sizeof(ST));
strcpy(Q->id,"world!");
Q->fa[0]=3.1;
Q->fa[1]=4.1;
//cout<<P->id<<" "<<P->fa[0]<<" "<<P->fa[1]<<endl;

// * P = * Q;
void ** plink=(void **)P;
// (*plink =(void *)Q;

*( (ST *)(plink)) = * Q; //plink要先强制转换一下
cout<<P->id<<" "<<P->fa[0]<<" "<<P->fa[1]<<endl;
}
OpenHero 2006-12-14
  • 打赏
  • 举报
回复
我倒~~哪你就得把代码贴出来看看啊~~
不然谁也不知道你是什么问题,....
softwarewander 2006-12-14
  • 打赏
  • 举报
回复
这个我知道阿, 不过就是输出的时候,进行了转化也还是垃圾字符。
或许我传得参数,或者对他的初始化有问题。 我在看看 :)
OpenHero 2006-12-14
  • 打赏
  • 举报
回复
记住void是没有类型可言的,只有你自己才可以规定它指向的是啥类型,所以,转换成你要的类型的时候,它就又对了.....
OpenHero 2006-12-14
  • 打赏
  • 举报
回复
plink本身是一个指向指针的指针,它的*plink也是一个指针大的空间,虽然它的初始值是p
但是它本身是没有类型的,只是void ** 型,
当*plink=(void*)Q的时候只是把plink指向的指针的位置变化成了一个指针而已.....
zmx0506 2006-12-14
  • 打赏
  • 举报
回复
很简单,你用了
void ** plink=(void **)P;
*plink =(void *)Q;
语句,那么你要查看的时候就再来个
ST *pst = (ST *)(*plink);
这个时候再查看
pst->id,应该就不乱了

呵呵,通信协议中经常用这种转换的
myfriend023 2006-12-14
  • 打赏
  • 举报
回复
在用的时候再把它转换回来
加载更多回复(7)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧