最近做一个移植项目,将32位上的VPN移植到64位环境上。由于当初设计未考虑可移植性,导致移植时出现了很多的问题,其中最典型的一个问题就是通信结构带指针的问题。
场景分析:
如下的两个结构体,其中B在通信时做通信结构使用
struct A
{
int count; //链表结点数
int *b; //数据链表
void* setPoint(void *p)//设置指针,传入起始指针,返回结束指针
{
return (int*)p + count;
}
}
struct B
{
int count; //链表结点数
A *y; //A结构链表
void* setPoint(void *p) //设置指针,传入起始指针,返回结束指针
{
return (A*)p+count;
}
}
乍一看是不是很乱呢,确实,之所以在通信时使用这样的结构,是为了传送链表数据用的,也就是不固定的数据单元。
这里就不深入讨论这个结构的内存布局了,简单说一下,内存布局分为头部和尾部,头部为结构体数据,尾部为结构体所包含的链表数据。
移植分析
主要讨论移植的问题,众所周知,指针在32位下的长度为32位,而在64 位下的长度为64位,那么我们问题出现了。
我们知道,通信协议一定需要保证通信双方的数据包一模一样的,现在上面的这种情况如果32位与64位机通过B结构通信时,必然导致协议不一致而解析错误的问题。原因很简单,就是指针长度变了。
那么怎么解决这个问题了,首先想的当然是在64位机上采用一套跟32位机一模一样的结构体。
方法一、
在64位机上,多定义一套结构体(32位兼容结构),用int代替指针,请注意,由于指针在传到对端时必然失效,所以,这里的指针在通信过程中已经没有作用了。
用int做占位,处理时采用 标准结构体处理,在发送的时候将其转换成32位兼容结构,再发送出去,这样,对方在收到时就会协议兼容。
但问题是,这样做就会多做一次结构体解码和编码,即对整个多维链表的赋值,这是一个工作量很大的过程,特别当这样的结构多次出现时。
方法二、
自己实现一个指针类,用来替代结构体中出现的指针,该类模拟指针,但长度只有32位。
问题立刻出现了,怎么才能够用32位的长度模拟64位的指针呢,答案是不可能。
正常情况下确实是不可能的,应为无论如何,32位都没办法表示64位的长度。
但在这种通信结构中却是有可能的,因为通信结构大小一定小于2^32。
而通信结构中的指针指向的是通信结构中的某一个区域。
于是,可以采用偏移的方式模拟64位指针。
这样就很顺其自然地解决了所有的问题,只需要简单地对指针进行替换,就可以实现32与64位通信兼容
下面给出64位下模拟32位指针的简单实现:
class ptr
{
private:
int shift;
public:
char* oprator=(char* p){ shift=(char*)this-p;return p;} //计算出p相对于this的偏移,保存下来,用作下次计算指针实际值用