閱讀664 返回首頁    go 阿裏雲 go 技術社區[雲棲]


【C++】C++常見麵試題匯總,持續更新中...

1:指針(*)、引用(&)、解引用(*)、取地址(&)、的概念和區別

概念:

指針指向一塊內存,指針保存的是內存的地址;引用是變量的別名,本質是引用該變量的地址

解引用是取指針指向的地址的內容,取地址是獲得變量在內存中的地址。

區別:

(1)引用使用是無需解引用,指針需解引用。

(2)引用不能為空,指針可以為空。

(3)引用在定義時被初始化一次,之後不可變;指針指向的值和本身的值是可變的,也就是說指針隻是一塊地址,地址裏的東西可變。

(4)程序會給指針變量分配內存區域,而引用不需要分配內存區域。

2:memset、memcpy和strcpy的區別

memcpy是內存拷貝函數,可以拷貝任何數據類型的對象,例如memcpy(b, a, sizeof(b))。

strcpy隻能拷貝字符串,遇到’\0′結束拷貝。

memset用來對一段內存空間全部設置為某個字符,例如:char a[100];memset(a, '', sizeof(a))。


3:struct和class的區別,struct與union的區別

struct和class都是聲明類的關鍵字

區別是:

(1)在默認情況下,struct的成員變量是公共(public)的;在默認情況下,class的成員變量是私有(private)的。

(2)struct保證成員按照聲明順序在內存中存儲。class不能保證。

(3)對於繼承來說,class默認是private繼承,struct默認是public繼承。

區別是(union和class同理):

(1)一個union類型的變量,所有成員變量共享一塊內存,該內存的大小有這些成員變量中長度最大的一個來決定,

struct中成員變量內存都是獨立的。

(2)union分配的內存是連續的,而struct不能保證分配的內存是連續的。

struct與union的區別:

(1)struct與union都由多個不同的數據類型成員組成。但是, union的成員共享存儲空間一塊地址空間,而struct的成員則不是。
(2)因此,對於union的一個成員賦值, 那麼其它成員會重寫,而struct則不會

4:指針在16位機、32位機、64位機分別占用多少個字節

16位機--2字節、32位機--4字節、64位機--8字節。

5:如何引用一個已經定義過的全局變量?區別是什麼

如果在同一個文件中,直接引用即可。

如果不在同一個文件,有兩種方式:

(1)直接引用頭文件就可以使用了。

(2)用extern關鍵字重新聲明一下。

6:全局變量可不可以定義在可被多個.C文件包含的頭文件中?因為全局變量的作用域是整個源程序,可以聲明多次,但是隻能定義一次。變量的聲明一般放在頭文件中,那麼變的定義可以放在頭文件中嗎?在實際的編程中一般很少在頭文件中定義全局變量,因為多次引用可能重定義。7:do……while和while……do有什麼區別?

do...while先執行循環再判斷條件,while...do先判斷條件再執行循環。

8:對於一個頻繁使用的短小函數,在C語言中應用什麼實現,在C++中應用什麼實現?

C用宏定義,C++用inline。9:main 函數執行以前,會執行什麼代碼?

全局對象的構造函數會在main 函數之前執行,比如int a;初始化為0。

10:main 主函數執行完畢後,會執行什麼代碼?

可以,使用on_exit 注冊的函數會在代碼執行完畢後執行:

void main( void )

{

String str(“zhanglin”);

on_exit( fn1 );

on_exit( fn2 );

on_exit( fn3 );

on_exit( fn4 );

printf( “This is executed first.\n” );

}

int fn1()

{

printf( “next.\n” );

return 0;

}

11:局部變量能否和全局變量重名?

可以,但是局部會屏蔽全局。要用全局變量,需要使用域作用符“::”。

12:描述內存分配方式以及它們的區別?1.從靜態存儲區域分配。該存儲區域在程序編譯的時候就已經分配好了,這塊內存在程序的整個運行期間都存在。例如全局變量,static 變量。

2. 在棧上創建。在執行函數時,函數的局部變量存儲在該區域,函數執行結束時會釋放該存儲空間。棧內存分配運算內置於處理器的指令集。

3. 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc 或new 申請任意多少的內存,程序員自己負責在何時用free 或delete 釋放內存。     動態內存的生存期由程序員決定,使用非常靈活。

13:類的成員函數重載、覆蓋和隱藏的概念和區別?概念:重載是指再同一個作用域內,有幾個同名的函數,但是參數列表的個數和類型不同。 函數覆蓋是指派生類函數覆蓋基類函數,函數名、參數類型、返回值類型一模一樣。派生類的對象會調用子類中的覆蓋版本,覆蓋父類中的函數版本。         “隱藏”是指派生類的函數屏蔽了與其同名的基類函數。覆蓋和重載的區別:函數是否處在不同的作用域,參數列表是否一樣;基類函數是否有virtual關鍵字。

隱藏和覆蓋的區別:

(1)派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏。

(2)派生類的函數與基類的函數同名,參數也相同,但是基類函數沒有virtual 關鍵字。此時,基類的函數被隱藏有virtual,就是覆蓋。

如果子類覆蓋父類的函數但是不加virtual ,也能實現多態,由於virtual修飾符會被隱形繼承,但是盡量加上。

14:static關鍵字?

為什麼引入static:

編譯器為局部變量在棧上分配空間,但是函數執行結束時會釋放掉。所以static可以解決函數執行完後變量值被釋放的問題,static變量存儲在靜態存儲區。

缺點:

不安全,因為作用域是全局的。

優點:

因為靜態變量對所有對象所公有的,可以節省內存,提高時間效率。

靜態數據成員使用方式:

<類名>::<靜態成員名>

作用:

(1)可以用於全局變量的定義:為該變量分配靜態存儲區。程序運行結束前不會被釋放。
(2)聲明和定義靜態成員函數:表示該函數為靜態函數,隻能在本文件中被調用。
(3)定義靜態局部變量:隻被初始化一次,隻有程序運行結束才會釋放。區別是作用域的範圍。

15:const與#define的概念和優缺點?

const用來定義常量、修飾函數參數、修飾函數返回值,可以避免被修改,提高程序的健壯性

define是宏定義,在編譯的時候會進行替換,這樣做的話可以避免沒有意義的數字或字符串,便於程序的閱讀。

區別:

1.const定義的數據有數據類型,而宏常量沒有數據類型。編譯器可以對const常量進行類型檢查。而對宏定義隻進行字符替換,沒有類型安全檢查,所以字符替換時可能出錯。

例子:

寫一個“標準”宏MIN,這個宏輸入兩個參數並返回較小的一個。

#define MIN(A,B) ((A) <= (B) (A) : (B))

least = MIN(a, b);

下麵的關鍵字const是什麼含意:

const int a;//a是一個常整型數

int const a;//a是一個常整型數

const int *a;//a是一個指向常整型數的指針

int * const a;//a是一個指向整型數的常指針

int const * a const;//a是一個指向常整型數的常指針


static關鍵字的作用:

(1)函數體內static變量的作用範圍為該函數體,不同於auto變量,該變量的內存隻被分配一次,因此其值在下次調用時仍維持上      次的值;

(2)在模塊內的static全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;

(3)在模塊內的static函數隻可被這一模塊內的其它函數調用,這個函數的使用範圍被限製在聲明它的模塊內;

(4)在類中的static成員變量屬於整個類所擁有,對類的所有對象隻有一份拷貝;

(5)在類中的static成員函數屬於整個類所擁有,這個函數不接收this指針,因而隻能訪問類的static成員變量。

const關鍵字的作用:

(1)欲阻止一個變量被改變,可以使用const關鍵字。在定義該const變量時,通常需要對它進行初始化,因為以後就沒有機會再去改變它了;

(2)對指針來說,可以指定指針本身為const,也可以指定指針所指的數據為const,或二者同時指定為const;

(3)在一個函數聲明中,const可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;

(4)對於類的成員函數,若指定其為const類型,則表明其是一個常函數,不能修改類的成員變量;

(5)對於類的成員函數,有時候必須指定其返回值為const類型,以使得其返回值不為“左值”。例如:

16:堆棧溢出的原因?

沒有回收垃圾資源

棧溢出:

一般都是由越界訪問導致的。例如局部變量數組越界訪問或者函數內局部變量使用過多,超出了操作係統為該進程分配的棧的大小。

堆溢出:

由於堆是用戶申請的,所以溢出的原因可能是程序員申請了資源但是忘記釋放了。

17:刷新緩衝區方式?

換行刷新緩衝區:printf(“\n”);程序結束刷新緩衝區:return 0;18:類和對象的兩個基本概念?類的作用或概念:用來描述一組具有相似屬性的東西的對象的一種數據結構。類中有數據成員的聲明和定義,有成員函數的實現代碼。對象就是類的實例化。計算機中想要使用類,隻能進行實例化。

19:介紹一下STL,詳細說明STL如何實現vector


STL是標準模版庫,由容器算法迭代器組成。

STL有以下的一些優點:

(1)可以很方便的對一堆數據進行排序(調用sort());

(2)調試程序時更加安全和方便;

(3)stl是跨平台的,在linux下也能使用

vector實質上就是一個動態數組,會根據數據的增加,動態的增加數組空間。

什麼是容器。如何實現?

容器是一種類類型,用來存儲數據

STL有7種主要容器:vector,list,deque,map,multimap,set,multiset.

20:變量的聲明和定義有什麼區別

變量的聲明是告訴編譯器我有某個類型的變量,但不會為其分配內存。但是定義會分配了內存。

21:簡述#define #endif 和#ifndef的作用

這三個命令一般是為了避免頭文件被重複引用。

#ifndef CH_H //意思是如果沒有引用ch.h

#define CH_H //引用ch.h

#endif  //否則不需要引用


22:引用與指針有什麼區別?

(1) 引用必須被初始化,指針不必。

(2) 引用初始化以後不能被改變,指針可以改變所指的對象。

(3) 不存在指向空值的引用,但是存在指向空值的指針。

23:C++繼承機製?

n類成員的訪問控製方式

public:類本身、派生類和其它類均可訪問;

protected:類本身和派生類均可訪問,其它類不能訪問;

private(默認):類本身可訪問,派生類和其它類不能訪問。

繼承成員的訪問控製規則

——由父類成員的訪問控製方式和繼承訪問控製方式共同決定

private+public(protectd,private)=>不可訪問

pubic(protected)+public=>public(protected)

public(protected)+protected=>protected

public(protected)+private(默認)=>private

C++中的模板和virtual異同? ==>?

private繼承和public繼承區別? ==>?


24:什麼是內存泄露?C++內存泄漏檢測內存泄露是指程序中動態分配了內存,但是在程序結束時沒有釋放這部分內存,從而造成那一部分內存不可用的情況

有一些內存泄漏的檢測工具,比如BoundsChecker。

靜態內存泄漏通過工具或者仔細檢查代碼找到泄漏點。

動態的內存泄漏很難查,一般通過在代碼中加斷點跟蹤和Run-Time內存檢測工具來查找。

內存泄漏的檢測可以分以下幾個步驟:

(1)看代碼new之後是否delete,就是申請了靜態內存用完是否釋放。看析構函數是否真的執行,如果沒有真正執行,就需要動態釋放對象;

(2)讓程序長時間運行,看任務管理器對應程序內存是不是一直向上增加;

(3)使用常用內存泄漏檢測工具來檢測內存泄漏點。

25:頭文件的作用是什麼?

(1)頭文件用於保存程序的聲明。
(2)通過頭文件可以來調用庫函數。因為有些代碼不能向用戶公布,隻要向用戶提供頭文件和二進製的庫即可。用戶隻需要按照頭文件中的接口聲明來調用庫功能,編譯器會從庫中提取相應的代碼。
(3)如果某個接口被實現或被使用時,其方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這一簡單的規則能大大減輕程序員調試、改錯的負擔。

26:函數模板與類模板有什麼區別?

答:函數模板的實例化是由編譯程序在處理函數調用時自動完成的,而類模板的實例化必須由程序員在程序中顯式地指定。

函數模板是模板的一種,可以生成各種類型的函數實例:

template

Type min( Type a, Type b ) 

{

return a < b ? a : b;

}

參數一般分為類型參數和非類型參數:

類型參數代表了一種具體的類型

非類型參數代表了一個常量表達式


27:system(”pause”)的作用?

調用DOS的命令,按任意鍵繼續,和getchar()差不多;省去了使用getchar();區別是一個屬於係統命令,一個屬於c++標準函數庫。

28:析構函數和虛函數的用法和作用?

析構函數是類成員函數,在類對象生命期結束的時候,由係統自動調用,釋放在構造函數中分配的資源。

虛函數是為了實現多態。含有純虛函數的類稱為抽象類,不能實例化對象,主要用作接口類

Test(int j):pb(j),pa(pb+5)  
 
 

~Test()    
{    
cout<<"釋放堆區director內存空間1次";    
}    

析構函數的特點:

1. 函數名稱固定:~類名( )
2. 沒有返回類型,沒有參數
3. 不可以重載,一般由係統自動的調用

29:編寫一個標準strcpy函數

可以拿10
char * strcpy( char *strDest, const char *strSrc )//為了實現鏈式操作,將目的地址返回,加3分!
{
assert( (strDest != NULL) &&(strSrc != NULL) );
char *address = strDest;
while( (*strDest++ = * strSrc++) != ‘\0’ );
return address;
}

30:new、delete;malloc、free關係

new和delete是一組,new用調用構造函數來實例化對象和調用析構函數釋放對象申請的資源。

malloc和free是一對,malloc用來申請內存和釋放內存,但是申請和釋放的對象隻能是內部數據類型。

區別:

malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。

maloc/free隻能操作內部數據類型

31:delete與 delete []區別
都是用來調用析構函數的:

(1)delete隻會調用一次析構函數,delete[]會調用每一個成員的析構函數。

(2)delete與new配套,delete []與new []配套,用new分配的內存用delete刪除用new[]分配的內存用delete[]刪除

32:繼承優缺點
優點:

繼承可以方便地改變父類的實現,可以實現多態,子類可以繼承父類的方法和屬性。

缺點:

破壞封裝,子類和父類可能存在耦合。

子類不能改變父類的接口。

33:C和C++有什麼不同?

(1)c是麵向過程的,也就是說更偏向邏輯設計;c++是麵向對象的,提供了類,偏向類的設計。

(2)c適合要求代碼體積小的,效率高的場合,如比如嵌入式。


34:析構函數的調用次序,子類析構時要調用父類的析構函數嗎?

析構函數調用的次序是:先派生類的析構後基類的析構,也就是說在基類的的析構調用的時候,派生類的信息已經全部銷毀了定義一個對象時先調用基類的構造函數、然後調用派生類的構造函數;35:什麼是“野指針”?野指針指向一個已刪除的對象或無意義地址的指針。與空指針不同,野指針無法通過簡單地判斷是否為 NULL避免,而隻能通過養成良好的編程習慣來盡力避免。造成的主要原因是:指針變量沒有被初始化,或者指針p被free或者delete之後,沒有置為NULL。36:常量指針和指針常量的區別?常量指針:是一個指向常量的指針。可以防止對指針誤操作而修改該常量。
指針常量:是一個常量,且是一個指針。指針常量不能修改指針所指向的地址,一旦初始化,地址就固定了,不能對它進行移動操作。但是指針常量的內容是可以改變。


37:sizeof的概念(作用),舉例

sizeof是C語言的一種單目操作符,並不是函數。sizeof以字節的形式返回操作數的大小。

(1)sizeof(int **a[3][4])

int **p; //16位下sizeof(p)=2, 32位下sizeof(p)=4

總共 3*4*sizeof(p)

(2)若操作數具有類型char、unsigned char或signed char,其結果等於1。 

(3)當操作數是指針時,sizeof依賴於係統的位數 
(4)當操作數具有數組類型時,其結果是數組的總字節數。 
(5)聯合類型操作數的sizeof是其最大字節成員的字節數。

結構類型操作數的sizeof是這種類型對象的總字節數(考慮對齊問題時)。這樣做可以提高程序的性能,避免訪問兩次內存;
(6)如果操作數是函數中的數組形參或函數類型的形參,sizeof給出其指針的大小。 


sizeof和strlen()的區別:① sizeof是運算符,計算數據所占的內存空間;strlen()是一個函數,計算字符數組的字符數;
② sizeof可以用類型作參數;strlen()隻能用char*作參數,必須是以‘/0’結束
③ 數組做sizeof的參數不退化,傳遞給strlen就退化為指針了;
④ sizeof操作符的結果類型是size_t,它在頭文件中typedef為unsigned int類型。該類型保證能容納實現建立的最大對象的字節大小。

38:如果NULL 和0 作為空指針常數是等價的, 那到底該用哪一個?

#define NULL 0

按道理說,null和0,沒有區別,但為何要多此一舉呢,

(1)什麼是空指針常量?

0、0L、'\0'、3 - 3、0 * 17 以及 (void*)0都是空指針常量。

(2)什麼是空指針?

如果 p 是一個指針變量,則 p = 0;、p = 0L;、p = '\0';、p = 3 - 3;、p = 0 * 17; 中的任何一種賦值操作之後, p 都成為一個空指針,由係統保證空指針不指向任何實際的對象或者函數。

(3)什麼是 NULL?

即 NULL 是一個標準規定的宏定義,用來表示空指針常量。因此,除了上麵的各種賦值方式之外,還可以用 p = NULL; 來使 p 成為一個空指針。

(4)空指針指向了內存的什麼地方?

標準並沒有對空指針指向內存中的什麼地方這一個問題作出規定,一般取決於係統的實現。我們常見的空指針一般指向 0 地址,即空指針的內部用全 0 來表示。

空指針的“邏輯地址”一定是0,對於空指針的地址,操作係統是特殊處理的。並非空指針指向一個0地址的物理地址。

在實際編程中不需要了解在我們的係統上空指針到底是一個 0指針還是非0地址,我們隻需要了解一個指針是否是空指針就可以了——編譯器會自動實現其中的轉換,為我們屏蔽其中的實現細節。

(5)可以用 memset 函數來得到一個空指針嗎?

這個問題等同於:如果 p 是一個指針變量,那麼memset( &p, 0, sizeof(p) ); 和 p = 0;是等價的嗎?

答案是否定的,雖然在大多數係統上是等價的,但是因為有的係統存在著“非零空指針” (nonzero null pointer),所以這時兩者不等價。

(6)可以定義自己的 NULL 的實現嗎?兼答"NULL 的值可以是 1、2、3 等值嗎?"類似問題

NULL 是標準庫中的一個符合上述條件的保留標識符。所以,如果包含了相應的標準頭文件而引入了 NULL 的話,則再在程序中重新定義 NULL 為不同的內容是非法的,其行為是未定義的。也就是說,如果是符合標準的程序,其 NULL 的值隻能是 0,不可能是除 0 之外的其它值,比如 1、2、3 等。

(7)malloc 函數在分配內存失敗時返回 0 還是 NULL?

malloc 函數是標準 C 規定的庫函數。在標準中明確規定了在其內存分配失敗時返回的是空指針

39:如果NULL定義成#define NULL ((char *)0) 難道不就可以向函數傳入不加轉換的NULL了嗎?

不行。因為有的機器不同類型數據的指針有不同的內部表達。如果是字符指針的函數沒有問題, 但對於其它類型的指針參數仍然有問題, 而合法的構造如FILE *fp = NULL;則會失敗。
如果定義#define NULL ((void *)0)除了潛在地幫助錯誤程序運行以外, 這樣的定義還可以發現錯誤使用NULL 的程序。無論如何, ANSI 函數原型確保大多數指針參數在傳入函數時正確轉換。

40:使用非全零的值作為空指針內部表達的機器上, NULL是如何定義的?

跟其它機器一樣: 定義為0 。當程序員請求一個空指針時, 無論寫“0” 還是“NULL”, 都是有編譯器來生成適合機器的空指針的二進製表達形式。因此, 在空指針的內部表達不為0 的機器上定義NULL 為0 跟在其它機器上一樣合法:編譯器在指針上下文看到的未加修飾的0 都會被生成正確的空指針。

41:NULL 是什麼, 它是怎麼定義的?

很多人不願意在程序中到處出現未加修飾的0。因此定義了預處理宏NULL為空指針常數, 通常是0 或者((void *)0) 。希望區別整數0 和空指針0 的人可以在需要空指針的地方使用NULL。

42:用“if(p)” 判斷空指針是否可靠?如果空指針的內部表達不是0 會怎麼樣?

表達式中要求布爾值時:如果表達式等於0 則認為該值為假。if(p) 等價於if(p != 0)。

43:怎樣在程序裏獲得一個空指針?

在指針上下文中的常數0 會在編譯時轉換為空指針。
char *p = 0;
if(p != 0)

44:空指針到底是什麼?

空指針表示“未分配” 或者“尚未指向任何地方” 的指針。
空指針在概念上不同於未初始化的指針。空指針可以確保不指向任何對象或函數; 而未初始化指針則可能指向任何地方。

45:我能否用void** 指針作為參數, 使函數按引用接受一般指針?

不可移植。C 中沒有一般的指針的指針類型。void* 可以用作一般指針隻是因為當它和其它類型相互賦值的時候, 如果需要, 它可以自動轉換成其它類型; 但是, 如果試圖這樣轉換所指類型為void* 之外的類型的void** 指針時, 這個轉換不能完成。

46:我有一個char * 型指針剛好指向一些int 型變量, 我想跳過它們。 為什麼((int *)p)++; 不行?

類型轉換的實質“把這些二進製位看作另一種類型, 並作相應的對待”; ((int *)p)++是一個轉換操作符, 根據定義它隻能生成一個右值(rvalue)。而右值既不能賦值, 也不能用++ 自增。正確的做法:p = (char *)((int *)p + 1);

47:*p++ 自增p 還是p 所指向的變量?

*p++ 和*(p++) 等價。要自增p 指向的值, 使用(*p)++, 或者++*p。

48:我想聲明一個指針並為它分配一些空間,代碼char *p; *p = malloc(10)的問題;

你所聲明的指針是p, 而不是*p, 當你操作指針本身時, 你隻需要使用指針的名字即可:p = malloc(10);

49:int i=7; printf(“%d\n”, i++ *i++);的值?

i++*i++=49

50:枚舉和#define 有什麼不同?1):#define 是在預編譯階段進行簡單替換。枚舉常量則是在編譯的時候確定其值。
2):一般在編譯器裏,可以調試枚舉常量,但是不能調試宏常量。
3):枚舉可以一次定義大量相關的常量,而#define 宏一次隻能定義一個。
51:C++文件編譯與執行的四個階段

第一階段:預處理階段。根據文件中的預處理指令來修改源文件的內容。如#include指令,作用是把頭文件的內容添加到.cpp文件中。

第二階段:編譯階段,將其翻譯成等價的中間代碼或匯編代碼。

第三階段:匯編階段,把匯編語言翻譯成目標機器指令。

第四階段:是鏈接,例如,某個源文件中的函數可能引用了另一個源文件中定義的某個函數;在程序中可能調用了某個庫文件中的函數。


52聲明struct x1 { . . . }; 和typedef struct { . . . } x2; 有什麼不同?

第一種形式聲明了一個“結構標簽”; 

第二種聲明了一個“類型定義”。

主要的區別是第一種方式定義結構體變量需要寫“struct x1”而引用第一種, 而第二種方式定義結構體變量不需要使用struct 關鍵字。

53:以下的初始化有什麼區別?

char a[] = “string literal”; char *p= “string literal”
用作數組初始值, 它指明該數組中字符的初始值。

第二種情況會轉化為一個無名的靜態字符數組, 可能會存儲在隻讀內存中, 這就是造成它不一定能被修改。第二個聲明把p 初始化成指向無名數組的第一個元素。為了編譯舊代碼, 有的編譯器有一個控製字符串是否可寫的開關。

54:對於沒有初始化的變量的初始值可以作怎樣的假定?如果一個全局變量初始值為“零”, 它可否作為空指針或浮點零?

對於具有“靜態” 生存期的未初始化全局變量可以確保初始值為零,如果是指針會被初始化為正確的空指針, 如果是浮點數會被初始化為0.0 。
對於局部變量,如果沒有顯示地初始化, 則包含的是垃圾內容。
用malloc() 和realloc() 動態分配的內存也可能包含垃圾數據, 因此必須由調用者正確地初始化。

55:函數指針的定義是什麼?

是一個指向函數的指針。看例子:
A),char * (*fun1)(char * p1,char * p2);//fun1 不是函數名,而是一個指針變量,它指向一個函數。這個函數有兩個指針類型的參數,函數的返回值也是一個指針。
B),char * *fun2(char * p1,char * p2);//是個二級指針
C),char * fun3(char * p1,char * p2);//函數的返回值為char *類型

56:int *p = NULL 和*p = NULL 有什麼區別?

int *p = NULL;//定義一個指針變量p,其指向的內存裏麵保存的是int 類型的數據;在定義變量p 的同時把p 的值設置為0×00000000,而不是把*p 的值設置為0×00000000

int *p;
*p = NULL;
給*p 賦值為NULL,即給p指向的內存賦值為NULL;但是由於p 指向的內存可能是非法的,所以調試的時候編譯器可
能會報告一個內存訪問錯誤。

int i = 10;
int *p = &i;
*p = NULL;
在編譯器上調試一下,我們發現p 指向的內存由原來的10 變為0 了;而p 本身的值, 即內存地址並沒有改變。

57:介紹一下#error 預處理

#error 預處理指令的作用是,編譯程序時,隻要遇到#error 就會生成一個編譯錯誤提示消息,並停止編譯。其語法格式為:
#error error-message
注意,宏串error-message 不用雙引號包圍。遇到#error 指令時,錯誤信息被顯示,可能同時還顯示編譯程序作者預先定義的其他內容。

58:用變量a給出下麵的定義

a) int a; //一個整型數

b) int *a; //一個指向整型數的指針

c) int **a; //一個指向指針的的指針,它指向的指針是指向一個整型數

d) int a[10]; //一個有10個整型數的數組

e) int *a[10]; //一個有10個指針的數組,該指針是指向一個整型數的

f) int (*a)[10]; //一個指向有10個整型數數組的指針

g) int (*a)(int); //一個指向函數的指針,該函數有一個整型參數並返回一個整型數

h) int (*a[10])(int); // 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數

59:分別給出BOOL,int,float,指針變量 與“零值”比較的 if 語句(假設變量名為var)

BOOL型變量:if(!var)

int型變量: if(var==0)

float型變量:

const float EPSINON = 0.00001;

if ((x >= – EPSINON) && (x <= EPSINON)

指針變量:  if(var==NULL)

60:什麼是預編譯?何時需要預編譯?預編譯又稱為預處理 , 是做些代碼文本的替換工作。處理 # 開頭的指令 , 比如拷貝 #include 包含的文件代碼, #define 宏定義的替換 , 條件編譯等。
c 編譯係統在對程序進行通常的編譯之前,先進行預處理。 c 提供的預處理功能主要有以下三 種: 1 )宏定義  2 )文件包含  3 )條件編譯


61:內聯函數與宏有什麼區別

內聯函數在編譯時展開,宏在預編譯時展開
在編譯的時候內聯函數可以直接被嵌入到目標代碼中,而宏隻是一個簡單的文本替換
內聯函數可以完成諸如類型檢測、語句是否正確等編譯功能,宏就不具備這樣的功能
inline函數是函數,宏不是函數。
62:iostream與iostream.h的區別#include <iostream.h>非標準輸入輸出流
#include <iostream>標準輸入輸出流
有“.h”的就是非標準的,C的標準庫函數,無“.h”的,就要用到命令空間,是C++的。63:namespace的使用因為標準庫非常的龐大,所程序員在選擇的類的名稱或函數名時就很有可能和標準庫中的某個名字相同。所以為了避免這種情況所造成的名字衝突,就把標準庫中的一切都被放在名字空間std中。
C++標準程序庫中的所有標識符都被定義於一個名為std的namespace中。
1、直接指定標識符。例如std::ostream
2、使用using關鍵字。
using std::cout;
using std::endl;
3、最方便的就是使用using namespace std;
64:堆與棧的區別(1)一個是靜態的,一個是動態的,堆是靜態的,由用戶申請和釋放,棧是動態的,保存程序的局部變量(2)申請後係統的響應不同
棧:隻要棧的剩餘空間大於申請空間,係統就為程序提供內存,否則將拋出棧溢出異常
堆:當係統收到程序申請時,先遍曆操作係統中記錄空閑內存地址的鏈表,尋找第一個大於所申請空間的堆結點,然後將該結點從空間結點鏈表中刪除,並將該結點的空間分配給程序。
(3)申請大小限製的不同
棧:在windows下,棧的大小一般是2M,如果申請的空間超過棧的剩餘空間時,將提示overflow。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於係統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍曆方向是由低地址向高地址。堆的大小受限於計算機係統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。

65:含參數的宏與函數的優缺點
宏: 
優點:在預處理階段完成,不占用編譯時間,同時,省去了函數調用的開銷,運行效率高
缺點:不進行類型檢查,多次宏替換會導致代碼體積變大,而且由於宏本質上是字符串替換,故可能會由於一些參數的副作用導致得出錯誤的結果。
函數:優點:有類型檢查,比較安全。缺點:函數調用需要參數、返回地址等的入棧、出棧開銷,效率沒有帶參數宏高
宏與內聯函數的區別
內聯函數和宏都是在程序出現的地方展開,內聯函數不是通過函數調用實現的,是在調用該函數的程序處將它展開(在編譯期間完成的);宏同樣是;
不同的是:內聯函數可以在編譯期間完成諸如類型檢測,語句是否正確等編譯功能;宏就不具有這樣的功能,而且宏展開的時間和內聯函數也是不同的(在運行期間展開)

66:多態的作用?
(1)可以隱藏實現的細節,使得代碼模塊化;方便擴展代碼;

(2)可以實現接口重用。

67: 類的靜態成員和非靜態成員有何區別?
靜態成員則是屬於這個類的
非靜態成員是屬於每個對象的

68:C++純虛函數,虛函數,虛函數的實現,什麼是虛指針?

純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。
virtual void f()=0;//是一個接口,子類必須實現這個接口
虛指針或虛函數指針是虛函數的實現細節。帶有虛函數的每一個對象都有一個虛指針指向該類的虛函數表。虛函數 :虛函數是在基類中被聲明為virtual,並在派生類中重新定義的成員函數,可實現成員函數的動態覆蓋(Override)
純虛函數和虛函數的區別是,純虛函數子類必須實現。
純虛函數的優點:(1)可以實現多態特性
(2)定義一個標準的接口,
在派生類中必須予以重寫以實現多態性。

抽象類 :包含純虛函數的類稱為抽象類。由於抽象類包含了沒有定義的純虛函數,所以不能定義抽象類的對象。

多態性可分為兩類:靜態多態和動態多態。函數重載和運算符重載實現的多態屬於靜態多態,動態多態性是通過虛函數實現的。

虛函數與構造函數,析構函數,成員函數的關係

為什麼基類析構函數是虛函數?

編譯器總是根據類型來調用類成員函數。但是一個派生類的指針可以安全地轉化為一個基類的指針。這樣刪除一個基類的指針的時候,C++不管這個指針指向一個基類對象還是一個派生類的對象,調用的都是基類的析構函數而不是派生類的。如果你依賴於派生類的析構函數的代碼來釋放資源,而沒有重載析構函數,那麼會有資源泄漏。

為什麼構造函數不能為虛函數

虛函數采用一種虛調用的方法。需調用是一種可以在隻有部分信息的情況下工作的機製。如果創建一個對象,則需要知道對象的準確類型,因此構造函數不能為虛函數。

如果虛函數是有效的,那為什麼不把所有函數設為虛函數?

不行。因為每個虛函數的對象都要維護一個虛函數表,因此在使用虛函數的時候都會產生一定的係統開銷,這是沒有必要的。

69:麵向對象的三個基本特征,並簡單敘述之?
1. 封裝:將客觀事物抽象成類,每個類對自身的數據和方法。封裝可以使得代碼模塊化,目的是為了代碼重用
2. 繼承:子類繼承父類的方法和屬性,繼承可以擴展已存在的代碼,目的是為了代碼重用
3. 多態:允許將子類類型的指針賦值給父類類型的指針。

70:什麼是“&(引用)”?申明和使用“引用”要注意哪些問題?

引用就是某個目標變量的“別名”。注意事項:(1)申明一個引用的時候,必須要對其進行初始化。(2)初始化後,該引用名不能再作為其他變量名的別名。(3)引用本身不占存儲單元,係統不給引用分配存儲單元。(4)返回引用時,在內存中不產生被返回值的副本

(5)不能返回局部變量的引用。主要原因是局部變量會在函數返回後被銷毀.

71:引用與多態的關係?
引用就是對象的別名。引用主要用作函數的形參。引用必須用與該引用同類型的對象初始化: 引用是除指針外另一個可以產生多態效果的手段。這意味著,一個基類的引用可以指向它的派生類實例。
int ival = 1024;int &refVal = ival;
const 對象的引用隻能是const類型的:const int ival = 1024;const int &refVal = ival;
多態是通過虛函數實現的。

72:指針和引用有什麼區別;為什麼傳引用比傳指針安全?如果我使用常量指針難道不行嗎?
(1) 引用在創建的同時必須初始化,保證引用的對象是有效的,所以不存在NULL引用;而指針在定義的時候不必初始化,所以,指針則可以是NULL,可以在定義後麵的任何地方重新賦值。
(2) 引用一旦被初始化為指向一個對象,它就不能被改變為另一個對象的引用;而指針在任何時候都可以改變為指向另一個對象.
(3) 引用的創建和銷毀並不會調用類的拷貝構造函數

因為不存在空引用,並且引用一旦被初始化為指向一個對象,它就不能被改變為另一個對象的引用,所以比指針安全。
由於const 指針仍然存在空指針,並且有可能產生野指針,所以還是不安全

73:參數傳遞有幾種方式;實現多態參數傳遞采用什麼方式,如果沒有使用某種方式原因是什麼?
傳值,傳指針或者引用

74:拷貝構造函數相關問題,深拷貝,淺拷貝,臨時對象等。
深拷貝意味著拷貝了資源和指針,而淺拷貝隻是拷貝了指針,沒有拷貝資源
這樣使得兩個指針指向同一份資源,可能造成對同一份析構兩次,程序崩潰。而且浪費時間,並且不安全。
臨時對象的開銷比局部對象小些。

75:構造函數的特點

構造函數隻在建立對象的時候自動被調用一次
構造函數必須是公共的,否則無法生成對象
構造函數隻負責為自己的類構造對象

在構造函數中初始化變量
Person::Person( ) : name(“Jack”), age(30)
{

}

76:麵向對象如何實現數據隱藏

定義類來實現數據隱藏:

成員函數和屬性的類型:

私有成員private
保護成員protected
公共成員public

77:字符指針、浮點數指針、以及函數指針這三種類型的變量哪個占用的內存最大?為什麼?

所有指針變量占用內存單元的數量都是相同的。

78:C++是不是類型安全的?

不是。兩個不同類型的指針之間可以強製轉換.

79:const char*, char const*, char *const的區別是什麼?

把一個聲明從右向左讀,* 讀成指向
char  * const cp;//cp是常指針,指向char類型的數據
const char * cp;//cp是char類型的指針,指向const char
char const * p;//C++裏麵沒有const*的運算符,所以const屬於前麵的類型。

80:什麼是模板和宏?模板怎麼實現?模板有什麼缺點和優點?模版特化的概念,為什麼特化?

標準庫大量采用了模板技術。比如容器。

模板是一個藍圖,它本身不是類或函數。編譯器用模板產生指定的類或函數的特定類型版本。模版的形參分為類型形參非類型形參類型形參就是表示類型的形參,跟在關鍵字typename後非類型形參用來表示常量表達式

81:空指針和懸垂指針的區別?

空指針是指被賦值為NULL的指針;delete指向動態分配對象的指針將會產生懸垂指針。

空指針可以被多次delete,而懸垂指針再次刪除時程序會變得非常不穩定;

使用空指針和懸垂指針都是非法的,而且有可能造成程序崩潰,如果指針是空指針,盡管同樣是崩潰,但和懸垂指針相比是一種可預料的崩潰。

(a)指針數組和數組指針,函數指針和指針函數相關概念

指針數組:用於存儲指針的數組

int* a[4]          

元素表示:*a[i]

數組指針:指向數組的指針

int (*a)[4]

元素表示:(*a)[i]  

指針函數:函數返回類型是某一類型的指針,int *f(xy);

指針函數與函數指針表示方法的不同。最簡單的辨別方式就是看函數名前麵的指針*號有沒有被括號()包含,如果被包含就是函數指針,反之則是指針函數。

函數指針:是指向函數的指針變量,即本質是一個指針變量。

int (*f) (int x); /* 聲明一個函數指針 */ 類型說明符 (*指針的變量名)(參數)

f=func; /* func函數的首地址賦給指針f */

指向函數的指針包含了函數的地址

指針的指針:
例如:char ** cp;
如果有三個星號,那就是指針的指針的指針,依次類推。
指針的指針需要用到指針的地址。
        char c='A';
        char *p=&c;
        char **cp=&p;
通過指針的指針,不僅可以訪問它指向的指針,還可以訪問它指向的指針所指向的數據:
        char *p1=*cp;
        char c1=**cp;

指向指針數組的指針:
char *Names[]={ Bill,Sam,0};
char **nm=Names;
while(*nm!=0) printf(%s\n,*nm++);
先用字符型指針數組Names的地址來初始化指針nm。每次printf()的調用都首先傳遞指針nm指向的字符型指針,然後對nm進行自增運算使其指向數組的下一個元素(還是指針)

82:什麼是智能指針?

當類中有指針成員時,一般有兩種方式來管理指針成員:

(1)每個類對象都保留一份指針指向的對象的拷貝;

(2)使用智能指針,從而實現指針指向的對象的共享。實質是使用計數器與對象相關聯,這樣做可以保證對象正確的刪除,避免垂懸指針。

最後更新:2017-11-27 20:03:57

  上一篇:go  從達標到卓越 —— API 設計之道
  下一篇:go  阿裏、騰訊、百度、華為、京東、搜狗和滴滴最新麵試題匯集