二十万字C/C++、嵌入式软开面试题全集宝典十
目錄
1、 編碼實現某一變量某位清0或置1
2、 分別寫出BOOL,int,float,指針類型的變量a 與“零”的比較語句。
3、 局部變量全局變量的問題?
4、 數組和指針的區別?
5、 C++如何阻止一個類被實例化?一般在什么時候將構造函數聲明為private?
6、 如何禁止自動生成拷貝構造函數?
7、 assert與NDEBUGE
8、 Debug和release的區別
9、 main函數有沒有返回值
10、 寫一個比較大小的模板函數
11、 c++怎么實現一個函數先于main函數運行
12、 成員函數里memset(this,0,sizeof(*this))會發生什么
13、 方法調用的原理(棧,匯編)
14、 MFC消息處理如何封裝的?
15、 函數指針
16、 回調函數的作用
17、 隨機數的生成
18、 for(;;)和while(true)的區別
19、 數據結構中的常見樹結構
20、 什么是平衡二叉樹
?
1、 編碼實現某一變量某位清0或置1
#define BIT3 (0x1 << 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位清零 }2、 分別寫出BOOL,int,float,指針類型的變量a 與“零”的比較語句。
BOOL : if ( !a ) or if(a) int : if ( a == 0) float : const EXPRESSION EXP = 0.000001 if ( a < EXP && a >-EXP) pointer : if ( a != NULL) or if(a == NULL)無論是float還是double類型的變量,都有精度限制。所以一定要避免將浮點變量用“==”或“!=”與數字比較,應該設法轉化成“>=”或“<=”形式。
3、 局部變量全局變量的問題?
1.局部會屏蔽全局。要用全局變量,需要使用"::"局部變量可以與全局變量同名,在函數內引用這個變量時,會用到同名的局部變量,而不會用到全局變量。對于有些編譯器而言,在同一個函數內可以定義多個同名的局部變量,比如在兩個循環體內都定義一個同名的局部變量,而那個局部變量的作用域就在那個循環體內。
2.如何引用一個已經定義過的全局變量,可以用引用頭文件的方式,也可以用extern關鍵字,如果用引用頭文件方式來引用某個在頭文件中聲明的全局變理,假定你將那個變寫錯了,那么在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那么在編譯期間不會報錯,而在連接期間報錯。
3.全局變量可不可以定義在可被多個.C文件包含的頭文件中,在不同的C文件中以static形式來聲明同名全局變量。可以在不同的C文件中聲明同名的全局變量,前提是其中只能有一個C文件中對此變量賦初值,此時連接不會出錯。
4、 數組和指針的區別?
1、數組在內存中是連續存放的,開辟一塊連續的內存空間;數組所占存儲空間:sizeof(數組名);數組大小:sizeof(數組名)/sizeof(數組元素數據類型);
2、用運算符sizeof 可以計算出數組的容量(字節數)。sizeof(p),p?為指針得到的是一個指針變量的字節數,而不是p所指的內存容量。
3、編譯器為了簡化對數組的支持,實際上是利用指針實現了對數組的支持。具體來說,就是將表達式中的數組元素引用轉換為指針加偏移量的引用。
4、在向函數傳遞參數的時候,如果實參是一個數組,那用于接受的形參為對應的指針。也就是傳遞過去是數組的首地址而不是整個數組,能夠提高效率;
5、在使用下標的時候,兩者的用法相同,都是原地址加上下標值,不過數組的原地址就是數組首元素的地址是固定的,指針的原地址就不是固定的。
5、 C++如何阻止一個類被實例化?一般在什么時候將構造函數聲明為private?
1、將類定義為抽象基類或者將構造函數聲明為private;(lsy注:考慮下單例模式的實現)
2、不允許類外部創建類對象,只能在類內部創建對象
6、 如何禁止自動生成拷貝構造函數?
1.為了阻止編譯器默認生成拷貝構造函數和拷貝賦值函數,我們需要手動去重寫這兩個函數,某些情況下,為了避免調用拷貝構造函數和拷貝賦值函數,我們需要將他們設置成private,防止被調用。
2.類的成員函數和friend函數還是可以調用private函數,如果這個private函數只聲明不定義,則會產生一個連接錯誤;
3.針對上述兩種情況,我們可以定一個base類,在base類中將拷貝構造函數和拷貝賦值函數設置成private,那么派生類中編譯器將不會自動生成這兩個函數,且由于base類中該函數是私有的,因此,派生類將阻止編譯器執行相關的操作。
7、 assert與NDEBUGE
1.assert宏的原型定義在<assert.h>中,其作用是如果它的條件返回錯誤,則終止程序執行,原型定義:
#include <assert.h> void assert( int expression );assert的作用是現計算表達式 expression ,如果其值為假(即為0),那么它先向stderr打印一條出錯信息,然后通過調用?abort?來終止程序運行。如果表達式為真,assert什么也不做。
2.NDEBUG宏是Standard C中定義的宏,專門用來控制assert()的行為。如果定義了這個宏,則assert不會起作用。定義NDEBUG能避免檢查各種條件所需的運行時開銷,當然此時根本就不會執行運行時檢查。
3.C Standard中規定了assert以宏來實現。<assert.h>被設計來可以被多次包含,其中一上來就undef assert,然后由NDEBUG宏來決定其行為。
8、 Debug和release的區別
1.調試版本,包含調試信息,所以容量比Release大很多,并且不進行任何優化(優化會使調試復雜化,因為源代碼和生成的指令間關系會更復雜),便于程序員調試。Debug模式下生成兩個文件,除了.exe或.dll文件外,還有一個.pdb文件,該文件記錄了代碼中斷點等調試信息;?
2.發布版本,不對源代碼進行調試,編譯時對應用程序的速度進行優化,使得程序在代碼大小和運行速度上都是最優的。(調試信息可在單獨的PDB文件中生成)。Release模式下生成一個文件.exe或.dll文件。
3.實際上,Debug 和 Release 并沒有本質的界限,他們只是一組編譯選項的集合,編譯器只是按照預定的選項行動。事實上,我們甚至可以修改這些選項,從而得到優化過的調試版本或是帶跟蹤語句的發布版本。
9、 main函數有沒有返回值
1.程序運行過程入口點main函數,main()函數返回值類型必須是int,這樣返回值才能傳遞給程序激活者(如操作系統)表示程序正常退出。main(int args, char **argv) 參數的傳遞。參數的處理,一般會調用getopt()函數處理,但實踐中,這僅僅是一部分,不會經常用到的技能點。
10、 寫一個比較大小的模板函數
#include<iostream> using?namespace?std; template<typename?type1,typename?type2>//函數模板 type1 Max(type1 a,type2 b) { return?a > b ??a : b; } void?main() { cout<<"Max = "<<Max(5.5,'a')<<endl; }11、 c++怎么實現一個函數先于main函數運行
1.如果在main函數之前聲明一個類的全局的對象。那么其執行順序,根據全局對象的生存期和作用域,肯定先于main函數。
class simpleClass?{public:simpleClass( );{?cout << "simpleClass constructor.." << endl;???????? }};simpleClass g_objectSimple;?????????//step1全局對象?int _tmain(int?argc, _TCHAR* argv[])??//step3?{?return 0;?}2.定義在main( )函數之前的全局對象、靜態對象的構造函數在main( )函數之前執行。
3.main函數執行之前,主要就是初始化系統相關資源;
1設置棧指針
2初始化static靜態和global全局變量,即data段的內容
3將未初始化部分的全局變量賦初值:數值型short,int,long等為0,bool為FALSE,指針為NULL,等等,即.bss段的內容
4全局對象初始化,在main之前調用構造函數
5將main函數的參數,argc,argv等傳遞給main函數,然后才真正運行main函數
4.main函數執行之后
1全局對象的析構函數會在main函數之后執行;
2可以用_onexit 注冊一個函數,它會在main 之后執行;
12、 成員函數里memset(this,0,sizeof(*this))會發生什么
1.有時候類里面定義了很多int,char,struct等c語言里的那些類型的變量,我習慣在構造函數中將它們初始化為0,但是一句句的寫太麻煩,所以直接就memset(this, 0, sizeof *this);將整個對象的內存全部置為0。對于這種情形可以很好的工作,但是下面幾種情形是不可以這么使用的;
2.類含有虛函數表:這么做會破壞虛函數表,后續對虛函數的調用都將出現異常;
3.類中含有C++類型的對象:例如,類中定義了一個list的對象,由于在構造函數體的代碼執行之前就對list對象完成了初始化,假設list在它的構造函數里分配了內存,那么我們這么一做就破壞了list對象的內存。
13、 方法調用的原理(棧,匯編)
1.機器用棧來傳遞過程參數、存儲返回信息、保存寄存器用于以后恢復,以及本地存儲。而為單個過程分配的那部分棧稱為幀棧;幀棧可以認為是程序棧的一段,它有兩個端點,一個標識起始地址,一個標識著結束地址,兩個指針結束地址指針esp,開始地址指針ebp;
2.由一系列棧幀構成,這些棧幀對應一個過程,而且每一個棧指針+4的位置存儲函數返回地址;每一個棧幀都建立在調用者的下方,當被調用者執行完畢時,這一段棧幀會被釋放。由于棧幀是向地址遞減的方向延伸,因此如果我們將棧指針減去一定的值,就相當于給棧幀分配了一定空間的內存。如果將棧指針加上一定的值,也就是向上移動,那么就相當于壓縮了棧幀的長度,也就是說內存被釋放了。
3.過程實現
1備份原來的幀指針,調整當前的棧幀指針到棧指針位置;
2建立起來的棧幀就是為被調用者準備的,當被調用者使用棧幀時,需要給臨時變量分配預留內存;
3使用建立好的棧幀,比如讀取和寫入,一般使用mov,push以及pop指令等等。
4恢復被調用者寄存器當中的值,這一過程其實是從棧幀中將備份的值再恢復到寄存器,不過此時這些值可能已經不在棧頂了
5恢復被調用者寄存器當中的值,這一過程其實是從棧幀中將備份的值再恢復到寄存器,不過此時這些值可能已經不在棧頂了。
6釋放被調用者的棧幀,釋放就意味著將棧指針加大,而具體的做法一般是直接將棧指針指向幀指針,因此會采用類似下面的匯編代碼處理。
7恢復調用者的棧幀,恢復其實就是調整棧幀兩端,使得當前棧幀的區域又回到了原始的位置。
8彈出返回地址,跳出當前過程,繼續執行調用者的代碼。
4.過程調用和返回指令
1call指令
2leave指令
3ret指令
14、 MFC消息處理如何封裝的?
15、 函數指針
從定義和?途兩??來說?下??的理解:
?先是定義:函數指針是指向函數的指針變量。函數指針本身?先是?個指針變量,該指針變量指向?個具體的函數。這正如?指針變量可指向整型變量、字符型、數組?樣,這?是指向函數。
在編譯時,每?個函數都有?個??地址,該??地址就是函數指針所指向的地址。有了指向函數的指針變量后,可?該指針變量調?函數,就如同?指針變量可引?其他類型變量?樣,在這些概念上是?體?致的。
其次是?途:調?函數和做函數的參數,?如回調函數。
示例:
char * fun(char * p) {…} // 函數fun char * (*pf)(char * p); //?函數指針pf pf = fun; // 函數指針pf指向函數fun,fun就是函數地址 不需要用&fun pf(p); // 通過函數指針pf調?函數fun16、 回調函數的作用
1.當發生某種事件時,系統或其他函數將會自動調用你定義的一段函數;
2.回調函數就相當于一個中斷處理函數,由系統在符合你設定的條件時自動調用。為此,你需要做三件事:1,聲明;2,定義;3,設置觸發條件,就是在你的函數中把你的回調函數名稱轉化為地址作為一個參數,以便于系統調用;
3.回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用為調用它所指向的函數時,我們就說這是回調函數;
4.因為可以把調用者與被調用者分開。調用者不關心誰是被調用者,所有它需知道的,只是存在一個具有某種特定原型、某些限制條件(如返回值為int)的被調用函數。
17、 隨機數的生成
1.#include<time.h>??srand((unsigned)time(NULL));???cout<<(rand()%(b-a))+a;
2.由于rand()的內部實現是用線性同余法做的,所以生成的并不是真正的隨機數,而是在一定范圍內可看為隨機的偽隨機數。
3.種子寫為srand(time(0))代表著獲取系統時間,電腦右下角的時間,每一秒后系統時間的改變,數字序列的改變得到的數字不同,這才得帶不同的數字,形成了真隨機數,即使是真隨機數,也是有規律可循。
lsy注:這里加一道算法題,用random7()實現random10.
18、 for(;;)和while(true)的區別
總結 for(;;) 比 while(true) 好。
1.編譯前??while (1);
編譯后
mov eax,1
test eax,eax
je foo+23h
jmp foo+18h
2.編譯前for (;;);
編譯后
jmp foo+23h
對比之下,for (;;)指令少,不占用寄存器,而且沒有判斷跳轉,比while (1)好。也就是說兩者在在宏觀上完全一樣的邏輯,但是底層完全不一樣,for相對于來說更加簡潔明了。
19、 數據結構中的常見樹結構
1、?叉樹:任何節點最多只允許有兩個?節點,稱為左?節點和右?節點,以遞歸的?式定義?叉樹為,?個?叉樹如果不為空,便是由?個根節點和左右兩個?樹構成,左右?樹都可能為空。
2、?叉搜索樹(二叉查找樹):?叉搜索樹可以提供對數時間的元素插?和訪問。節點的放置規則是:任何節點的鍵值?定?于其左?樹的每?個節點的鍵值,并?于其右?樹中的每?個節點的鍵值。因此?直向左?可以取得最?值,?直向右?可以得到最?值。插?:從根節點開始,遇鍵值較?則向左,遇鍵值較?則向右,直到尾端,即插?點。刪除:如果刪除點只有?個?節點,則直接將其?節點連??節點。如果刪除點有兩個?節點,以右?樹中的最?值代替要刪除的位置。
3、平衡?叉樹:其實對于樹的平衡與否沒有?個絕對的標準, “平衡”的?致意 思是:沒有任何?個節點過深,不同的平衡條件會造就出不同的效率表現。以及不同的實現復雜度。
4、AVL樹:高度平衡的平衡?叉樹(嚴格的平衡?叉樹),AVL-tree 是要求任何節點的左右?樹?度相差最多為 1 的平衡?叉樹。 當插?新的節點破壞平衡性的時候,從下往上找到第?個不平衡點,需要進?單旋轉,或者雙旋轉進?調整。
5、紅黑樹:紅黑樹就是一種平衡二叉樹,說它平衡的意思是它不會出現左子樹與右子樹的高度之差不會大于1,左子樹和右子樹保持一種平衡的關系。
6、B樹、B+樹
20、 什么是平衡二叉樹
1.平衡二叉樹的性質
在平衡二叉樹樹中,任一節點對應的兩棵子樹的最大高度差為1,因此它也被稱為高度平衡樹。查找、插入和刪除在平均和最壞情況下的時間復雜度都是O(log n)。
1可以是空樹。
2假如不是空樹,任何一個結點的左子樹與右子樹都是平衡二叉樹,并且高度之差的絕對值不超過1。
2.平衡二叉樹的左旋右旋
增加和刪除元素的操作則可能需要借由一次或多次樹旋轉,以實現樹的重新平衡。
1左旋
當插入的新節點在節點右子樹的右子樹位置時。
簡稱:右右->左旋
(1)原節點的右孩子替代此節點位置
(2)原節點右孩子的左子樹變為該節點的右子樹
(3)原節點本身變為右孩子新節點的左子樹
2右旋
當新插入是節點左子樹的左子樹時,需要右旋。
簡稱:左左->右旋
右旋操作與左旋類似,操作流程為:
(1)原節點的左孩子代表此節點
(2)原節點的左孩子的右子樹變為節點的左子樹
(3)將原節點作為左孩子新節點的右子樹。
3.平衡二叉樹的插入方式
1插入方式:LL
描述:在 A 的左子樹根節點的左子樹上插入節點而破壞平衡
旋轉方式:右旋轉
2插入方式:RR
描述:在 A 的右子樹根節點的右子樹上插入節點而破壞平衡
旋轉方式:左旋轉
3插入方式:LR
描述:在A的左子樹根節點的右子樹上插入節點而破壞平衡
旋轉方式:先左旋后右旋
4插入方式:RL
描述:在 A 的右子樹根節點的左子樹上插入節點而破壞平衡
旋轉方式:先右旋后左旋
lsy注:詳解平衡二叉樹(AVL),紅黑樹與平衡二叉樹的區別_子木呀的博客-CSDN博客
總結
以上是生活随笔為你收集整理的二十万字C/C++、嵌入式软开面试题全集宝典十的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: yy神曲url解析php_php解析ur
- 下一篇: 二十万字C/C++、嵌入式软开面试题全集