在看C++对象模型相关的帖子,最常见的是直接拿对象的地址去取虚表地址,代码如下:
Base b;
printf("%p\n", (int*)*(int*)*(int*)(&b));
但是,这里的代码是在32位下的,如果是x64位就直接宕了
长度
原因是x64位的地址是64位的,而x86下面是32位的。
// x86下面的地址
0034116D
003412EE
// x64下面打印的地址
00007FF664371447
00007FF66437108C
出问题的地方主要是强转之后解引用发生的,在x64下面int指针还是64位的,强转没有问题。但是,如果(int*)之后就出问题了,拿到一个int类型的整数,只有32位。而后面又去把它转成int*的64位地址,高位用0xFFFFFFFF补全,结果就不对了。
0x00007FF6BB966841 处(位于 diffrent_address_study.exe 中)引发的异常: 0xC0000005: 读取位置 0xFFFFFFFFBB96AD30 时发生访问冲突。
开始以为把int换成long就行了,结果死翘翘,long也是4位。这个估计得看系统了,我得win10下面,x64下面的长度如下
sizeof(int) = 4
sizeof(long) = 4
sizeof(int*) = 8
sizeof(long*) = 8
sizeof(intptr_t) = 8
解决
使用intptr_t代替
看定义
// Definitions of common types
#ifdef _WIN64
typedef unsigned __int64 size_t;
typedef __int64 ptrdiff_t;
typedef __int64 intptr_t;
#else
typedef unsigned int size_t;
typedef int ptrdiff_t;
typedef int intptr_t;
#endif
所以,这也提醒我自己,以后写代码地址转成int这样的操作很危险。用库里面有的类型,比如这里intptr_t。
跳过解引用运算符
还看到一些网友[1]的写法,我觉得这个比较好,跳过了解引用运算符的操作,也就不会出现问题了。而且对于虚表也比较清晰
Base b;
long** pVtab = (long**)&b;