c/c++經典面試題

來自:http://www.mianwww.com/html/2012/04/16174.html


面試題1:變量的聲明和定義有什麼區別
爲變量分配地址和存儲空間的稱爲定義,不分配地址的稱爲聲明。一個變量可以在多個地方聲明,但是隻在一個地方定義。加入extern修飾的是變量的聲明,說明此變量將在文件以外或在文件後面部分定義。
說明:很多時候一個變量,只是聲明不分配內存空間,直到具體使用時才初始化,分配內存空間,如外部變量。


面試題2:寫出bool 、int、 float、指針變量與“零值”比較的if語句
bool型數據: if( flag ) { A; } else { B; }
int型數據: if( 0 != flag ) { A; } else { B; }
指針型數: if( NULL == flag ) { A; } else { B; }
float型數據: if ( ( flag >= NORM ) && ( flag <= NORM ) ) { A}
注意:應特別注意在int、指針型變量和“零值”比較的時候,把“零值”放在左邊,這樣當把“==”誤寫成“=”時,編譯器可以報錯,否則這種邏輯錯誤不容易發現,並且可能導致很嚴重的後果。


面試題3:sizeof和strlen的區別
sizeof和strlen有以下區別:
sizeof是一個操作符,strlen是庫函數。
sizeof的參數可以是數據的類型,也可以是變量,而strlen只能以結尾爲‘\0‘的字符串作參數。
編譯器在編譯時就計算出了sizeof的結果。而strlen函數必須在運行時才能計算出來。並且sizeof計算的是數據類型佔內存的大小,而strlen計算的是字符串實際的長度。
數組做sizeof的參數不退化,傳遞給strlen就退化爲指針了。
注意:有些是操作符看起來像是函數,而有些函數名看起來又像操作符,這類容易混淆的名稱一定要加以區分,否則遇到數組名這類特殊數據類型作參數時就很容易出錯。最容易混淆爲函數的操作符就是sizeof。


面試題4:C語言的關鍵字 static 和 C++ 的關鍵字 static 有什麼區別
在C中static用來修飾局部靜態變量和外部靜態變量、函數。而C++中除了上述功能外,還用來定義類的成員變量和函數。即靜態成員和靜態成員函數。
注意:編程時static的記憶性,和全局性的特點可以讓在不同時期調用的函數進行通信,傳遞信息,而C++的靜態成員則可以在多個對象實例間進行通信,傳遞信息。


面試題5:C中的malloc和C++中的new有什麼區別
malloc和new有以下不同:
(1)new、delete 是操作符,可以重載,只能在C++中使用。
(2)malloc、free是函數,可以覆蓋,C、C++中都可以使用。
(3)new 可以調用對象的構造函數,對應的delete調用相應的析構函數。
(4)malloc僅僅分配內存,free僅僅回收內存,並不執行構造和析構函數
(5)new、delete返回的是某種數據類型指針,malloc、free返回的是void指針。
注意:malloc申請的內存空間要用free釋放,而new申請的內存空間要用delete釋放,不要混用。因爲兩者實現的機理不同。


面試題6:寫一個“標準”宏MIN #define min(a,b)((a)<=(b)?(a):(b))
注意:在調用時一定要注意這個宏定義的副作用,如下調用: ((++*p)<=(x)?(++*p):(x)。
p指針就自加了兩次,違背了MIN的本意。


面試題7:一個指針可以是volatile嗎
可以,因爲指針和普通變量一樣,有時也有變化程序的不可控性。常見例:子中斷服務子程序修改一個指向一個buffer的指針時,必須用volatile來修飾這個指針。
說明:指針是一種普通的變量,從訪問上沒有什麼不同於其他變量的特性。其保存的數值是個整型數據,和整型變量不同的是,這個整型數據指向的是一段內存地址。


面試題8:a和&a有什麼區別
請寫出以下代碼的打印結果,主要目的是考察a和&a的區別。 #include<stdio.h> void main( void ) { int a[5]={1,2,3,4,5}; int *ptr=(int *)(&a+1); printf(“%d,%d”,*(a+1),*(ptr-1)); return; }
輸出結果:2,5。
注意:數組名a可以作數組的首地址,而&a是數組的指針。思考,將原式的int *ptr=(int *)(&a+1);改爲int *ptr=(int *)(a+1);時輸出結果將是什麼呢?


面試題9:簡述C、C++程序編譯的內存分配情況
C、C++中內存分配方式可以分爲三種:
(1)從靜態存儲區域分配:
內存在程序編譯時就已經分配好,這塊內存在程序的整個運行期間都存在。速度快、不容易出錯,因爲有系統會善後。例如全局變量,static變量等。
(2)在棧上分配:
在執行函數時,函數內局部變量的存儲單元都在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
(3)從堆上分配:
即動態內存分配。程序在運行的時候用malloc或new申請任意大小的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由程序員決定,使用非常靈活。如果在堆上分配了空間,就有責任回收它,否則運行的程序會出現內存泄漏,另外頻繁地分配和釋放不同大小的堆空間將會產生堆內碎塊。
一個C、C++程序編譯時內存分爲5大存儲區:堆區、棧區、全局區、文字常量區、程序代碼區。

面試題10:簡述strcpy、sprintf與memcpy的區別
三者主要有以下不同之處:
(1)操作對象不同,strcpy的兩個操作對象均爲字符串,sprintf的操作源對象可以是多種數據類型,目的操作對象是字符串,memcpy 的兩個對象就是兩個任意可操作的內存地址,並不限於何種數據類型。
(2)執行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
(3)實現功能不同,strcpy主要實現字符串變量間的拷貝,sprintf主要實現其他數據類型格式到字符串的轉化,memcpy主要是內存塊間的拷貝。
說明:strcpy、sprintf與memcpy都可以實現拷貝的功能,但是針對的對象不同,根據實際需求,來選擇合適的函數實現拷貝功能。


面試題11:設置地址爲0x67a9的整型變量的值爲0xaa66 int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa66;
說明:這道題就是強制類型轉換的典型例子,無論在什麼平臺地址長度和整型數據的長度是一樣的,即一個整型數據可以強制轉換成地址指針類型,只要有意義即可。


面試題12:面向對象的三大特徵
面向對象的三大特徵是封裝性、繼承性和多態性:
封裝性:將客觀事物抽象成類,每個類對自身的數據和方法實行protection(private, protected,public)。
繼承性:廣義的繼承有三種實現形式:實現繼承(使用基類的屬性和方法而無需額外編碼的能力)、可視繼承(子窗體使用父窗體的外觀和實現代碼)、接口繼承(僅使用屬性和方法,實現滯後到子類實現)。
多態性:是將父類對象設置成爲和一個或更多它的子對象相等的技術。用子類對象給父類對象賦值之後,父類對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。
說明:面向對象的三個特徵是實現面向對象技術的關鍵,每一個特徵的相關技術都非常的複雜,程序員應該多看、多練。


面試題13:C++的空類有哪些成員函數
缺省構造函數。
缺省拷貝構造函數。
缺省析構函數。
缺省賦值運算符。
缺省取址運算符。
缺省取址運算符 const。
注意:有些書上只是簡單的介紹了前四個函數。沒有提及後面這兩個函數。但後面這兩個函數也是空類的默認函數。另外需要注意的是,只有當實際使用這些函數的時候,編譯器纔會去定義它們。

面試題14:談談你對拷貝構造函數和賦值運算符的認識
拷貝構造函數和賦值運算符重載有以下兩個不同之處:
(1)拷貝構造函數生成新的類對象,而賦值運算符不能。
(2)由於拷貝構造函數是直接構造一個新的類對象,所以在初始化這個對象之前不用檢驗源對象是否和新建對象相同。而賦值運算符則需要這個操作,另外賦值運算中如果原來的對象中有內存分配要先把內存釋放掉
注意:當有類中有指針類型的成員變量時,一定要重寫拷貝構造函數和賦值運算符,不要使用默認的。


面試題15:用C++設計一個不能被繼承的類

 template <typename T>

class A

 {

friend T;

private: A() {}

~A() {}

 };

 class B : virtual public A<B>

 { public:

B() {}

 ~B() {}

 };

class C : virtual public B

{

public:

C() {}

 ~C() {}

 };

void main( void ) { B b; //C c; return; }
注意:構造函數是繼承實現的關鍵,每次子類對象構造時,首先調用的是父類的構造函數,然後纔是自己的。


面試題16:訪問基類的私有虛函數
寫出以下程序的輸出結果:

 #include <iostream.h>

class A
{

virtual void g() { cout << “A::g” << endl; }

private:

virtual void f() { cout << “A::f” << endl; }

};

 class B : public A

 {

void g() { cout << “B::g” << endl; }

virtual void h() { cout << “B::h” << endl; }

};

typedef void( *Fun )( void );

void main()

{

B b; Fun pFun;

 for(int i = 0 ; i < 3; i++)

{ pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i ); pFun(); }

 }
輸出結果: B::g A::f B::h
注意:本題主要考察了面試者對虛函數的理解程度。一個對虛函數不瞭解的人很難正確的做出本題。在學習面向對象的多態性時一定要深刻理解虛函數表的工作原理。


面試題17:簡述類成員函數的重寫、重載和隱藏的區別
(1)重寫和重載主要有以下幾點不同。
範圍的區別:被重寫的和重寫的函數在兩個類中,而重載和被重載的函數在同一個類中。
參數的區別:被重寫函數和重寫函數的參數列表一定相同,而被重載函數和重載函數的參數列表一定不同。
virtual的區別:重寫的基類中被重寫的函數必須要有virtual修飾,而重載函數和被重載函數可以被virtual修飾,也可以沒有。
(2)隱藏和重寫、重載有以下幾點不同。
與重載的範圍不同:和重寫一樣,隱藏函數和被隱藏函數不在同一個類中。
參數的區別:隱藏函數和被隱藏的函數的參數列表可以相同,也可不同,但是函數名肯定要相同。當參數不相同時,無論基類中的參數是否被virtual修飾,基類的函數都是被隱藏,而不是被重寫。
說明:雖然重載和覆蓋都是實現多態的基礎,但是兩者實現的技術完全不相同,達到的目的也是完全不同的,覆蓋是動態態綁定的多態,而重載是靜態綁定的多態。


面試題18:簡述多態實現的原理
編譯器發現一個類中有虛函數,便會立即爲此類生成虛函數表 vtable。虛函數表的各表項爲指向對應虛函數的指針。編譯器還會在此類中隱含插入一個指針vptr(對vc編譯器來說,它插在類的第一個位置上)指向虛函數表。調用此類的構造函數時,在類的構造函數中,編譯器會隱含執行vptr與vtable的關聯代碼,將vptr指向對應的vtable,將類與此類的vtable聯繫了起來。另外在調用類的構造函數時,指向基礎類的指針此時已經變成指向具體的類的this指針,這樣依靠此this指針即可得到正確的vtable,。如此才能真正與函數體進行連接,這就是動態聯編,實現多態的基本原理。
注意:一定要區分虛函數,純虛函數、虛擬繼承的關係和區別。牢記虛函數實現原理,因爲多態C++面試的重要考點之一,而虛函數是實現多態的基礎。


面試題19:鏈表和數組有什麼區別
數組和鏈表有以下幾點不同:
(1)存儲形式:數組是一塊連續的空間,聲明時就要確定長度。鏈表是一塊可不連續的動態空間,長度可變,每個結點要保存相鄰結點指針。
(2)數據查找:數組的線性查找速度快,查找操作直接使用偏移地址。鏈表需要按順序檢索結點,效率低。
(3)數據插入或刪除:鏈表可以快速插入和刪除結點,而數組則可能需要大量數據移動。
(4)越界問題:鏈表不存在越界問題,數組有越界問題。
說明:在選擇數組或鏈表數據結構時,一定要根據實際需要進行選擇。數組便於查詢,鏈表便於插入刪除。數組節省空間但是長度固定,鏈表雖然變長但是佔了更多的存儲空間。


面試題20:怎樣把一個單鏈表反序
(1)反轉一個鏈表。

循環算法。

 List reverse(List n)

 {

 if(!n) //判斷鏈表是否爲空,爲空即退出。

 { return n; }

 list cur = n.next; //保存頭結點的下個結點

list pre = n; //保存頭結點

 list tmp;
pre.next = null; //頭結點的指針指空,轉換後變尾結點

while ( NULL != cur.next ) //循環直到cur.next爲空

{ tmp = cur; 

 tmp.next = pre ;

pre = tmp;

cur = cur.next;

 }

return tmp; //f返回頭指針

 }


(2)反轉一個鏈表。

遞歸算法。

 List *reverse( List *oldList, List *newHead = NULL )

 { List *next = oldList-> next; //記錄上次翻轉後的鏈表

oldList-> next = newHead; //將當前結點插入到翻轉後鏈表的開頭

 newHead = oldList; //遞歸處理剩餘的鏈表

return ( next==NULL )? newHead: reverse( t, newHead );

 }


面試題 21:簡述隊列和棧的異同
隊列和棧都是線性存儲結構,但是兩者的插入和刪除數據的操作不同,隊列是“先進先出”,棧是“後進先出”。
注意:區別棧區和堆區。堆區的存取是“順序隨意”,而棧區是“後進先出”。棧由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。堆一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收。分配方式類似於鏈表。
它與本題中的堆和棧是兩回事。堆棧只是一種數據結構,而堆區和棧區是程序的不同內存存儲區域。


面試題22:能否用兩個棧實現一個隊列的功能

面試題23:計算一顆二叉樹的深度

int depth(BiTree T)

 {

 if(!T)

return 0; //判斷當前結點是否爲葉子結點
int d1= depth(T->lchild); //求當前結點的左孩子樹的深度

 int d2= depth(T->rchild); //求當前結點的右孩子樹的深度

 return (d1>d2?d1:d2)+1;

}
面試題24:編碼實現直接插入排序

面試題25:編碼實現冒泡排序

面試題26:編碼實現直接選擇排序


面試題27:編程實現堆排序

面試題28:編程實現基數排序


面試題29:談談你對編程規範的理解或認識
編程規範可總結爲:程序的可行性,可讀性、可移植性以及可測試性。


面試題31:&&和&、||和|有什麼區別
(1)&和|對操作數進行求值運算,&&和||只是判斷邏輯關係。
(2)&&和||在在判斷左側操作數就能確定結果的情況下就不再對右側操作數求值。

面試題32:C++的引用和C語言的指針有什麼區別
指針和引用主要有以下區別:
(1)引用必須被初始化,但是不分配存儲空間。指針不聲明時初始化,在初始化的時候需要分配存儲空間。
(2)引用初始化以後不能被改變,指針可以改變所指的對象。
(3)不存在指向空值的引用,但是存在指向空值的指針。

面試題33:在二元樹中找出和爲某一值的所有路徑
輸入一個整數和一棵二元樹。從樹的根結點開始往下訪問,一直到葉結點所經過的所有結點形成一條路徑。打印出和與輸入整數相等的所有路徑。例如,輸入整數9和如下二元樹:
3
/ \
2 6
/ \
5 4
則打印出兩條路徑:3,6和3,2,4。

面試題34:寫一個“標準”宏MIN
寫一個“標準”宏MIN,這個宏輸入兩個參數並且返回較小的一個。
【答案】 #define min(a,b)((a)<=(b)?(a):(b))
注意:在調用時一定要注意這個宏定義的副作用,如下調用: ((++*p)<=(x)?(++*p):(x)。
p指針就自加了兩次,違背了MIN的本意。


面試題35:typedef和define有什麼區別
(1)用法不同:typedef用來定義一種數據類型的別名,增強程序的可讀性。define主要用來定義常量,以及書寫複雜使用頻繁的宏。
(2)執行時間不同:typedef是編譯過程的一部分,有類型檢查的功能。define是宏定義,是預編譯的部分,其發生在編譯之前,只是簡單的進行字符串的替換,不進行類型的檢查。
(3)作用域不同:typedef有作用域限定。define不受作用域約束,只要是在define聲明後的引用都是正確的。
(4)對指針的操作不同:typedef和define定義的指針時有很大的區別。
注意:typedef定義是語句,因爲句尾要加上分號。而define不是語句,千萬不能在句尾加分號。

面試題36:關鍵字const是什麼
const用來定義一個只讀的變量或對象。

主要優點:便於類型檢查、同宏定義一樣可以方便地進行參數的修改和調整、節省空間,避免不必要的內存分配、可爲函數重載提供參考。
說明:const修飾函數參數,是一種編程規範的要求,便於閱讀,一看即知這個參數不能被改變,實現時不易出錯。


面試題37:static有什麼作用
static在C中主要用於定義全局靜態變量、定義局部靜態變量、定義靜態函數。在C++中新增了兩種作用:定義靜態數據成員、靜態函數成員。
注意:因爲static定義的變量分配在靜態區,所以其定義的變量的默認值爲0,普通變量的默認值爲隨機數,在定義指針變量時要特別注意。


面試題38:extern有什麼作用
extern標識的變量或者函數聲明其定義在別的文件中,提示編譯器遇到此變量和函數時在其它模塊中尋找其定義。


面試題39:流操作符重載爲什麼返回引用
在程序中,流操作符>>和<<經常連續使用。

因此這兩個操作符的返回值應該是一個仍舊支持這兩個操作符的流引用。其他的數據類型都無法做到這一點。
注意:除了在賦值操作符和流操作符之外的其他的一些操作符中,如+、-、*、/等卻千萬不能返回引用。因爲這四個操作符的對象都是右值,因此,它們必須構造一個對象作爲返回值。


面試題40:簡述指針常量與常量指針區別
指針常量是指定義了一個指針,這個指針的值只能在定義時初始化,其他地方不能改變。

常量指針是指定義了一個指針,這個指針指向一個只讀的對象,不能通過常量指針來改變這個對象的值。
指針常量強調的是指針的不可改變性,而常量指針強調的是指針對其所指對象的不可改變性。
注意:無論是指針常量還是常量指針,其最大的用途就是作爲函數的形式參數,保證實參在被調用函數中的不可改變特性。


面試題41:數組名和指針的區別
請寫出以下代碼的打印結果:

 #include <iostream.h>

#include <string.h>

void main(void)

 {

 char str[13]=”Hello world!”;
char *pStr=”Hello world!”;

 cout<<sizeof(str)<<endl;

cout<<sizeof(pStr)<<endl;

cout<<strlen(str)<<endl;

cout<<strlen(pStr)<<endl;

 return;

 }
【答案】
打印結果: 13 4 12 12
注意:一定要記得數組名並不是真正意義上的指針,它的內涵要比指針豐富的多。但是當數組名當做參數傳遞給函數後,其失去原來的含義,變作普通的指針。另外要注意sizeof不是函數,只是操作符。


面試題42:如何避免“野指針”
“野指針”產生原因及解決辦法如下:
(1)指針變量聲明時沒有被初始化。解決辦法:指針聲明時初始化,可以是具體的地址值,也可讓它指向NULL。
(2)指針 p 被 free 或者 delete 之後,沒有置爲 NULL。解決辦法:指針指向的內存空間被釋放後指針應該指向NULL。
(3)指針操作超越了變量的作用範圍。解決辦法:在變量的作用域結束前釋放掉變量的地址空間並且讓指針指向NULL。
注意:“野指針”的解決方法也是編程規範的基本原則,平時使用指針時一定要避免產生“野指針”,在使用指針前一定要檢驗指針的合法性。


面試題43:常引用有什麼作用
常引用的引入主要是爲了避免使用變量的引用時,在不知情的情況下改變變量的值。

常引用主要用於定義一個普通變量的只讀屬性的別名、作爲函數的傳入形參,避免實參在調用函數中被意外的改變。

面試題44:編碼實現字符串轉化爲數字
編碼實現函數atoi(),設計一個程序,把一個字符串轉化爲一個整型數值。例如數字:“5486321”,轉化成字符:5486321。

面試題45:簡述strcpy、sprintf與memcpy的區別
三者主要有以下不同之處:
(1)操作對象不同,strcpy的兩個操作對象均爲字符串,sprintf的操作源對象可以是多種數據類型,
目的操作對象是字符串,memcpy 的兩個對象就是兩個任意可操作的內存地址,並不限於何種數據類型。
(2)執行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
(3)實現功能不同,strcpy主要實現字符串變量間的拷貝,sprintf主要實現其他數據類型格式到字符串的轉化,memcpy主要是內存塊間的拷貝。
說明:strcpy、sprintf與memcpy都可以實現拷貝的功能,但是針對的對象不同,根據實際需求,來選擇合適的函數實現拷貝功能。


面試題46:用C編寫一個死循環程序

while(1) { }

面試題47:編碼實現某一變量某位清0或置1
給定一個整型變量a,寫兩段代碼,第一個設置a的bit 3,第二個清a的bit 3,在以上兩個操作中,要保持其他位不變。
【答案】 #define BIT3 (0×1 << 3 ) Satic int a;
設置a的bit 3: void set_bit3( void ) { a |= BIT3; //將a第3位置1 }
清a的bit 3 void set_bit3( void ) { a &= ~BIT3; //將a第3位清零 }
說明:在置或清變量或寄存器的某一位時,一定要注意不要影響其他位。所以用加減法是很難實現的。


面試題48:評論下面這個中斷函數
中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展——讓標準C支持中斷。

具體代表事實是,產生了一個新的關鍵字__interrupt。

下面的代碼就使用了__interrupt關鍵字去定義一箇中斷服務子程序(ISR),

請評論以下這段代碼。

 __interrupt double compute_area (double radius)

{

double area = PI * radius * radius;

 printf(” Area = %f”, area);

 return area;

}
【答案】
這段中斷服務程序主要有以下四個問題:
(1)ISR 不能返回一個值。
(2)ISR 不能傳遞參數。
(3)在ISR 中做浮點運算是不明智的。
(4)printf()經常有重入和性能上的問題。
注意:本題的第三個和第四個問題雖不是考察的重點,但是如果能提到這兩點可給面試官留下一個好印象。


面試題49:構造函數能否爲虛函數
構造函數不能是虛函數。


面試題50:談談你對面向對象的認識
面向對象可以理解成對待每一個問題,都是首先要確定這個問題由幾個部分組成,而每一個部分其實就是一個對象。然後再分別設計這些對象,最後得到整個程序。傳統的程序設計多是基於功能的思想來進行考慮和設計的,而面向對象的程序設計則是基於對象的角度來考慮問題。這樣做能夠使得程序更加的簡潔清晰。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章