從彙編看c 中成員函式指標 一

2021-08-21 21:19:40 字數 3394 閱讀 5997

下面先來看c++的原始碼:

#include using

namespace

std;

class

x

virtual

intget2()

virtual

intget3()

};int

main()

類x有3個成員函式,其中get1是普通的成員函式,而get2和get3都分別是虛成員函式。在main函式裡面分別定義了指向這三個成員函式的指標,並且將他們的值輸出來。然後用成員指針對他們進行了呼叫。下面來看呼叫後的結果:

可以看到,gp1 gp2 gp3輸出的好像都是位址。下面主要來看一下面函式裡面,定義三個成員函式指標的彙編碼:

;

20 : int(x::*gp1)() = &x::get1;

mov dword ptr _gp1$[ebp], offset ?get1@x@@qaehxz ;

x::get1;取?get1@x@@qaehxz所代表的記憶體位址,即x::get1的位址給gp1

;21 : int(x::*gp2)() = &x::get2;

mov dword ptr _gp2$[ebp], offset ??_9x@@$ba@ae ;

x::`vcall''; 取??_9x@@$ba@ae所代表的記憶體位址,即x::`vcall''的位址給gp2

;22 : int(x::*gp3)() = &x::get3;

mov dword ptr _gp3$[ebp], offset ??_9x@@$b3ae ;

x::`vcall'';取??_9x@@$b3ae所代表的記憶體位址,即x::`vcall''的位址給gp3

通過彙編碼可以看到,gp1儲存的確實是成員函式get1的位址,而gp2和gp3儲存的確不是虛函式get2和get3的位址,而是x::vcall和x::vcall的位址。那麼,x::vcall和vcall到底是什麼呢?我們繼續看彙編**,接下來是x::vcall的彙編碼:

??_9x@@$ba@ae proc                    ;

x::`vcall'', comdat

mov eax, dword ptr [ecx];

寄存是ecx裡面儲存的是物件x的首位址,

;因此,這裡是將物件x首位址處記憶體內容(即vftable首位址)給暫存器eax

jmp dword ptr [eax];

跳轉到vftable首位址處記憶體(裡面存的是虛函式get2的位址)所儲存的位址處執行

;這裡就是跳轉去執行虛函式get2

??_9x@@$ba@ae endp ;

x::`vcall''

通過彙編碼,我們可以發現,x::vcall是一段**,它的作用是跳轉到相應的虛函式位址去執行。

下面是x::vcall的彙編碼:

??_9x@@$b3ae proc                    ;

x::`vcall'', comdat

mov eax, dword ptr [ecx];

暫存器ecx裡面儲存的是物件x的首位址

;因此,這裡是將物件x首位址處記憶體內容(即vftable首位址)給暫存器eax

jmp dword ptr [eax+4];

跳轉到偏移vftable首位址處4byte處記憶體(裡面存的是虛函式get2的位址)所儲存的位址處執行

;這裡就是跳轉去執行虛函式get3

??_9x@@$b3ae endp ;

x::`vcall''

通過彙編碼,我們返現,x::vcall和x::vcall的作用一樣。

因此,gp2和gp3儲存的不是虛函式get2和虛函式get3的位址,而是相應的vcall函式的位址。那麼,通過這些成員函式的指標呼叫函式的時候,有發生了什麼?

下面是呼叫函式的彙編**:

;

28 : /********************用成員函式指標呼叫虛函式*******************/

;29 : (x.*gp1)();

lea ecx, dword ptr _x$[ebp];

取物件x的首位址,給暫存器ecx,作為遺憾引數傳遞給get1

call dword ptr _gp1$[ebp];

gp1中存有get1的位址,這裡直接呼叫get1函式

;30 : (xp->*gp1)();

mov ecx, dword ptr _xp$[ebp];

指標變數xp儲存有物件x的首位址,這裡將物件首位址給暫存器ecx 作為隱含引數傳遞給get1

call dword ptr _gp1$[ebp];

gp1中存有get1的位址,這裡直接呼叫get1函式

;用指標和物件操作成員變數指標效果一樣

;31 : (x.*gp2)();

lea ecx, dword ptr _x$[ebp];

取物件x的首位址,給暫存器ecx,作為隱含引數傳遞給x::vcall

call dword ptr _gp2$[ebp];

gp2中存有x::vcall的位址,這裡呼叫x::vcall,由vcall查詢虛表,執行get2

;32 : (xp->*gp2)();

mov ecx, dword ptr _xp$[ebp];

用指標呼叫和用物件x呼叫效果一樣

call

dword ptr _gp2$[ebp]

;33 : (x.*gp3)();

lea ecx, dword ptr _x$[ebp];

將物件x首位址給暫存器ecx,作為隱含引數傳遞給x::vcall

call dword ptr _gp3$[ebp];

gp3中存有x::vcall的位址,這裡呼叫x::vcall,由vcall查詢虛表,呼叫get3

;34 : (xp->*gp3)();

mov ecx, dword ptr _xp$[ebp];

指標呼叫和物件呼叫效果一樣

call dword ptr _gp3$[ebp]

通過彙編碼發現,普通成員函式時通過位址直接呼叫,而虛成員函式時通先呼叫vcall函式,然後由vcall函式查詢虛表呼叫相應的虛函式

由此可以看出,乙個類裡面的每一給虛函式都有乙個vcall函式與之對應,通過vcall函式來呼叫相應的虛函式。

從彙編看c 中指向成員變數的指標 一

在c 中,指向類成員變數的指標儲存的並不是該成員變數所在記憶體的位址,而僅僅是該成員變數在該類物件中相對於物件首位址的偏移量。因此,它必須繫結到某乙個物件或者物件指標上面,這裡的物件和物件指標,就相當於充當了this指標的容器。下面先看c 原始碼以及輸出結果 include include usin...

從彙編看C 中的指標和引用

以前寫c 的時候,我認為指標是就是乙個存著另乙個變數位址的變數,他是有儲存空間的,而引用只是乙個變數的別名,不會占用儲存空間。最近在搞彙編,發現下面兩段 的生成的彙編 是一樣的 指標 void func 引用 void func 上面兩段 的生成的彙編都是 004019d0 push ebp 004...

從彙編看函式呼叫

首先介紹幾個名詞 棧幀 也叫過程活動記錄,是編譯器用來實現過程 函式呼叫的一種資料結構。棧幀中儲存了該函式的返回位址和區域性變數。暫存器 cpu內部用來存放資料的一些小型儲存區域,用來暫時存放參與運算的資料和運算結果。常用的暫存器有 esp 棧指標暫存器 extended stack pointer...