c c 一道c 面試題的思考與學習

2021-07-25 12:03:28 字數 3005 閱讀 6409

先上面試題,問列印結果是什麼?

#include

class a

};class b : public a

};class c : public b

};class d : public c

virtual

void

fund

()};

class e : public d

virtual

void

fune

()};

class e1 : public d

virtual

void

fune1

()};

intmain

()

再看到這裡的時候,再做一遍吧,答案放在最後

問題:類中的記憶體布局是什麼樣的?

類的非靜態成員函式是放在什麼地方的?

虛函式是放在什麼地方的?虛表是在**,以及如何儲存的?

簡單的結構如下圖,參考[2]描述地超級詳細,也利於理解,可以仔細閱讀一下。

c++物件模型

另外,為了解題,還要講講編譯鏈結以及函式呼叫的簡單原理

編譯的時候,靜態以及非靜態成員函式也是已經編譯好了的。虛函式,編譯器能夠確定的是偏移量,待執行的時候會去找具體物件的vptr,指向該物件所在類對應的虛表,找到真正應該呼叫的函式[3]。 文章[4]也說到了,虛函式和普通函式的在彙編一層的表現形式,虛函式要通過虛表去查詢真正該呼叫的函式,而普通函式直接就找到了的。

現在,運用上面的結論來回答問題

看一下測試**和結果如下: 在上面面試題後面加入以下測試**

printf("**********=vritual address*****====\n");

printf("vtable p1: %x\n", *((int*)p1));

printf("vtable p2: %x\n", *((int*)p2));

printf("vtable p3: %x\n", *((int*)p3));

printf("vtable p4: %x\n", *((int*)p4));

printf("vtable p5: %x\n", *((int*)p5));

printf("vtable p6: %x\n", *((int*)p6));

printf("p1 virtual fun: %x\n", *(int*)*((int*)p1));

printf("p2 virtual fun: %x\n", *(int*)*((int*)p2));

printf("p3 virtual fun: %x\n", *(int*)*((int*)p3));

printf("p4 virtual fun: %x\n", *(int*)*((int*)p4));

printf("p5 virtual fun 1: %x, offset: %x\n", *(int*)*((int*)p5), &e::fun);

printf("p5 virtual fun 2: %x, offset: %x\n", *((int*)*((int*)p5)+1), &e::fund);

printf("p5 virtual fun 3: %x, offset: %x\n", *((int*)*((int*)p5)+2), &e::fune);

printf("p6 virtual fun 1: %x, offset: %x\n", *(int*)*((int*)p6), &e1::fun);

printf("p6 virtual fun 2: %x, offset: %x\n", *((int*)*((int*)p6)+1), &e1::fund);

printf("p6 virtual fun 3: %x, offset: %x\n", *((int*)*((int*)p6)+2), &e1::fune1);

結果:

**********=vritual address*****====

vtable p1: 8049290

vtable p2: 8049258

vtable p3: 80492a0

vtable p4: 80492a0

vtable p5: 80492c0

vtable p6: 80492c0

p1 virtual fun: 8048e1e

p2 virtual fun: 8048e0a

p3 virtual fun: 8048e32

p4 virtual fun: 8048e32

p5 virtual fun 1: 8048e46, offset: 1

p5 virtual fun 2: 8048dce, offset: 5

p5 virtual fun 3: 8048de2, offset: 9

p6 virtual fun 1: 8048e46, offset: 1

p6 virtual fun 2: 8048dce, offset: 5

p6 virtual fun 3: 8048de2, offset: 9

p1雖然在編譯的時候fun是類a的,但是物件畢竟還是b物件,所以vptr的位址指向的依舊是b類的虛表位址,可以再新增一條列印b物件的虛表的測試**,發現也是0x8049170;

p3和p4,p5和p6的虛表位址是一樣的,這也說明了他們是指向同乙個類的例項(物件)的;

p3和p4,p5和p6的第乙個,第二個,第三個的虛函式位址都一樣,也同樣說明了是同乙個類中的虛表;

也可以看到fund的偏移量是5(和1之間的差是4,測試機器是32位的),fune和fune1都是偏移9,所以能夠通過pe1呼叫e物件的第三個虛函式。

[1]圖說c++物件模型:物件記憶體布局詳解

[2]c++虛表,你搞懂了嗎?

[3]c/c++雜記:虛函式的實現的基本原理 

[4]關於c++虛函式與普通函式的編譯與呼叫機制

答案:a, c, d, d, d_test, e_test, d_test

一道C面試題的思考

c語言真的是學無止境的感覺,大部分同學大學都會開設c語言課程。很多人把c語言二級過了就感覺入門了 對於那些在做嵌入式開發的工程師,幾乎每天都要接觸c語言,很多人會感覺自己c語言學得很溜了。那好,咱們用一道c語言面試題來測試一下。首先給出題目 定義乙個巨集,求兩個數中的最大數 ok,很多人應該能很快寫...

一道面試題的思考

在繼承中new和override相同點和區別?看下面的 有乙個基類a,b1和b2都繼承自a,並且使用不同的方式改變了父類方法print 的行為。測試 輸出什麼?為什麼?public void dotest public class a public class b1 a public class b...

一道面試題引發的思考

首先我們給出這道面試題的 以及題目 lista new arraylist list.add 1 list.add 2 for string item list 問 上段 執行會報錯嗎?如果把 1 換成 2 會報錯嗎?為什麼?首先給出答案 上面這段 執行不會報錯。把 1 換成 2 再執行就會報錯。為...