14.什麼是內存泄漏?面對內存泄漏和指針越界,你有哪些方法?你通常採用哪些方法來避免和減少這類錯誤?
19.C++中有了malloc / free , 爲什麼還需要 new / delete?
21.在C++程序中調用被C編譯器編譯後的函數,爲什麼要加extern“C”?
22.頭文件中的 ifndef/define/endif 是幹什麼用的? 該用法和 program once 的區別?
24. 32位,64位系統中,各種常用內置數據類型佔用的字節數?
25.virtual, inline, decltype,volatile,static, const關鍵字的作用?使用場景?
1.c++與c區別
答:C 是面向過程的一門編程語言,C++ 可以很好地進行面向對象的程序設計,
C++ 對 C 的增強,表現在六個方面:
- 增強了類型檢查機制
- 增加了面向對象的機制
- 增加了泛型編程的機制(template)
- 增加了異常處理
- 增加了重載的機制
- 增加了標準模板庫
參考:https://www.jianshu.com/p/2522b07219ae
2.union共同體和struct結構體的區別
- struct和union都是由多個不同的數據類型成員組成
- 同一時刻,union只存放了一個數據成員,而struct的所有成員都存在
- 在struct中,每個成員都有自己的內存,它們同時存在,在union中,所有成員不能同時佔用各自的一塊內存,因此不能同時存在,只能存在一個
- struct的長度爲所有成員長度之和,union的長度爲最長成員的長度
- 對union不同成員賦值會重寫其他成員,而struct的不同成員之間的賦值互不影響
3. static的作用
- 修飾局部變量,改變了局部變量的存儲區(棧->靜態存儲區)以及生命週期(離開作用於之後,並未被銷燬,仍然駐留在內存,直到程序銷燬,但是我們不能再對它進行訪問),但未改變其作用域。
- 修飾全局變量和函數,並未改變其存儲位置及生命週期,而改變了其作用域,使當前文件外的源文件無法訪問該變量和該函數。
- 修飾類的成員變量,使其成爲類的全局變量,被類的所有對象共享。
- 修飾類的成員函數,所有對象共享該函數,函數屬於類的,不需要創建對象即可訪問;靜態成員函數只能訪問靜態成員變量,不可訪問非靜態成員變量。
4.const的作用
- 限定變量爲不可修改
- 限定成員函數不可以修改任何數據成員
5.指針和引用的區別
- 指針是變量,存放變量的地址,佔用4Byte或者8Byte,引用是別名,不佔用內存空間
- 指針可以爲空,可以聲明之後再初始化,引用必須在聲明的時候初始化,而且初始化之後不可再改變
- 指針可以有多級,引用不可以
6. 重載、重寫、隱藏的區別
Overload(重載):在C++程序中,可以將語義、功能相似的幾個函數用同一個名字表示,但是參數或者返回值不同
- 相同的範圍(同一個類中)
- 函數名相同
- 參數或者返回值不同
- virtual關鍵字可有可無
Override(重寫):派生類函數覆蓋基類函數,用於多態
- 不同的範圍(分別位於基類和派生類)
- 函數名相同
- 參數和返回值相同
- 必須有virtual關鍵字
Overwrite(隱藏),派生類函數屏蔽與其同名的基類函數規則如下
- 派生類與基類函數同名,但是參數不同,基類函數將被隱藏
- 派生類與基類函數同名,並且參數相同,但是基類函數沒有virtual關鍵字,基類函數將被隱藏
7.虛函數與虛函數表相關知識點
多態是由虛函數實現的,而虛函數主要是通過虛函數表(V-Table)來實現的。
- 類中若有virtual修飾的成員函數,該類就會有一個虛函數表
- 該類定義的對象會有一個虛函數指針指向類的虛函數表,原始基類的對象的虛函數指針指向基類的虛函數表
- 有純虛函數的類爲抽象類,不能被實例化。
8.C++中struct和class的區別
- 默認繼承權限
- 成員的默認訪問權限
參考:https://blog.csdn.net/alidada_blog/article/details/83419757
9數組指針和指針數組
- int (*ptr)[5],數組的指針相當於二維數組,也就是指針的指針
- int *ptr[5],指針數組是一個數組,數組存放的元素爲指針
10.strlen和sizeof的區別
- strlen是函數,在運行時才能計算,返回字符串的長度,必須以‘\0’結尾;sizeof是運算符,在編譯時就計算好了。
11.進程和線程
- 進程是程序指令和相關資源的集合,是程序的運行實體,一個進程可以包含多個線程
- 進程是資源分配的基本單位,線程是程序調度的基本單位,線程共享進程的資源(動態堆,靜態存儲區,代碼段),但是具有自己獨立的棧空間和寄存器
- 進程的頻繁切換需要包含進程資源的保護和恢復動作,因此會引起多額外的開銷進而影響系統的性能,另外進程間的通信要求複雜的系統級實現,線程的切換比較快,容易實現併發,而且可以通過共享資源的方式進行多線程之間的通信,但是需要考慮同步的問題。
12.C/C++內存有哪幾種類型?
C中,內存分爲5個區:堆(malloc)、棧(如局部變量、函數參數)、程序代碼區(存放二進制代 碼)、全局/靜態存儲區(全局變量、static變量)和常量存儲區(常量)。此外,C++中有自由存儲區(new)一說。全局變量、static變量會初始化爲零,而堆和棧上的變量是隨機的,不確定的。
13.堆和棧的區別?
1).堆存放動態分配的對象——即那些在程序運行時分配的對象,比如局部變量,其生存期由程序控制;
2).棧用來保存定義在函數內的非static對象,僅在其定義的程序塊運行時才存在;
3).靜態內存用來保存static對象,類static數據成員以及定義在任何函數外部的變量,static對象在使用之前分配,程序結束時銷燬;
4).棧和靜態內存的對象由編譯器自動創建和銷燬。
14.什麼是內存泄漏?面對內存泄漏和指針越界,你有哪些方法?你通常採用哪些方法來避免和減少這類錯誤?
用動態存儲分配函數動態開闢的空間,在使用完畢後未釋放,結果導致一直佔據該內存單元即爲內存泄露。
1). 使用的時候要記得指針的長度.
2). malloc的時候得確定在那裏free.
3). 對指針賦值的時候應該注意被賦值指針需要不需要釋放.
4). 動態分配內存的指針最好不要再次賦值.
5). 在C++中應該優先考慮使用智能指針.
15. 函數調用的過程?
int main(void) { ... d = fun(a, b, c); cout<<d<<endl; ... return 0; }
調用fun()的過程大致如下:
main()========
1).參數拷貝(壓棧),注意順序是從右到左,即c-b-a;
2).保存d = fun(a, b, c)的下一條指令,即cout<<d<<endl(實際上是這條語句對應的彙編指令的起始位置);
3).跳轉到fun()函數,注意,到目前爲止,這些都是在main()中進行的;
fun()=====
4).移動ebp、esp形成新的棧幀結構;
5).壓棧(push)形成臨時變量並執行相關操作;
6).return一個值;
7).出棧(pop);
8).恢復main函數的棧幀結構;
9).返回main函數;
main()========
16. 左值和右值
17. int fun() 和 int fun(void)的區別?
這裏考察的是c 中的默認類型機制。
在c中,int fun() 會解讀爲返回值爲int(即使前面沒有int,也是如此,但是在c++中如果沒有返回類型將報錯),輸入類型和個數沒有限制, 而int fun(void)則限制輸入類型爲一個void。
在c++下,這兩種情況都會解讀爲返回int類型,輸入void類型。
18. 宏和內聯(inline)函數的比較?
1). 首先宏是C中引入的一種預處理功能;
2). 內聯(inline)函數是C++中引用的一個新的關鍵字;C++中推薦使用內聯函數來替代宏代碼片段;
3). 內聯函數將函數體直接擴展到調用內聯函數的地方,這樣減少了參數壓棧,跳轉,返回等過程;
4). 由於內聯發生在編譯階段,所以內聯相較宏,是有參數檢查和返回值檢查的,因此使用起來更爲安全;
5). 需要注意的是, inline會向編譯期提出內聯請求,但是是否內聯由編譯期決定(當然可以通過設置編譯器,強制使用內聯);
6). 由於內聯是一種優化方式,在某些情況下,即使沒有顯示的聲明內聯,比如定義在class內部的方法,編譯器也可能將其作爲內聯函數。
7). 內聯函數不能過於複雜,最初C++限定不能有任何形式的循環,不能有過多的條件判斷,不能對函數進行取地址操作等,但是現在的編譯器幾乎沒有什麼限制,基本都可以實現內聯。
19.C++中有了malloc / free , 爲什麼還需要 new / delete?
1). malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。
2). 對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。
由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數。
最後補充一點體外話,new 在申請內存的時候就可以初始化(如下代碼), 而malloc是不允許的。另外,由於malloc是庫函數,需要相應的庫支持,因此某些簡易的平臺可能不支持,但是new就沒有這個問題了,因爲new是C++語言所自帶的運算符。
補充:
- new是運算符,malloc()是一個庫函數;
- new會調用構造函數,malloc不會;
- new返回指定類型指針,malloc返回void*指針,需要強制類型轉換;
- new會自動計算需分配的空間,malloc不行;
- new可以被重載,malloc不能。
20.強制類型轉化
1).static_cast
a. 用於基本類型間的轉換
b. 不能用於基本類型指針間的轉換
c. 用於有繼承關係類對象間的轉換和類指針間的轉換
2). dynamic_cast
a. 用於有繼承關係的類指針間的轉換
b. 用於有交叉關係的類指針間的轉換
c. 具有類型檢查的功能
d. 需要虛函數的支持
3). reinterpret_cast
a. 用於指針間的類型轉換
b. 用於整數和指針間的類型轉換
4). const_cast
a. 用於去掉變量的const屬性
b. 轉換的目標類型必須是指針或者引用
21.在C++程序中調用被C編譯器編譯後的函數,爲什麼要加extern“C”?
C++語言支持函數重載,C語言不支持函數重載,函數被C++編譯器編譯後在庫中的名字與C語言的不同,假設某個函數原型爲:
void foo(int x, int y);
1
該函數被C編譯器編譯後在庫中的名字爲 _foo, 而C++編譯器則會產生像: _foo_int_int 之類的名字。爲了解決此類名字匹配的問題,C++提供了C鏈接交換指定符號 extern “C”。
22.頭文件中的 ifndef/define/endif 是幹什麼用的? 該用法和 program once 的區別?
相同點:
它們的作用是防止頭文件被重複包含。
不同點
1). ifndef 由語言本身提供支持,但是 program once 一般由編譯器提供支持,也就是說,有可能出現編譯器不支持的情況(主要是比較老的編譯器)。
2). 通常運行速度上 ifndef 一般慢於 program once,特別是在大型項目上, 區別會比較明顯,所以越來越多的編譯器開始支持 program once。
3). ifndef 作用於某一段被包含(define 和 endif 之間)的代碼, 而 program once 則是針對包含該語句的文件, 這也是爲什麼 program once 速度更快的原因。
4). 如果用 ifndef 包含某一段宏定義,當這個宏名字出現“撞車”時,可能會出現這個宏在程序中提示宏未定義的情況(在編寫大型程序時特性需要注意,因爲有很多程序員在同時寫代碼)。相反由於program once 針對整個文件, 因此它不存在宏名字“撞車”的情況, 但是如果某個頭文件被多次拷貝,program once 無法保證不被多次包含,因爲program once 是從物理上判斷是不是同一個頭文件,而不是從內容上。
23.說幾個C++11的新特性
- auto類型推導
- 範圍for循環
- lambda函數 https://www.cnblogs.com/zhoug2020/p/6795761.html
- override 和 final 關鍵字
24. 32位,64位系統中,各種常用內置數據類型佔用的字節數?
char :1個字節(固定)
*(即指針變量): 4個字節(32位機的尋址空間是4個字節。同理64位編譯器)(變化*)
short int : 2個字節(固定)
int: 4個字節(固定)
unsigned int : 4個字節(固定)
float: 4個字節(固定)
double: 8個字節(固定)
long: 4個字節
unsigned long: 4個字節(變化*,其實就是尋址控件的地址長度數值)
long long: 8個字節(固定)
64位操作系統
char :1個字節(固定)
*(即指針變量): 8個字節
short int : 2個字節(固定)
int: 4個字節(固定)
unsigned int : 4個字節(固定)
float: 4個字節(固定)
double: 8個字節(固定)
long: 8個字節
unsigned long: 8個字節(變化*其實就是尋址控件的地址長度數值)
long long: 8個字節(固定)
除*與long 不同其餘均相同。
25.virtual, inline, decltype,volatile,static, const關鍵字的作用?使用場景?
inline:在c/c++中,爲了解決一些頻繁調用的小函數大量消耗棧空間(棧內存)的問題,特別的引入了inline修飾符,表示爲內聯函數。
decltype:從表達式中推斷出要定義變量的類型,但卻不想用表達式的值去初始化變量。還有可能是函數的返回類型爲某表達式的的值類型。
volatile:volatile 關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如:操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。
static:
- 隱藏
在變量和函數名前面如果未加static,則它們是全局可見的。加了static,就會對其它源文件隱藏,利用這一特性可以在不同的文件中定義同名函數和同名變量,而不必擔心命名衝 突。static可以用作函數和變量的前綴,對於函數來講,static的作用僅限於隱藏 。
2.static變量中的記憶功能和全局生存期
26.深拷貝與淺拷貝的區別?
1.什麼時候用到拷貝函數?
a.一個對象以值傳遞的方式傳入函數體;
b.一個對象以值傳遞的方式從函數返回;
c.一個對象需要通過另外一個對象進行初始化。
如果在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器將會自動生成一個默認的拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝;
2.是否應該自定義拷貝函數?
自定義拷貝構造函數是一種良好的編程風格,它可以阻止編譯器形成默認的拷貝構造函數,提高源碼效率。
3.什麼叫深拷貝?什麼是淺拷貝?兩者異同?
如果一個類擁有資源,當這個類的對象發生複製過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。
4.深拷貝好還是淺拷貝好?
如果實行位拷貝,也就是把對象裏的值完全複製給另一個對象,如A=B。這時,如果B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。
27.計算類大小例子
class A {};: sizeof(A) = 1;
class A { virtual Fun(){} };: sizeof(A) = 4(32位機器)/8(64位機器);
class A { static int a; };: sizeof(A) = 1;
class A { int a; };: sizeof(A) = 4;
class A { static int a; int b; };: sizeof(A) = 4;
28.大端與小端的概念?各自的優勢是什麼?
- 大端與小端是用來描述多字節數據在內存中的存放順序,即字節序。大端(Big Endian)指低地址端存放高位字節,小端(Little Endian)是指低地址端存放低位字節。
- 需要記住計算機是以字節爲存儲單位。
- 爲了方便記憶可把大端和小端稱作高尾端和低尾端,eg:如果是高尾端模式一個字符串“11223344”把尾部“44”放在地址的高位,如果是地尾端模式,把“44”放在地址的低位。
各自優勢:
- Big Endian:符號位的判定固定爲第一個字節,容易判斷正負。
- Little Endian:長度爲1,2,4字節的數,排列方式都是一樣的,數據類型轉換非常方便。
舉一個例子,比如數字0x12 34 56 78在內存中的表示形式爲:
- 1)大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
- 2)小端模式:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
29.C/C++中堆和棧的區別
講解全面的一篇博客:https://blog.csdn.net/Fiorna0314/article/details/49757195
30.指針常量與常量指針
常量指針(被指向的對象是常量)
定義:又叫常指針,可以理解爲常量的指針,指向的是個常量
關鍵點:
- 常量指針指向的對象不能通過這個指針來修改,可是仍然可以通過原來的聲明修改;
- 常量指針可以被賦值爲變量的地址,之所以叫常量指針,是限制了通過這個指針修改變量的值;
- 指針還可以指向別處,因爲指針本身只是個變量,可以指向任意地址;
const int *p或int const *p
(記憶技巧:const讀作常量,*讀作指針)
#include <stdio.h> // 常量指針(被指向的對象是常量) int main() { int i = 10; int i2 = 11; const int *p = &i; printf("%d\n", *p);//10 i = 9; //OK,仍然可以通過原來的聲明修改值, //Error,*p是const int的,不可修改,即常量指針不可修改其指向地址 //*p = 11; //error: assignment of read-only location ‘*p’ p = &i2;//OK,指針還可以指向別處,因爲指針只是個變量,可以隨意指向; printf("%d\n", *p);//11 return 0; }
指針常量(指針本身是常量)
定義:
本質是一個常量,而用指針修飾它。指針常量的值是指針,這個值因爲是常量,所以不能被賦值。
關鍵點:
- 它是個常量!
- 指針所保存的地址可以改變,然而指針所指向的值卻不可以改變;
- 指針本身是常量,指向的地址不可以變化,但是指向的地址所對應的內容可以變化;
int* const p;
//指針常量(指針本身是常量) #include <stdio.h> int main() { int i = 10; int *const p = &i; printf("%d\n", *p);//10 //Error,因爲p是const 指針,因此不能改變p指向的內容 //p++;//error: increment of read-only variable ‘p’ (*p)++; //OK,指針是常量,指向的地址不可以變化,但是指向的地址所對應的內容可以變化 printf("%d\n", *p);//11 i = 9;//OK,仍然可以通過原來的聲明修改值, return 0; }
31、函數模板與類模板有什麼區別?
【參考答案】
函數模板的實例化是由編譯程序在處理函數調用時自動完成的,而類模板的實例化必須由程序員在程序中顯式地指定。
32.智能指針
shared_ptr unique_ptr,weak_ptr,auto_ptr
https://blog.csdn.net/flowing_wind/article/details/81301001
https://blog.csdn.net/k346k346/article/details/81478223#1unique_ptr_4
參考:
https://blog.csdn.net/kuweicai/article/details/82779648
https://zhuanlan.zhihu.com/p/51918989