如何應對C?
C/C++經典面試題
1, 變量的聲明和定義有什么區別
為變量分配地址和存儲空間的稱為定義,不分配地址的稱為聲明。一個變量可以在多個地方聲明,但只能在一個地方定義。加入extern 修飾的是變量的聲明,說明此變量將在文件以外或在文件后面部分定義
說明:很多時候一個變量,只是聲明不分配內存空間,直到具體使用時才初始化,分配內存空間,如外部變量。
2, 寫出 bool int float 指針變量與零值比較的if語句
Bool型數據
If(flag)
If(!flag)
Int型數據:if(0!=flag)
If(0==flag)
指針型數據:if(NULL==flag)
If(NUJLL!=flag)
Float 型數據:
Define NORM 0.00001;if(flag>=-NORM && flag<=NORM)
注意:應特別注意在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有一下不同:new,delete是操作符,可以重載,只能在c++中使用
Malloc,free是函數,可以覆蓋,c,c++中都可以使用
New可以調用對象的構造函數,對應的delete調用相應的析構函數
Malloc僅僅分配內存,free僅僅回收內存,并不執行構造函數和析構函數
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嗎
8,a和&a有什么區別
請寫出以下代碼的打印結果,主要目的是考察a和&a的區別
#include<stdlo.h>
Void main()
{
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與memecpy的區別
三者主要有以下不同之處
(1) 操作對象不同:strcpy的兩個操作對象均為字符串,sprintf的操作對象可以是多種數據類型,目的操作對象是字符串,memcpy的兩個對象是兩個任意可操作的內存地址,并不限于何種數據類型。
(2) 執行功能不同:strcpy主要實現字符串變量間的拷貝,sprintf主要實現其他數據類型格式到字符串類型的轉化,memcpy主要是內存塊間的拷貝
(3) 執行效率不同:memcpy最高,sprintf最低
說明:strcpy,sprintf,memcpy都可以實現拷貝的功能,但是針對的對象不同,根據實際需求,來選擇合適的函數實現拷貝功能
11,設置地址為ox67a9的整形變量的值為oxaa66
Int *ptr;
Ptr=(int *)ox67a9;
*ptr=oxaa66;
說明:這道題就是強制類型轉換的典型例子,無論在什么平臺地址長度和整型數據的長度是一樣的,即一個整型數據可以強制轉換成地址指針類型,只要有意義即可。
12,面向對象的三大特征:面向對象的三大特征是封裝性,繼承性,多態性。//有待完善
13.C++的空類有哪些成員函數
(1)缺省的構造函數
(2)缺省的拷貝構造函數
(3)缺省的析構函數
(4)缺省的賦值運算符
(5)缺省取址運算符
(6)缺省取址運算符const
注意:有些書上只是簡單的介紹了前四個函數,沒有提及后面兩個函數,但后兩個函數也是空類的默認函數。另外西藥注意的是,只有當實際使用這些函數的時候,編譯器才會定義他們。
14,談談你對拷貝構造函數和賦值運算符的認識
拷貝構造函數和復制運算符重載有以下兩個不同之處:(1)拷貝構造函數生成新的類對象,而賦值運算不能
(2)由于拷貝 構造函數是直接構造一個新的類對象,所以在初始化這個對象之前不用檢驗源對象是否和新建對象相同,而賦值運算符則需要這個操作,另外賦值運算符中如果原來的對象中內存非配要先把內存釋放掉
注意:當類中 有指針類型的成員變量時,一定要重寫拷貝構造函數和賦值運算符,不要使用默認的。
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,怎么吧一個單鏈表反序
21,簡述隊列和棧的異同
隊列和棧都是線性存儲結構,但是兩者的插入和刪除數據的操作不同,隊列是先進先出,棧是后進先出
注意:區別棧區和堆區,堆區的存取是順序隨意,而棧區是后進先出,棧由編譯器自動分配,存放函數的參數值,局部變量的值等,其操作方式類似數據結構中的棧,堆一般由程序員分配釋放,若程序員不釋放,程序結束時可能由os回收,分配方式類似于鏈表
它與本題的的堆和棧是兩回事,堆棧只是一種數據結構,而堆區和棧區是程序中的不同內存存儲區域
22-28各種排序算法
29談談你對編程規范的理解或認識
編程規范可總結為:程序的可行性,可讀性,可移植性,以及可測試性
說明:這是編程規范的總綱目,面試者不一定要去背誦上面給出的那幾個例子,應該去理解這幾個例子說明的問題,想一想,自己如何解決可行性,可讀性,可移植性,以及可測試性這幾個問題,結合上幾個例子和自己平時的編程習慣來回答這個問題
30 short i=0;i=i+1L; 這兩句有錯嗎
代碼一是錯的,代碼二十正確的
說明:在數據安全的情況下,大類型的數據向小類型的數據轉換一定要顯示的強制類型轉換
31,&& 和& ,||和|有什么區別
(1)&和|對操作數進行求值運算,&&和||是判斷邏輯關系
(2)&&和|| 在判斷左操作數就能確定結果的情況下就不再對右操作數求值。
注意:在編程的時候有些時候將&& 或||替換成&和 |沒有出錯,但是其邏輯是錯誤的,可能導致不可預測的后果。
32C++的引用和c語言的指針有什么區別
指針和引用主要有一下區別:(1)引用必須被初始化,但是不分配存儲空間,指針在聲明時初始化,在初始化的時候需要分配存儲空間。
(2)引用初始化以后不能被改變,指針可以改變所指向的對象
(3)不存在指向空值的引用,但存在指向空值的指針
注意:引用作為函數的參數時,會引發一定的問題,因為讓引用做參數,目的就是想改變這個引用所指向地址的內容,而函數調用時傳入的是實參,看不出函數的參數是正常變量還是引用,因此可能會引發錯誤,所以使用時一定要小心。
33,適當的數據結構為更好的算法設計提供了有利的條件
35typedef和define有什么區別
(1) 用法不同:typedef用來定義一種數據類型的別名,增強程序的可讀性,define主要用來定義常量,以及書寫復雜使用頻繁的宏
(2) 執行時間不同,typedef是編譯過程的一部分,有類型檢查得功能,define是宏定義,是預編譯的步部分,起發生在編譯之前,只是簡單的進行字符串的替換,不進行類型的檢查
(3) 作用域不同:typedef有作用域限定,define不受作用域約束,只要define聲明后的引用都是正確的。
(4) 對指針的操作不同,typedef和define定義的指針時有很大的區別
(5) 注意:typedef定義是語句,因為句尾要加分號,而define不是語句,千萬不能在句尾加分號。
36關鍵字const是什么
Const用來定義一個只讀的變量或對象。主要優點:便于類型檢查,同宏定義一樣可以方便的進行參數的修改和調整,節省空間,避免不必要的內存分配、可為函數重載提供參數。
說明:const修飾函數參數,是一種編程規范的要求,便于閱讀,一看即知這個參數不能被改變,實現時不易出錯
37static有什么作用
Static在c中主要用于定義全局靜態變量,定義局部靜態變量,定義靜態函數,在c++中新增了兩個作用,定義靜態數據成員,定義靜態函數成員
注意:因為static定義的變量分配在靜態區,所以其定義的變量的默認值為0,普通的變量的默認值為隨機數,在定義指針變量時要特別注意。
Extern有什么作用
Extern標識的變量或函數聲明起定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找定義
39流操作符重載為什么返回引用
在程序中,流操作符>>和<<經常連續使用,因此這兩個操作符的返回值應該是一個仍舊支持這兩個操作符的流引用,其他的數據類型都無法做到這一點。
注意:出來在復制操作符和流操作符之外的其他的一些操作符中,如+-*/卻千萬不能返回引用,因為這死者操作符的對象都是右值,因此他們必須構造一個對象作為返回值。
40,簡述指針常量與常量指針區別
指針常量是指定義了一個指針,這個指針的值只能在定義時初始化,其他地方不能改變,常量指針是指定義了一個指針,這個指針指向一個只讀的對象,不能通過常量指針來改變這個對象的值。
指針常量強調的是指針的不可改變性,而常量指針強調的是指針對其所指對象的不可改變性
注意:無論是指針常量還是常量指針,其最大的用途就是作為函數的形式參數,保證實參在被調用函數中的不可改變性。
41數組和指針的區別,即字符數組和字符串指針的區別
42如何避免“野指針”
野指針產生原因及解決辦法如下:(1)指針變量聲明時沒有被初始化,解決辦法:指針聲明時初始化,可以是具體的地址值,也可以是讓他指向NULL
(2)指針p被free或者delete之后,沒有置為NULL。解決辦法:指針指向的內存空間被釋放后指針應該指向NULL
(3)指針操作超越了變量的作用范圍,解決辦法:在變量的作用域結束前釋放掉變量的地址空間,并讓指針指向NULL
注意:野指針的解決方法也是編程規范的基本原則,平時使用指針時一定要避免產生野指針,在使用指針前一定要檢驗指針的合法性
43常引用有什么作用
常引用的引入主要是為了避免使用變量的引用時,在不知道的情況下改變變量的值,常引用主要用于定義一個普通變量的只讀屬性的別名,作為函數的傳入參數,避免實參在調用函數中被意外的改變。
說明:很多情況下,需要用常引用做形參,被引用對象等效于常對象,不能在函數中改變實參的值,這樣的好處是有較高的易讀性和較小的出錯率。
49,構造函數能否為虛函數
構造函數不能是虛函數,而且不能在構造函數中調用虛函數,因為那樣實際執行的是父類的對應的函數,因為自己還沒有構造好,析構函數可以是虛函數,而且在一個復雜類結構中,這往往是必須的。析構函數也可以是純虛函數,但純虛函數必須有定義體,因為析構函數的調用是在子類中隱含的。說明:虛函數的動態綁定特性是 實現重載的關鍵技術,動態綁定根據實際的調用情況查詢相應類的虛函數表,調用相應的虛函數。
50談談你對面向對象的認識
面向對象可以理解成對待每一個問題,都是首先要確定這個問題由幾個部分組成,而每一個部分其實就是一個對象,然后再分別設計這些對象,最后得到整個程序,傳統的程序設計多是基于功能的思想來進行考慮和設計的,而面向對象的程序設計則是基于對象的角度來考慮問題的。這樣做能夠是得程序更加簡潔清晰。
說明:編程中接觸最多的面向對象編程技術僅僅是面向對象技術中的一個組成部分,發揮面向對象技術的優勢是一個綜合的技術問題,不僅僅需要面向對象的分析,設計和編程技術,而且需要借助必要的建模和開發工具。