今天又被一个同事叫查问题,他从网络上接收一个结构体数据,然后根据偏移获取值,但是总是获取不对,由于涉及结构体嵌套,一直怀疑是发送方问题,查了好久最后发现是解析的时候计算偏移出了问题,这样的类似问题在我印象中不止一次了,上次也时一个同事遇到了类似问题。所以今天我有必要记录一下,加深映像,希望下次遇到能够快速发现问题;
在这不方便贴出原问题代码,下面给出另外一个实例,问题都是一样的。
// TestStructOffiset.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <string.h>
#include <stdlib.h>
typedef struct ProductInfo
{
int nProductId;
int nDateLen;
char data[0];
}*pProductInfo,sProductInfo;
typedef struct DetailClass
{
int nClassId;
int nNumber;
char Detail[255];
}*pDetailClass,sDetailClass;
void GetDetailInfo(pProductInfo pParameter)
{
printf("nProductId %d\n",pParameter->nProductId);
//错误计算偏移
printf("base %p and base+offset %p \n",pParameter,pParameter+sizeof(sProductInfo));
pDetailClass pTemp = (pDetailClass)(pParameter+sizeof(sProductInfo)); // 方式一
printf("classid and number %d,%d\n",pTemp->nClassId,pTemp->nNumber);
//正确计算偏移
printf("base %p and base+offset and %p \n",pParameter,(char *) pParameter+sizeof(sProductInfo));
pDetailClass pTemp1 = (pDetailClass)((char *)pParameter+sizeof(sProductInfo)); // 方式二
printf("classid and number %d,%d\n",pTemp1->nClassId,pTemp1->nNumber);
}
/*
void GetDetailInfo2(char* pParameter)
{
pProductInfo pProTemp = (pProductInfo)pParameter;
printf("nProductId %d\n",pProTemp->nProductId);
//计算偏移
printf("%p and %p \n",pParameter,pParameter+sizeof(sProductInfo));
pDetailClass pTemp = (pDetailClass)(pParameter+sizeof(sProductInfo));
printf("pTemp %d,%d\n",pTemp->nClassId,pTemp->nNumber);
}*/
int _tmain(int argc, _TCHAR* argv[])
{
int nLen = sizeof(sProductInfo)+sizeof(sDetailClass);
char *buf = (char *)malloc(sizeof(sProductInfo)+sizeof(sDetailClass));
if (NULL == buf)
{
printf("malloc failed !");
getchar();
retrun -1;
}
memset(buf,0,nLen);
pProductInfo pProTmp = (pProductInfo)buf;
pProTmp->nProductId = 1;
pProTmp->nDateLen = sizeof(sDetailClass);
sDetailClass sDetailTmp;
sDetailTmp.nClassId = 2;
sDetailTmp.nNumber = 0;
memcpy_s(pProTmp->data,sizeof(sDetailClass),&sDetailTmp,sizeof(sDetailClass));
GetDetailInfo(pProTmp);
//GetDetailInfo2((char *)pProTmp);
getchar();
return 0;
}
执行结果:
nProductId 1
base 00423208 and base+offset 00423248 ----- 偏移了64个字节(十六进制)
classid and number -858993460,-858993460 ----- 错误;读取字节与预期不符
base 00423208 and base+offset and 00423210 ----- 偏移了8个字节
classid and number 2,0 ----- 正确读取值
分析结果:
由于pParameter所指向的结构体大小为8个字节,然后移动sizeof(sProductInfo) = 8 个单位,实际就是移动了8*sizeof(sProductInfo) = 64个字节;而DetailClass 的偏移地址实际为 8个字节;所以方式一读取错误;方式二才是正解;
结论:
1、在利用指针给非单字节(如本文中的结构体或int 等)类型计算偏移时,一定要注意是否需要转为单字节类型的指针(如char *)再计算偏移值。
2、这种情况传参数的时候不要传结构体指针,而是将结构体指针转为char * 传入,再实际使用时再进行转换。如本文中注释的方法GetDetailInfo2调用。