问题提出
笔者编译了一个64位的应用程序,需要将一个结构体的内容发送给一个32位的应用程序解析,但是两边对结构体的sizeof求长度始终不一致,导致无法正常解析结构体数据。其中,发送端和接收端采用相同结构体定义和字节对齐,形式简化如下:
typedef struct _tagDEMO { int iVal1; virtual ~_tagDEMO(){} }sDEMO;
发送端(64位应用程序)打包代码简化如下:
void SendBuf(sDEMO& ts) { /// 申请空间 int sz = sizeof(ts); char* pBuf = new char[sz]; memset(pBuf,0,sz); /// 拷贝数据 memcpy(pBuf,&ts,sizeof(ts)); /// 发送数据 .... /// 释放空间 delete []pBuf; }
接收端(64位应用程序)解包代码简化如下:
void rcvProc(char* pBuf,int len) { sDEMO ts; memcpy(&ts, pBuf, sizeof(ts) ); }
问题分析实验
C++的类或者结构体中如果存在有虚函数,那么对其sizeof求长度的时候,这个长度是包含了虚函数表的指针地址长度的,而且这个虚函数表指针是放在结构体的最前面的,32位程序和64位程序对于指针长度的定义一个是4字节,一个是8字节,这也就导致了两边的结构体长度会不一致。
下面我们针对以上分析做如下实验:
1)结构体定义虚函数其结构体长度会增加
typedef struct _tagDEMO { int iVal1; }sDEMO; /// int main(int argc, _TCHAR* argv[]) { sDEMO ts; int sz = sizeof(ts); printf("sizeof(sDEMO) = %d\n",sz); /// return 0; }
/// typedef struct _tagDEMO { int iVal1; virtual ~_tagDEMO(){} }sDEMO; /// int main(int argc, _TCHAR* argv[]) { sDEMO ts; int sz = sizeof(ts); printf("sizeof(sDEMO) = %d\n",sz); return 0; }
由此可见,增加一个虚函数其结构体长度增加了4(一个指针的长度,实验是在32位程序中运行的)。
2)结构体中不管定义多少个虚函数,都是增加一个指针长度(虚函数表指针)
/// typedef struct _tagDEMO { int iVal1; virtual ~_tagDEMO(){} virtual void vFun1(){} virtual void vFun2(){} }sDEMO; /// int main(int argc, _TCHAR* argv[]) { sDEMO ts; int sz = sizeof(ts); printf("sizeof(sDEMO) = %d\n",sz); return 0; }
多加了几个虚函数,结构体的长度仍然为8。
3)结构体数据中首先放置虚函数表指针
/// typedef struct _tagDEMO { int iVal1; virtual ~_tagDEMO(){} virtual void vFun1(){} virtual void vFun2(){} }sDEMO; /// int main(int argc, _TCHAR* argv[]) { sDEMO ts; ts.iVal1 = 10; int sz = sizeof(ts); printf("sizeof(sDEMO) = %d\n",sz); /// char* pBuf = new char[sz]; memset(pBuf,0,sz); memcpy(pBuf,&ts,sizeof(ts)); printf("first 4 bytes = %d, second 4 bytes = %d\n",*( (int*)pBuf ),*( (int*)(pBuf + 4) ) ); /// return 0; }
前面4个字节并不是我们赋值的10,而是后面4个字节的值为10
问题解决办法
要在32位程序和64位程序之间传递结构体数据,一是在结构体中新增打解包的函数,利用打解包函数组织数据,例如:
/// typedef struct _tagDEMO { int iVal1; virtual ~_tagDEMO(){} virtual void vFun1(){} virtual void vFun2(){} void pack(char* &pBuf,int& len) { int offset = 0; /// memcpy(pBuf + offset,&iVal1,sizeof(iVal1)); offset += sizeof(iVal1); /// len = offset; } void unpack(char* pBuf,int len) { int offset = 0; /// memcpy(&iVal1,pBuf + offset,sizeof(iVal1)); offset += sizeof(iVal1); /// } }sDEMO;
另外一种如果要保留直接拷贝结构体的方式,那么就需要将结构体中所有的虚函数去掉,如下所示:
/// typedef struct _tagDEMO { int iVal1; int iVal2; /// void Fun1(); }sDEMO;