由一道C 題所想到的

2021-06-17 23:27:00 字數 1806 閱讀 4577

前段時間在睿思上@vivianliu出了如下一道c++題目:

給定乙個類:

class mycl

protected:

virtual int geta()

};int main()

可見輸出是乙個未初始化的a。

先上**吧

class myder:mycl		//同樣是派生乙個子類

};int main()

; //為下文修改記憶體分配策略

mycl* pacl = reinterpret_cast(data); //基於位的強制轉換

myder ader(*pacl);

ader.geta();

system("pause");

return 0;

}

從以上**可以看出,還有第二種方法來繞開預設建構函式的自動呼叫。

注意到mycl類的大小是

8個位元組

,前四個位元組是乙個指標,指向

虛函式表的位址

,後四個位元組則是類成員

a的大小了

。所以可以定義乙個大小為8個位元組的int型陣列:  

int data[2] = ;

再利用強制轉換:

mycl* pacl = reinterpret_cast(data);

則可以構造乙個物件,相應的,後四個位元組的值為1,a則被初始化為1(怎麼感覺hack進了類的記憶體,所以這種強制轉化要慎用)。

但是這樣強制轉化之後,類虛函式表指標指向的值為0,也就是說並沒有初始化虛函式表,所以還需要一種手段來初始化虛函式表。弄乙個子類然後呼叫複製建構函式,就能實現虛函式表的自動初始化。(注意到子類複製建構函式中有一行注釋掉的geta(),把注釋去掉的話,直接呼叫子類複製建構函式便能呼叫基類的geta(),當然像現在這樣,宣告了

using:mycl::geta()

,直接在子類外頭呼叫也行)

這種方法不用構建乙個子類。可直接hack進類的虛函式表,通過函式指標來呼叫虛函式,即使該虛函式是private也管用。**:

int main()

(int*)ba,獲得虛函式表位址;

*(int*)ba,解引,獲得虛函式表;

(int*)*(int*)ba,獲得第乙個虛函式位址;

*((int*)*(int*)ba),獲得第乙個虛函式;

但是這種方法還有一種缺陷,就是函式指標pf並不知道ba的this指標的位址,所以通過pf呼叫的函式,a的值不是正確的,可能是任意記憶體位置的值。方法是改變函式指標的型別定義以及虛函式位址的傳遞方式。

int main()

可以看出pf定義為

(mycl::*func)(void)

型別,即可以視為mycl的乙個成員函式,通過

(*(func*)(*(int*)ba));的方法(獲取虛函式表後,通過

*(func*)

的方法獲取第乙個虛成員函式),複製給pf。再通過

(ba->*pf)();

的方式呼叫,可以認為中間的->就把this指標傳給了pf函式。這樣就能獲得正確的a的值,大家可以拿int data[2] = ;來初始化ba,再通過hack虛函式表的方式獲取a的值,就能發現這種方法是正確的。

陳皓在這篇文章也說了,c++是有缺陷的,c++也有其危險性,但是了解它的這種危險性能讓我們更好的學習這門語言,以下這篇文章也在記憶體級別分析了c++類的繼承原理:

謝謝大家!!!

由一道acm題目所想到

已經很長一段時間沒有ac題了,一周之前報名參加了計算客的程式設計大賽,雖然我的結果不 是特別理想,但是還是學到了一些東西。下邊,我先貼出題目和對應的程式原始碼 如下 c語言 includeint main if n 2 1 else if p n 1 p n 2 else totaltime p n...

由一道試題想到的

前段時間忙著換工作,面試了幾家公司,其中有些筆試題,蠻有意思的,給我很大啟發。最好玩的一道程式設計題 不使用中間變數,交換2個int型變數a和b的值。寫出方法。這個其實不是很難,方法如下 public class exchange void swap int a int b 給我的啟發是,在程式設計...

由SDRAM是一道坎想到的

自己頹廢了乙個星期,始終不想繼續,似乎快放棄了,心態開始轉變。也許是因為ta,也可能是自己沒勇氣面對sdram這道坎,趁著現在還清醒,站起來,別讓我看不起你 今天出去接人,看似簡單的事,但暴露出自己的很多問題 1.感覺和物流部經理溝通不夠自然,上次那件事之後,感覺很尷尬,自己就不能改改?2.在等修模...