筆者環(huán)境:
$ gcc --version
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
$ uname -a
Linux debian9-64-Desktop 4.9.0-8-amd64 #1 SMP Debian 4.9.110-3+deb9u6 (2018-10-08) x86_64 GNU/Linux
虛表:
#include<iostream>
typedef void (*fun)(void) ;
class A{
private:
virtual void printA(){
std::cout<<"hello A"<<std::endl;
}
virtual void printB(){
std::cout<<"hello B"<<std::endl;
}
void printC(){
std::cout<<"hello C"<<std::endl;
}
};
int main(int argc,char * argv[]){
A a;
long* pa =(long*)&a;
long* ppa = (long*)(*pa);
void (*funA)(void) =(void (*)(void))(*ppa);
funA();
fun funB =(fun)(*(++ppa));
funB();
return 0;
}
問題一:
請問上例中的,sizeof(A)
有多少個(gè)字節(jié)在辆?
在64位機(jī)证薇,是8個(gè)字節(jié)
在32位機(jī)度苔,是4個(gè)字節(jié),
問題二:
請問上例中棕叫,為什么要使用 long *
呢林螃?
因?yàn)楣P者的環(huán)境是64位機(jī)奕删,
void*
是占用8個(gè)字節(jié)的俺泣。所以這里使用long
方便我調(diào)用下一個(gè)虛方法,只需要通過long* +1
即可
問題三:
請問上例中完残,為啥會取了那么多次的指針伏钠,分別是干什么的。
long* pa =(long*)&a;
表示取得類的地址
long* ppa = (long*)(*pa);
表示取得虛表的地址
void (*funA)(void) =(void (*)(void))(*ppa);
表示取得在虛表中首個(gè)虛函數(shù)的地址
++ppa
表示地址往后偏移8個(gè)字節(jié)谨设,就是第二個(gè)虛函數(shù)的地址
問題四:
請問上例中熟掂,如果編譯出可執(zhí)行文件,可以看得到 printC
的函數(shù)符號嗎扎拣?為什么赴肚?
讓我們執(zhí)行編譯然后驗(yàn)證一下:
$ g++ -I. main.cpp $ objdump -Tt a.out |grep print 0000000000000b0a w F .text 0000000000000037 _ZN1A6printBEv 0000000000000ad2 w F .text 0000000000000037 _ZN1A6printAEv
經(jīng)過 粉碎命名 ,但是也依舊可以看到是沒有
printC
的簽名的二蓝。那我們換種方式
$ strings a.out |grep print vfwprintf printB vwprintf vswprintf printA _ZN1A6printAEv printC _ZN1A6printBEv _ZN1A6printCEv _ZN1A6printBEv _ZN1A6printAEv
可以看到也是有的誉券,證明這個(gè)符號在,并且存在在代碼區(qū)刊愚。
問題五:
請問上例中踊跟,如果我要按照你這樣調(diào)用 printC
可不可以呢?
經(jīng)過一通詢問鸥诽,與查找商玫,發(fā)現(xiàn),可以使用這里的文章所列舉的方法進(jìn)行調(diào)用