校招八股:C/C++开发工程师常见笔试、面试题目不完全汇总【很基础】
這里匯總一些C/C++開發(fā)崗的常見面試八股題,都屬于比較基礎(chǔ)、偏理論性的題目。換句話說(shuō),如果這些題目答不上來(lái),可能會(huì)給面試官留下的基礎(chǔ)不好的印象,尤其是科班生哈。
廢話不多說(shuō),直接開始。
一、C/C++篇
1. 基礎(chǔ)中的基礎(chǔ)篇
簡(jiǎn)述C和C++的區(qū)別
難度:? 被考頻率:???
如果你面試的是C++開發(fā)崗,有些面試官會(huì)把這個(gè)問(wèn)題作為第一個(gè)問(wèn)題。C和C++的區(qū)別細(xì)究起來(lái)的話太多了,因此,我們只要回答它們最大的,也是最主要的區(qū)別就好了。如果面試官想知道其余細(xì)節(jié)的話會(huì)繼續(xù)追問(wèn)的。
C和C++的區(qū)別如下:
思想上:C是面向過(guò)程的,它的主要特點(diǎn)是函數(shù)。編程思想是將問(wèn)題分解成不同的步驟,并調(diào)用函數(shù)來(lái)依次實(shí)現(xiàn)這些步驟。
C++是面向?qū)ο蟮?#xff0c;它的主要特點(diǎn)是類和對(duì)象。編程思想是將數(shù)據(jù)和數(shù)據(jù)操作封裝成不同的類,通過(guò)創(chuàng)建這些類的對(duì)象并調(diào)用對(duì)象的成員函數(shù)來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)模型的操作。
應(yīng)用上:C的應(yīng)用更偏底層,常常用于嵌入式開發(fā)、驅(qū)動(dòng)開發(fā)等直接與硬件交互的領(lǐng)域。
C++由于它優(yōu)秀的面向?qū)ο髾C(jī)制,在大型應(yīng)用程序的開發(fā)方面也表現(xiàn)出色。
C++是對(duì)C的擴(kuò)充。除了添加了面向?qū)ο髾C(jī)制以外,C++還添加的機(jī)制常用到的有:類模板、異常處理、運(yùn)算符重載、標(biāo)準(zhǔn)模板庫(kù)(STL)、命名空間(namespace)、流(stream)等等。
C程序從源程序到二進(jìn)制機(jī)器代碼的過(guò)程和gcc指令
難度:?? 被考頻率:???
舉例:gcc編譯源代碼hello.c的過(guò)程。
1. 預(yù)處理(Preprocessing):gcc -E hello.c -o hello.i
處理所有預(yù)編譯指令,即源代碼文件中以“#”開頭的指令,具體為:
a. 展開宏定義#define。
b. 處理?xiàng)l件編譯指令#ifdef。
c. 處理文件包含指令#include。
d. 刪除注釋"//“和”/* */"。
e. 添加行號(hào)和文件標(biāo)識(shí),便于編譯器在編譯階段產(chǎn)生錯(cuò)誤和警告提示時(shí)能夠顯示行號(hào)。
2. 編譯(Compilation):gcc -S hello.i -o hello.s
對(duì)預(yù)處理后的文件進(jìn)行詞法分析、語(yǔ)法分析、語(yǔ)義分析、中間代碼的生成和優(yōu)化,最后得到匯編代碼文件。
a. 詞法分析(Lexical analysis):詞法分析器會(huì)從左到右逐個(gè)字符讀入源程序,按照詞法規(guī)則將源代碼分割成一個(gè)個(gè)單詞(Token),檢查詞法錯(cuò)誤,并輸出二元組<單詞類別,單詞屬性>方便后續(xù)編譯過(guò)程的引用。
b. 語(yǔ)法分析(Syntax analysis):識(shí)別由詞法分析器輸出的單詞符號(hào)序列,構(gòu)造一棵語(yǔ)法樹。語(yǔ)法樹指出了詞法單元流的語(yǔ)法結(jié)構(gòu),可以判斷是否符合語(yǔ)法規(guī)范。常見的語(yǔ)法分析方法分為自下而上和自上而下兩大方法。
c. 語(yǔ)義分析(Semantic analysis):使用語(yǔ)法樹和單詞符號(hào)表中的信息,進(jìn)一步檢查源程序是否滿足語(yǔ)言定義的語(yǔ)義約束。這一步分析的時(shí)靜態(tài)語(yǔ)義,也就是在編譯期間能分析的語(yǔ)義,而動(dòng)態(tài)語(yǔ)義指在運(yùn)行期間才能確定的語(yǔ)義。
d. 中間代碼生成:根據(jù)語(yǔ)義分析的輸出,生成類機(jī)器語(yǔ)言的中間代碼,如三地址代碼。
e. 中間代碼優(yōu)化:改進(jìn)中間代碼的質(zhì)量。
f. 目標(biāo)代碼生成:將中間代碼映射成目標(biāo)語(yǔ)言(匯編語(yǔ)言)。
3. 匯編(Assembly):gcc -c hello.s -o hello.o
匯編器將匯編代碼翻譯成可執(zhí)行的機(jī)器碼。每一條匯編語(yǔ)句對(duì)應(yīng)一條機(jī)器指令,因此匯編器的工作就是根據(jù)匯編指令和機(jī)器指令的對(duì)照表一一翻譯過(guò)來(lái)即可。
4. 鏈接(Linking):gcc hello.o -o hello
鏈接器將多個(gè)匯編后得到的機(jī)器碼文件鏈接,從而生成一個(gè)可執(zhí)行程序。分為靜態(tài)鏈接和動(dòng)態(tài)鏈接兩種方式。
a. 靜態(tài)鏈接:在鏈接可執(zhí)行文件時(shí),鏈接器會(huì)找到所有被用到的源代碼,將它們復(fù)制并組合起來(lái)形成一個(gè)可執(zhí)行文件。優(yōu)點(diǎn)是由于可執(zhí)行文件中已經(jīng)具備了執(zhí)行程序需要的全部?jī)?nèi)容,因此執(zhí)行時(shí)運(yùn)行速度較快。缺點(diǎn)是如果有多個(gè)可執(zhí)行程序需要引用同一個(gè)目標(biāo)文件,每個(gè)可執(zhí)行程序都會(huì)復(fù)制一份該文件的副本,造成空間浪費(fèi);另外,如果某個(gè)目標(biāo)文件被修改了,則所有引用該文件的可執(zhí)行程序都需要重新編譯。
b. 動(dòng)態(tài)鏈接:程序被拆分成獨(dú)立的部分存儲(chǔ),只有運(yùn)行時(shí)才鏈接在一起形成完整的程序。優(yōu)點(diǎn)是即使多個(gè)程序依賴同一個(gè)動(dòng)態(tài)鏈接庫(kù),也不需要將這個(gè)庫(kù)復(fù)制多份,而是所有程序共享這個(gè)庫(kù);此外在程序更新時(shí)只更新被修改的庫(kù),不需要重新鏈接所有程序。缺點(diǎn)是因?yàn)閷㈡溄舆^(guò)程推遲到程序運(yùn)行時(shí)期,所以會(huì)對(duì)程序性能產(chǎn)生損失。
C程序的內(nèi)存管理
難度:?? 被考到頻率:????
C或C++語(yǔ)言編寫的程序在處理機(jī)上運(yùn)行,通常被分成五段:
棧區(qū):存放函數(shù)中聲明的局部變量、函數(shù)的形參和返回值。地址空間“向下減少”。
堆區(qū):保存動(dòng)態(tài)分配的內(nèi)存區(qū)域,可由程序員向操作系統(tǒng)申請(qǐng)和自行釋放。
靜態(tài)區(qū)(全局區(qū)):存儲(chǔ)全局變量和靜態(tài)變量。靜態(tài)區(qū)內(nèi)存程序開始時(shí)創(chuàng)建,直到程序運(yùn)行結(jié)束后才會(huì)被釋放。
常量區(qū):保存常量,在程序運(yùn)行期間不能被修改的量,如字符串常量"abcd"。
代碼區(qū):存放程序代碼,二進(jìn)制機(jī)器指令形式,只讀。
補(bǔ)充: 一個(gè)由C語(yǔ)言程序編譯得到的可執(zhí)行二進(jìn)制文件,從硬盤上被加載到內(nèi)存空間上運(yùn)行的過(guò)程,內(nèi)存的分配和管理機(jī)制。
一個(gè)C語(yǔ)言編譯的可執(zhí)行二進(jìn)制文件,通常被分為三段:代碼段(text)、數(shù)據(jù)段(data)、堆棧段(BSS)。
裝載到內(nèi)存上被分為五段:棧區(qū)(stack)、堆區(qū)(heap)、未初始化變量(BSS)、初始化變量(data)、代碼段(text)。
可執(zhí)行文件中,text段存放二進(jìn)制程序;data段存放靜態(tài)初始化的數(shù)據(jù),即賦初值的全局變量和static變量;BSS段存放未初始化的數(shù)據(jù),即沒有賦初值的全局變量和static變量,在可執(zhí)行文件中BSS段并不占用實(shí)際空間,而是只記載該區(qū)大小的數(shù)值,程序載入時(shí)菜實(shí)際分配空間,且該控件由系統(tǒng)初始化為零。
應(yīng)用程序加載到內(nèi)存空間時(shí),操作系統(tǒng)根據(jù)可執(zhí)行文件中header中的內(nèi)容,為text段、data段、為BSS段分配相應(yīng)的內(nèi)存空間,將文件中
data段和text段內(nèi)容拷貝到內(nèi)存中,將BSS段初始化為零,同時(shí)為堆區(qū)和棧區(qū)分配空間并維護(hù)。
堆和棧的區(qū)別
難度:?? 被考到頻率:?????
| 用戶自己申請(qǐng) | 系統(tǒng)分配 |
| 由不連續(xù)的內(nèi)存塊構(gòu)成的鏈表,大小可以調(diào)整 | 連續(xù)空間,大小固定 |
| 速度慢,會(huì)產(chǎn)生碎片 | 速度快,不會(huì)產(chǎn)生碎片 |
| 向高地址增長(zhǎng) | 向低地址增長(zhǎng) |
| 由庫(kù)函數(shù)(malloc/realloc/free、new/delete)提供服務(wù) | 由系統(tǒng)提供服務(wù) |
什么叫內(nèi)存泄漏
難度:?? 被考到頻率:??
內(nèi)存泄漏,通常指堆內(nèi)存泄漏,程序員向系統(tǒng)申請(qǐng)任意大小的堆內(nèi)存塊,使用完畢必須進(jìn)行顯式的內(nèi)存釋放,否則該內(nèi)存不能再次被使用。即使用malloc、realloc、new申請(qǐng)的空間,必須使用free、delete進(jìn)行釋放。
什么叫作用域
2. C++特性篇
C++的三大特征
難度:? 被考到頻率:??
繼承、多態(tài)、封裝。
繼承:一個(gè)對(duì)象可以繼承另一類對(duì)象的特征和能力。目的是避免公用代碼的重復(fù)開發(fā),減少代碼和數(shù)據(jù)冗余。
多態(tài):為不同數(shù)據(jù)類型的實(shí)體提供統(tǒng)一的接口,程序運(yùn)行時(shí),相同的消息可能會(huì)送給多個(gè)不同的類別之對(duì)象,而系統(tǒng)可依據(jù)對(duì)象所屬類別,引發(fā)對(duì)應(yīng)類別的方法,而有不同的行為
封裝:把客觀的事物抽象成一個(gè)類,就是將數(shù)據(jù)和方法打包在一起,加以權(quán)限的區(qū)分,達(dá)到保護(hù)并安全使用數(shù)據(jù)的目的。
靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)
難度:?? 被考到頻率:??
多態(tài)分為靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)。
靜態(tài)多態(tài):在編譯期間決定程序的執(zhí)行過(guò)程。包括函數(shù)重載和泛型編程,泛型編程包括函數(shù)模板和類模板。
動(dòng)態(tài)多態(tài):在程序運(yùn)行時(shí)根據(jù)被引用對(duì)象的實(shí)際類型判斷調(diào)用哪個(gè)方法。包括虛函數(shù)。
C++中重載、重寫(覆蓋)和隱藏的區(qū)別
難度:?? 被考到頻率:??
派生類中的函數(shù)覆蓋基類中的同名函數(shù),要求兩個(gè)函數(shù)具有相同的參數(shù)個(gè)數(shù)、參數(shù)類型和返回值類型,且基類中的函數(shù)必須是虛函數(shù)。重寫指的是重寫基類函數(shù)中的函數(shù)體。
在某些情況下,派生類中的函數(shù)屏蔽了基類中的同名函數(shù)的現(xiàn)象。如果想調(diào)用基類的函數(shù)必須加作用域限定符。具體情況有:
(1)派生類和基類中具有同名函數(shù),兩個(gè)函數(shù)參數(shù)列表相同,且基類的函數(shù)沒有被聲明稱虛函數(shù)。
class A { //父類 public:void fun(int a) {...} };class B : public A { //子類 public:void fun(int a) {...} //隱藏父類的fun函數(shù) };int main() {B b;b.fun(2); //調(diào)用B中的fun函數(shù)b.A::fun(2); //調(diào)用A中fun函數(shù)return 0; }(2)派生類和基類中具有同名函數(shù),兩個(gè)函數(shù)參數(shù)列表不同,無(wú)論基類的函數(shù)是不是虛函數(shù)都會(huì)被隱藏。
class A { //父類 public:void fun(int a) {...} };class B : public A { //子類 public:void fun(char* a) {...} //隱藏父類的fun函數(shù) };int main() {B b;b.fun(2); //報(bào)錯(cuò),調(diào)用B中的fun函數(shù),但參數(shù)類型出錯(cuò)b.A::fun(2); //調(diào)用A中fun函數(shù)return 0; }虛函數(shù)
難度:?? 被考到頻率:???
基類和派生類中可以出現(xiàn)名字相同、參數(shù)個(gè)數(shù)和參數(shù)類型都相同的函數(shù),直接調(diào)用時(shí)編譯器會(huì)讓派生類函數(shù)覆蓋基類函數(shù),或者通過(guò)添加作用域限定符來(lái)調(diào)用基類函數(shù)。因此,
下面的例子中,基類和派生類中具有同名函數(shù)display()。
#include <iostream> using namespace std;//定義基類 class Point { protected:float x, y; public:Point(float x = 0, float y = 0); //構(gòu)造函數(shù)void display(); };Point::Point(float a, float b) : x(a), y(b) {} void Point::display() {cout << "[x, y] = [" << x << ", " << y << "]" << endl; }//定義派生類,繼承自父類Point class Circle : public Point { private:float radius; public:Circle(float x = 0, float y = 0, float r = 0); //構(gòu)造函數(shù)void display(); //與基類中同名的函數(shù) };Circle::Circle(float a, float b, float r) : Point(a, b), radius(r) {} void Circle::display() {cout << "[x, y] = [" << x << ", " << y << "]; r = " << radius << endl;return; }int main() {Point p(1, 1); //定義一個(gè)基類對(duì)象Circle c(5, 5, 2.5); //定義一個(gè)派生類對(duì)象Point* pt; //定義一個(gè)基類指針pt = &p; //指針指向基類對(duì)象pt->display(); //調(diào)用同名函數(shù)pt = &c; //指針指向派生類對(duì)象pt->display(); //調(diào)用同名函數(shù)return 0; }輸出結(jié)果。
[x, y] = [1, 1]
[x, y] = [5, 5]
分析輸出結(jié)果可以發(fā)現(xiàn),第一行調(diào)用的基類的成員函數(shù),第二行雖然指針指向了派生類,但由于指針的類型時(shí)基類的,調(diào)用的依舊是基類的成員函數(shù)。如果我們希望指針指向什么類的對(duì)象,就調(diào)用該類的成員函數(shù),則需要將基類的display函數(shù)聲明為虛函數(shù)。
修改代碼,其他地方不修改,只是在基類Point中的聲明display函數(shù)時(shí)添加關(guān)鍵字virtual,如下。
//定義基類 class Point { protected:float x, y; public:Point(float x = 0, float y = 0); //構(gòu)造函數(shù)virtual void display(); }; ... ...運(yùn)行結(jié)果如下。
[x, y] = [1, 1]
[x, y] = [5, 5]; r = 2.5
在設(shè)定虛函數(shù)之前,基類指針本應(yīng)該指向基類對(duì)象,如果指向派生類對(duì)象,則必須進(jìn)行指針類型轉(zhuǎn)換,將派生類的指針轉(zhuǎn)換成基類指針,因此通過(guò)該指針也只能調(diào)用派生類對(duì)象中的基類部分。
設(shè)定虛函數(shù)之后,派生類的基類部分取代了原本基類中的虛函數(shù),因此即使基類指針指向派生類對(duì)象,也會(huì)調(diào)用派生類中的成員函數(shù)。
虛函數(shù)的原理
難度:??? 被考到頻率:??
實(shí)現(xiàn)原理:虛函數(shù)表+虛表指針
虛函數(shù)表(vtbl):是一個(gè)數(shù)組,每個(gè)元素都用來(lái)存儲(chǔ)虛函數(shù)的地址。每個(gè)類有一個(gè)自己的虛函數(shù)表,由于類中虛函數(shù)的個(gè)數(shù)可以在編譯時(shí)期就能確定,因此虛函數(shù)表的大小在編譯時(shí)期就可以確定。虛函數(shù)表是全局共享的,存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū),在編譯時(shí)完成構(gòu)造,全局可用。
虛表指針(vptr):編譯器為每個(gè)類對(duì)象添加一個(gè)隱藏成員,隱藏成員保存了一個(gè)指向虛函數(shù)表的指針,
未完待續(xù)……
總結(jié)
以上是生活随笔為你收集整理的校招八股:C/C++开发工程师常见笔试、面试题目不完全汇总【很基础】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。