二十万字C/C++、嵌入式软开面试题全集宝典一
?
目錄
1、引用和指針的區別?
2、從匯編層去解釋一下引用
3、C++中的指針參數傳遞和引用參數傳遞
4、形參與實參的區別?
5、static的用法和作用?
6、靜態變量什么時候初始化
7、const?
8、const成員函數的理解和應用?
9、指針和const的用法?const?修飾指針如何區分?
10、C++?是怎么定義常量的?常量存放在內存的哪個位置?
11、mutable
12、extern用法?
13、int轉字符串,字符串轉int?strcat,strcpy,strncpy,memset,memcpy的內部實現?
14、深拷貝與淺拷貝?
15、C++模板是什么,底層怎么實現的?
??
1、引用和指針的區別?
1.指針是一個實體,需要分配內存空間。引用只是變量的別名,不需要分配內存空間。
2.引用在定義的時候必須進行初始化,并且不能夠改變。指針在定義的時候不一定要初始化,并且指向的空間可變。(注:“引用不能為空(NULL),引用必須與合法的存儲單元關聯,指針則可以是NULL)”)
3.有多級指針,但是沒有多級引用,只能有一級引用。
4.指針和引用的自增運算結果不一樣。(指針是指向下一個空間,引用時引用的變量值加1)
5.sizeof 引用得到的是所指向的變量(對象)的大小,而sizeof 指針得到的是指針本身的大小。
6.引用訪問一個變量是直接訪問,而指針訪問一個變量是間接訪問。
使用指針前最好做類型檢查,防止野指針的出現;
7.引用底層是通過指針實現的;
8.作為參數時也不同,傳指針的實質是傳值,傳遞的值是指針的地址;傳引用的實質是傳地址,傳遞的是變量的地址。
2、從匯編層去解釋一下引用
9:??????????int x = 1; 00401048????mov dword ptr [ebp-4],1 10:?????????int &b = x; 0040104F????lea eax,[ebp-4] 00401052????mov dword ptr [ebp-8],eaxx的地址為ebp-4,b的地址為ebp-8,因為棧內的變量內存是從高往低進行分配的。所以b的地址比x的低。lea eax,[ebp-4]? 這條語句將x的地址ebp-4放入eax寄存器mov dword ptr [ebp-8],eax?這條語句將eax的值放入b的地址ebp-8中上面兩條匯編的作用即:將x的地址存入變量b中,這不和將某個變量的地址存入指針變量是一樣的嗎?所以從匯編層次來看,的確引用是通過指針來實現的。
3、C++中的指針參數傳遞和引用參數傳遞
1.指針參數傳遞本質上是值傳遞,它所傳遞的是一個地址值。值傳遞過程中,被調函數的形式參數作為被調函數的局部變量處理,會在棧中開辟內存空間以存放由主調函數傳遞進來的實參值,從而形成了實參的一個副本(替身)。值傳遞的特點是,被調函數對形式參數的任何操作都是作為局部變量進行的,不會影響主調函數的實參變量的值(形參指針變了,實參指針不會變)。
2.引用參數傳遞過程中,被調函數的形式參數也作為局部變量在棧中開辟了內存空間,但是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參(本體)的任何操作都被處理成間接尋址,即通過棧中存放的地址訪問主調函數中的實參變量(根據別名找到主調函數中的本體)。因此,被調函數對形參的任何操作都會影響主調函數中的實參變量。
3.引用傳遞和指針傳遞是不同的,雖然他們都是在被調函數棧空間上的一個局部變量,但是任何對于引用參數的處理都會通過一個間接尋址的方式操作到主調函數中的相關變量。而對于指針傳遞的參數,如果改變被調函數中的指針地址,它將應用不到主調函數的相關變量。如果想通過指針參數傳遞來改變主調函數中的相關變量(地址),那就得使用指向指針的指針或者指針引用。
4.從編譯的角度來講,程序在編譯時分別將指針和引用添加到符號表上,符號表中記錄的是變量名及變量所對應地址。指針變量在符號表上對應的地址值為指針變量的地址值,而引用在符號表上對應的地址值為引用對象的地址值(與實參名字不同,地址相同)。符號表生成之后就不會再改,因此指針可以改變其指向的對象(指針變量中的值可以改),而引用對象則不能修改。
4、形參與實參的區別?
1.形參變量只有在被調用時才分配內存單元,在調用結束時,?即刻釋放所分配的內存單元。因此,形參只有在函數內部有效。?函數調用結束返回主調函數后則不能再使用該形參變量。
2.實參可以是常量、變量、表達式、函數等, 無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值, 以便把這些值傳送給形參。 因此應預先用賦值,輸入等辦法使實參獲得確定值,會產生一個臨時變量。
3.實參和形參在數量上,類型上,順序上應嚴格一致,?否則會發生“類型不匹配”的錯誤。
4.函數調用中發生的數據傳送是單向的。 即只能把實參的值傳送給形參,而不能把形參的值反向地傳送給實參。 因此在函數調用過程中,形參的值發生改變,而實參中的值不會變化。
5.當形參和實參不是指針類型時,在該函數運行時,形參和實參是不同的變量,他們在內存中位于不同的位置,形參將實參的內容復制一份,在該函數運行結束的時候形參被釋放,而實參內容不會改變。
6.值傳遞:有一個形參向函數所屬的棧拷貝數據的過程,如果值傳遞的對象是類對象????或是大的結構體對象,將耗費一定的時間和空間。(傳值)
7.指針傳遞:同樣有一個形參向函數所屬的棧拷貝數據的過程,但拷貝的數據是一個固定為4字節的地址。(傳值,傳遞的是地址值)
8.引用傳遞:同樣有上述的數據拷貝過程,但其是針對地址的,相當于為該數據所在的地址起了一個別名。(傳地址)
9.效率上講,指針傳遞和引用傳遞比值傳遞效率高。一般主張使用引用傳遞,代碼邏輯上更加緊湊、清晰。
5、static的用法和作用?
1)static作?:控制變量的存儲?式和可?性。
1.static作用1:隱藏。(static函數,static變量均可)
當同時編譯多個文件時,所有未加static前綴的全局變量和函數都具有全局可見性。
2.static作用2:
保持變量內容的持久。(static變量中的記憶功能和全局生存期)存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。共有兩種變量存儲在靜態存儲區:全局變量和static變量,只不過和全局變量比起來,static可以控制變量的可見范圍,說到底static還是用來隱藏的。
3.static作用3:默認初始化為0(static變量)
其實全局變量也具備這一屬性,因為全局變量也存儲在靜態數據區。在靜態數據區,內存中所有的字節默認值都是0x00,某些時候這一特點可以減少程序員的工作量。
2)static用法:
用法1:修飾局部變量:
?般情況下,對于局部變量在程序中是存放在棧區的,并且局部的?命周期在包含語句塊執?結束時便結束了。但是如果? static 關鍵字修飾的話,該變量便會存放在靜態數據區,其?命周期會?直延續到整個程序執?結束。但是要注意的是,雖然?static 對局部變量進?修飾之后,其?命周期以及存儲空間發?了變化,但其作?域并沒有改變,作?域還是限制在其語句塊。
用法2:修飾全部變量:
對于?個全局變量,它既可以在本?件中被訪問到,也可以在同?個?程中其它源?件被訪問(添加 extern進?聲明即可)。?static對全局變量進?修飾改變了其作?域范圍,由原來的整個?程可?變成了本?件可?。
用法3:修飾函數:
?static?修飾函數,情況和修飾全局變量類似,也是改變了函數的作?域。
用法4:修飾類:
如果 C++ 中對類中的某個函數?static修飾,則表示該函數屬于?個類?不是屬于此類的任何特定對象;如果對類中的某個變量進?static修飾,則表示該變量以及所有的對象所有,存儲空間中只存在?個副本,可以通過;類和對象去調?。(補充:靜態?常量數據成員,其只能在類外定義和初始化,在類內僅是聲明?已。)
用法5:C++中的類成員聲明static
5.1函數體內static變量的作用范圍為該函數體,不同于auto變量,該變量的內存只被分配一次,因此其值在下次調用時仍維持上次的值;
5.2在模塊內的static全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
5.3在模塊內的static函數只可被這一模塊內的其它函數調用,這個函數的使用范圍被限制在聲明它的模塊內;???
5.4在類中的static成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;???
5.5在類中的static成員函數屬于整個類所擁有,這個函數不接收this指針,因而只能訪問類的static成員變量。
類內:
5.6static類對象必須要在類外進行初始化,static修飾的變量先于對象存在,所以static修飾的變量要在類外初始化;
5.7由于static修飾的類成員屬于類,不屬于對象,因此static類成員函數是沒有this指針的,this指針是指向本對象的指針。正因為沒有this指針,所以static類成員函數不能訪問非static的類成員,只能訪問 static修飾的類成員;
5.8 static成員函數不能被virtual修飾,static成員不屬于任何對象或實例,所以加上virtual沒有任何實際意義;靜態成員函數沒有this指針,虛函數的實現是為每一個對象分配一個vptr指針,而vptr是通過this指針調用的,所以不能為virtual;虛函數的調用關系,this->vptr->ctable->virtual function
6、靜態變量什么時候初始化
1.初始化只有一次,但是可以多次賦值,在主程序之前,編譯器已經為其分配好了內存(靜態存儲區)。
2.靜態局部變量和全局變量一樣,數據都存放在全局區域,所以在主程序之前,編譯器已經為其分配好了內存,但在C和C++中靜態局部變量的初始化節點又有點不太一樣。在C中,初始化發生在代碼執行之前,編譯階段分配好內存之后,就會進行初始化,所以我們看到在C語言中無法使用變量對靜態局部變量進行初始化,在程序運行結束,變量所處的全局內存會被全部回收。
3.在C++中,初始化時在執行相關代碼時才會進行初始化,主要是由于C++引入對象后,要進行初始化必須執行相應構造函數和析構函數,在構造函數或析構函數中經常會需要進行某些程序中需要進行的特定操作,并非簡單地分配內存。所以C++標準定為全局或靜態對象是有首次用到時才會進行構造,并通過atexit()來管理。在程序結束,按照構造順序反方向進行逐個析構。所以在C++中是可以使用變量對靜態局部變量進行初始化的。
7、const?
在C語言里,const只是聲明只讀屬性,還是可以通過修改內存上的值進行修改,只不過這種做法是未定義的。C++里的常量是真正的常量,放在符號表里。
1.阻止一個變量被改變,可以使用const關鍵字。在定義該const變量時,通常需要對它進行初始化,因為以后就沒有機會再去改變它了;
2.對指針來說,可以指定指針本身為const,也可以指定指針所指的數據為const,或二者同時指定為const;
3.在一個函數聲明中,const可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;
5.對于類的成員函數,可以指定其返回值為const類型,以使得其返回值不為“左值”。若指定成員函數為const類型,則表明其是一個常函數,不能修改類的成員變量,類的常對象只能訪問類的常成員函數;
6、const成員函數可以訪問非const對象的非const數據成員、const數據成員,也可以訪問const對象內的所有數據成員;
7、非const成員函數可以訪問非const對象的非const數據成員、const數據成員,但不可以訪問const對象的任意數據成員;
8、一個沒有明確聲明為const的成員函數被看作是將要修改對象中數據成員的函數,而且編譯器不允許它為一個const對象所調用。因此const對象只能調用const成員函數。
9、const類型變量可以通過類型轉換符const_cast將const類型轉換為非const類型;
10、const類型變量必須定義的時候進行初始化,因此也導致如果類的成員變量有const類型的變量,那么該變量必須在類的初始化列表中進行初始化;
11、對于函數值傳遞的情況,因為參數傳遞是通過復制實參創建一個臨時變量傳遞進函數的,函數內只能改變臨時變量,但無法改變實參。則這個時候無論加不加const對實參不會產生任何影響。但是在引用或指針傳遞函數調用中,因為傳進去的是一個引用或指針,這樣函數內部可以改變引用或指針所指向的變量,這時const 才是實實在在地保護了實參所指向的變量。因為在編譯階段編譯器對調用函數的選擇是根據實參進行的,所以,只有引用傳遞和指針傳遞可以用是否加const來重載。一個擁有頂層const的形參無法和另一個沒有頂層const的形參區分開來。
8、const成員函數的理解和應用?
①const Stock & Stock::topval (②const Stock & s)?③const
①處const:確保返回的Stock對象在以后的使用中不能被修改??
②處const:確保此方法不修改傳遞的參數 S
③處const:保證此方法不修改調用它的對象,const對象只能調用const成員函數,不能調用非const函數
9、指針和const的用法?const?修飾指針如何區分?
當const修飾指針時,由于const的位置不同,它的修飾對象會有所不同。
1、int *const p2中const修飾p2的值,所以理解為p2的值不可以改變,即p2只能指向固定的一個變量地址,但可以通過*p2讀寫這個變量的值。頂層指針表示指針本身是一個常量
2、int const *p1或者const int *p1兩種情況中const修飾*p1,所以理解為*p1的值不可以改變,即不可以給*p1賦值改變p1指向變量的值,但可以通過給p賦值不同的地址改變這個指針指向。底層指針表示指針所指向的變量是一個常量。
4、int const *const p;p這個指針不可改,指向的值也不可變。
示例:下?都是合法的聲明,但是含義?不同:
const int * p1; //指向整形常量的指針,它指向的值不能修改
int * const p2; //指向整形的常量指針 ,它不能在指向別的變量,但指向(變量)的值可以修改。
const int *const p3; //指向整形常量 的 常量指針 。它既不能再指向別的常量,指向的值也不能修改。
理解這些聲明的技巧在于,const是右結合型。查看關鍵字const右邊來確定什么被聲明為常量 ,如果該關鍵字的右邊是類型,則值是常量;如果關鍵字的右邊是指針變量,則指針本身是常量。
10、C++?是怎么定義常量的?常量存放在內存的哪個位置?
對于局部常量,存放在棧區;
對于全局常量,編譯期?般不分配內存,放在符號表中以提?訪問效率;
字?值常量,?如字符串,放在常量區。
11、mutable
1.如果需要在const成員方法中修改一個成員變量的值,那么需要將這個成員變量修飾為mutable。即用mutable修飾的成員變量不受const成員方法的限制;
2.可以認為mutable的變量是類的輔助狀態,但是只是起到類的一些方面表述的功能,修改他的內容我們可以認為對象的狀態本身并沒有改變的。實際上由于const_cast的存在,這個概念很多時候用處不是很到了。
12、extern用法?
作用:
1.修飾符 extern ?在變量或者函數的聲明前,?來說明 “此變量/函數是在別處定義的,要在此處引?”。
2.有了#include,為啥要? extern?因為? extern 能夠提前進行聲明,會加速程序的編譯過程,這樣能節省時間。
用法:
1、extern修飾變量的聲明
如果文件a.c需要引用b.c中變量int v,就可以在a.c中聲明extern int v,然后就可以引用變量v。
2、extern修飾函數的聲明
如果文件a.c需要引用b.c中的函數,比如在b.c中原型是int fun(int mu),那么就可以在a.c中聲明extern int fun(int mu),然后就能使用fun來做任何事情。就像變量的聲明一樣,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范圍中。
3、extern修飾符可用于指示C或者C++函數的調用規范。
比如在C++中調用C庫函數,就需要在C++程序中用extern “C”聲明要引用的函數。這是給鏈接器用的,告訴鏈接器在鏈接的時候用C函數規范來鏈接。主要原因是C++和C程序編譯完成后在目標代碼中命名規則不同。
13、int轉字符串,字符串轉int?strcat,strcpy,strncpy,memset,memcpy的內部實現?
c++11標準增加了全局函數std::to_string
可以使用std::stoi/stol/stoll等等函數
strcpy擁有返回值,有時候函數原本不需要返回值,但為了增加靈活性如支持鏈式表達。
14、深拷貝與淺拷貝?
1.淺拷貝:只是拷貝了基本類型的數據,而引用類型數據,復制后也是會發生引用,我們把這種拷貝叫做“(淺復制)淺拷貝”,換句話說,淺復制僅僅是指向被復制的內存地址,如果原地址中對象被改變了,那么淺復制出來的對象也會相應改變。
深拷貝:在計算機中開辟了一塊新的內存地址用于存放復制的對象。
?
2.在某些狀況下,類內成員變量需要動態開辟堆內存,如果實行淺拷貝,也就是把對象里的值完全復制給另一個對象,如A=B。這時,如果B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。
3.深拷?與淺拷?之間的區別就在于深拷?會在堆內存中另外申請空間來存儲數據,從?也就解決來野指針的問題。簡??之,當數據成員中有指針時,必需要?深拷?更加安全。
lsy注:賦值運算符重載是深拷貝還是淺拷貝?可以深拷貝也可以淺拷貝。提供了深拷貝的可能性。
15、C++模板是什么,底層怎么實現的?
1、模板是C++支持參數化多態的工具,為類或者函數聲明一種一般模式,使得類中的某些數據成員或者成員函數的參數、返回值取得任意類型。模板是一種對類型進行參數化的工具。
2、函數模板的實例化是由編譯程序處理函數調用時自動完成的;類模板的實例化是由程序員在程序中顯式的指定;函數模板針對參數類型不同和返回值類型不同的函數;類模板針對數據成員、成員函數和繼承的基類類型不同的類。
3、編譯器會對函數模板進行兩次編譯:在聲明的地方對模板代碼本身進行編譯,在對參數替換后的代碼進行編譯。
4、函數模板要被實例化后才能成為真正的函數,在使用函數模板的源文件中包含函數模板的頭文件,如果該頭文件中只有聲明,沒有定義,那編譯器無法實例化該模板,最終導致鏈接錯誤。
總結
以上是生活随笔為你收集整理的二十万字C/C++、嵌入式软开面试题全集宝典一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++ 暂停功能_2020.10.16撸
- 下一篇: 二十万字C/C++、嵌入式软开面试题全集