李洪強經典面試題20

 

50socket鏈接和http鏈接的區別c++

簡單說,你瀏覽的網頁(網址以http://開頭)都是http協議傳輸到你的瀏覽器的, 而http是基於socket之上的。socket是一套完成tcp,udp協議的接口。程序員

HTTP協議:簡單對象訪問協議,對應於應用層  ,HTTP協議是基於TCP鏈接的面試

tcp協議:    對應於傳輸層算法

ip協議:     對應於網絡層 
TCP/IP是傳輸層協議,主要解決數據如何在網絡中傳輸;而HTTP是應用層協議,主要解決如何包裝數據。sql

Socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API),經過Socket,咱們才能使用TCP/IP協議。數據庫

http鏈接:http鏈接就是所謂的短鏈接,即客戶端向服務器端發送一次請求,服務器端響應後鏈接即會斷掉;編程

socket鏈接:socket鏈接就是所謂的長鏈接,理論上客戶端和服務器端一旦創建起鏈接將不會主動斷掉;可是因爲各類環境因素可能會是鏈接斷開,好比說:服務器端或客戶端主機down了,網絡故障,或者二者之間長時間沒有數據傳輸,網絡防火牆可能會斷開該鏈接以釋放網絡資源。因此當一個socket鏈接中沒有數據的傳輸,那麼爲了維持鏈接須要發送心跳消息~~具體心跳消息格式是開發者本身定義的設計模式

咱們已經知道網絡中的進程是經過socket來通訊的,那什麼是socket呢?socket起源於Unix,而Unix/Linux基本哲學之一就是「一切皆文件」,均可以用「打開open –> 讀寫write/read –> 關閉close」模式來操做。個人理解就是Socket就是該模式的一個實現,socket便是一種特殊的文件,一些socket函數就是對其進行的操做(讀/寫IO、打開、關閉),這些函數咱們在後面進行介紹。咱們在傳輸數據時,能夠只使用(傳輸層)TCP/IP協議,可是那樣的話,若是沒有應用層,便沒法識別數據內容,若是想要使傳輸的數據有意義,則必須使用到應用層協議,應用層協議有不少,好比HTTP、FTP、TELNET等,也能夠本身定義應用層協議。WEB使用HTTP協議做應用層協議,以封裝HTTP文本信息,而後使用TCP/IP作傳輸層協議將它發到網絡上。
1)Socket是一個針對TCP和UDP編程的接口,你能夠藉助它創建TCP鏈接等等。而TCP和UDP協議屬於傳輸層 。
  而http是個應用層的協議,它實際上也創建在TCP協議之上。 數組

 (HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通訊的能力。)瀏覽器

 2)Socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API),經過Socket,咱們才能使用TCP/IP協議。Socket的出現只是使得程序員更方便地使用TCP/IP協議棧而已,是對TCP/IP協議的抽象,從而造成了咱們知道的一些最基本的函數接口。

51什麼是TCP鏈接的三次握手

第一次握手:客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。

握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP鏈接一旦創建,在通訊雙方中的任何一方主動關閉鏈接以前,TCP 鏈接都將被一直保持下去。斷開鏈接時服務器和客戶端都可以主動發起斷開TCP鏈接的請求,斷開過程須要通過「四次握手」(過程就不細寫了,就是服務器和客戶端交互,最終肯定斷開)

52利用Socket創建網絡鏈接的步驟

創建Socket鏈接至少須要一對套接字,其中一個運行於客戶端,稱爲ClientSocket ,另外一個運行於服務器端,稱爲ServerSocket 。

套接字之間的鏈接過程分爲三個步驟:服務器監聽,客戶端請求,鏈接確認。

1。服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待鏈接的狀態,實時監控網絡狀態,等待客戶端的鏈接請求。

2。客戶端請求:指客戶端的套接字提出鏈接請求,要鏈接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要鏈接的服務器的套接字,指出服務器端套接字的地址和端口號,而後就向服務器端套接字提出鏈接請求。

3。鏈接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的鏈接請求時,就響應客戶端套接字的請求,創建一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式創建鏈接。而服務器端套接字繼續處於監聽狀態,繼續接收其餘客戶端套接字的鏈接請求。

53進程與線程

進程(process)是一塊包含了某些資源的內存區域。操做系統利用進程把它的工做劃分爲一些功能單元。

進程中所包含的一個或多個執行單元稱爲線程(thread)。進程還擁有一個私有的虛擬地址空間,該空間僅能被它所包含的線程訪問。

一般在一個進程中能夠包含若干個線程,它們能夠利用進程所擁有的資源。

在引入線程的操做系統中,一般都是把進程做爲分配資源的基本單位,而把線程做爲獨立運行和獨立調度的基本單位。

因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統內多個程序間併發執行的程度。

簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.一個程序就是一個進程,而一個程序中的多個任務則被稱爲線程。

 

線程只能歸屬於一個進程而且它只能訪問該進程所擁有的資源。當操做系統建立一個進程後,該進程會自動申請一個名爲主線程或首要線程的線程。應用程序(application)是由一個或多個相互協做的進程組成的。

另外,進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。
線程在執行過程當中與進程仍是有區別的。每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行。但操做系統並無將多個線程看作多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。

進程是具備必定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位.
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程本身基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),可是它可與同屬一個進程的其餘的線程共享進程所擁有的所有資源.
一個線程能夠建立和撤銷另外一個線程;同一個進程中的多個線程之間能夠併發執行.

54多線程

多線程編程是防止主線程堵塞,增長運行效率等等的最佳方法。而原始的多線程方法存在不少的毛病,包括線程鎖死等。在Cocoa中,Apple提供了NSOperation這個類,提供了一個優秀的多線程編程方法。

本次介紹NSOperation的子集,簡易方法的NSInvocationOperation:

 

一個NSOperationQueue 操做隊列,就至關於一個線程管理器,而非一個線程。由於你能夠設置這個線程管理器內能夠並行運行的的線程數量等等

55oc語法裏的@perpoerty不用寫@synzhesize了,自動填充了。而且的_name;

寫方法時候不用提早聲明。llvm 全局方法便利。

枚舉類型。enum hello:Integer{  } 冒號後面直接能夠跟類型,之前是:

enum hello{} 後面在指定爲Integer .

橋接。ARC 自動release retain 的時候 CFString CFArray . Core Fountion. 加上橋接_brige  才能區分CFString 和NSString 而如今自動區分了,叫固定橋接。

 

下拉刷新封裝好了。

UICollectionViewController. 能夠把表格分紅多列。

 

Social Framework(社交集成)

UIActivityViewController來詢問用戶的社交行爲

 

緩存:就是存放在臨時文件裏,好比新浪微博請求的數據,和圖片,下次請求看這裏有沒有值。

56Singleton(單例模式),也叫單子模式,是一種經常使用的軟件設計模式。在應用這個模式時,單例對象的類必須保證只有一個實例存在。 

代碼以下: 

static ClassA *classA = nil;//靜態的該類的實例 

+ (ClassA *)sharedManager 

{ 

@synchronized(self) { 

if (!classA) { 

classA = [[super allocWithZone:NULL]init]; 

return classA; 

} 

+ (id)allocWithZone:(NSZone *)zone { 

return [[self sharedManager] retain]; 

- (id)copyWithZone:(NSZone *)zone { 

return self; 

- (id)retain { 

return self; 

- (NSUIntger)retainCount { 

return NSUIntgerMax; 

- (oneway void)release { 

- (id)autorelease { 

return self; 

-(void)dealloc{ 

57請寫一個C函數,若處理器是Big_endian的,則返回0;如果Little_endian的,則返回1 int checkCPU( ) {   

     {           

       union w      

            {        

                     int a;      

                     char b;         

             } c;             

            c.a = 1;    

        return  (c.b ==1);      

  } 

剖析:嵌入式系統開發者應該對Little-endian和Big-endian模式很是瞭解。採用Little-endian模式的CPU對操做數的存放方式是從低字節到高字節, Big-endian  模式的CPU對操做數的存放方式是從高字節到低字節。在弄清楚這個以前要弄清楚這個問題:字節從右到坐爲從高到低! 假設從地址0x4000開始存放: 0x12345678,是也個32位四個字節的數據,最高字節是0x12,最低字節是0x78:在Little-endian模式CPU內存中的存放方式爲: (高字節在高地址, 低字節在低地址) 

內存地址0x4000 0x4001 0x4002 0x4003 

存放內容 0x78 0x56 0x34 0x12 

大端機則相反。 

 

有的處理器系統採用了小端方式進行數據存放,如Intel的奔騰。有的處理器系統採用了大端方式進行數據存放,如IBM半導體和Freescale的PowerPC處理器。不只對於處理器,一些外設的設計中也存在着使用大端或者小端進行數據存放的選擇。所以在一個處理器系統中,有可能存在大端和小端模式同時存在的現象。這一現象爲系統的軟硬件設計帶來了不小的麻煩,這要求系統設計工程師,必須深刻理解大端和小端模式的差異。大端與小端模式的差異體如今一個處理器的寄存器,指令集,系統總線等各個層次中。   聯合體union的存放順序是全部成員都從低地址開始存放的。以上是網上的原文。讓咱們看看在ARM處理器上union是如何存儲的呢?   地址A ---------------- |A     |A+1   |A+2   |A+3    |int a; |      |         |         |          -------------------- |A     |char b; |      | ---------                                                                            若是是小端如何存儲c.a的呢?  

                                         地址A ----------- 

------------------- |A    |A+1   |A+2    |A+3 | int a; 

|0x01 |0x00   |0x00   |0x00 | ------------------------------------- |A    |char b; |     | ---------                                  

                                若是是大端如何存儲c.a的呢?   

  地址A --------------------- 

--------- |A      |A+1    |A+2     |A+3     |int a; |0x00   |0x00   |0x00    |0x01    | ------------------------------------------ |A      |char b; |       | ---------                                                                                                                                                        如今知道爲何c.b==0的話是大端,c.b==1的話就是小端了吧。

58

堆和棧上的指針 

指針所指向的這塊內存是在哪裏分配的,在堆上稱爲堆上的指針,在棧上爲棧上的指針. 

在堆上的指針,能夠保存在全局數據結構中,供不一樣函數使用訪問同一塊內存. 

在棧上的指針,在函數退出後,該內存即不可訪問. 

59什麼是指針的釋放? 

具體來講包括兩個概念. 

1 釋放該指針指向的內存,只有堆上的內存才須要咱們手工釋放,棧上不須要. 

2 將該指針重定向爲NULL. 

60數據結構中的指針? 

其實就是指向一塊內存的地址,經過指針傳遞,可實現複雜的內存訪問. 

7 函數指針? 

指向一塊函數的入口地址. 

 

8 指針做爲函數的參數? 

好比指向一個複雜數據結構的指針做爲函數變量 

這種方法避免整個複雜數據類型內存的壓棧出棧操做,提升效率. 

注意:指針自己不可變,但指針指向的數據結構能夠改變. 

 

9 指向指針的指針? 

指針指向的變量是一個指針,即具體內容爲一個指針的值,是一個地址. 

此時指針指向的變量長度也是4位. 

61指針與地址的區別? 

區別: 

1指針意味着已經有一個指針變量存在,他的值是一個地址,指針變量自己也存放在一個長度爲四個字節的地址當中,而地址概念自己並不表明有任何變量存在. 

2 指針的值,若是沒有限制,一般是能夠變化的,也能夠指向另一個地址. 

   地址表示內存空間的一個位置點,他是用來賦給指針的,地址自己是沒有大小概念,指針指向變量的大小,取決於地址後面存放的變量類型. 

62指針與數組名的關係? 

  其值都是一個地址,但前者是能夠移動的,後者是不可變的. 

 

12 怎樣防止指針的越界使用問題? 

  必須讓指針指向一個有效的內存地址, 

1 防止數組越界 

2 防止向一塊內存中拷貝過多的內容 

3 防止使用空指針 

4 防止改變const修改的指針 

5 防止改變指向靜態存儲區的內容 

6 防止兩次釋放一個指針 

7 防止使用野指針. 

 

 

13 指針的類型轉換? 

指針轉換一般是指針類型和void * 類型以前進行強制轉換,從而與指望或返回void指針的函數進行正確的交接. 

63static有什麼用途?(請至少說明兩種)
            1.限制變量的做用域
            2.設置變量的存儲域
            7. 引用與指針有什麼區別?
            1) 引用必須被初始化,指針沒必要。
            2) 引用初始化之後不能被改變,指針能夠改變所指的對象。
            2) 不存在指向空值的引用,可是存在指向空值的指針。
            8. 描述實時系統的基本特性
            在特定時間內完成特定的任務,實時性與可靠性

64全局變量和局部變量在內存中是否有區別?若是有,是什麼區別?
            全局變量儲存在靜態數據庫,局部變量在堆棧
            10. 什麼是平衡二叉樹?
            左右子樹都是平衡二叉樹且左右子樹的深度差值的絕對值不大於1

65堆棧溢出通常是由什麼緣由致使的?

            沒有回收垃圾資源

            12. 什麼函數不能聲明爲虛函數?

            constructor

            13. 冒泡排序算法的時間複雜度是什麼?

            O(n^2)

            14. 寫出float x 與「零值」比較的if語句。

            if(x>0.000001&&x<-0.000001)

            16. Internet採用哪一種網絡協議?該協議的主要層次結構?

            tcp/ip 應用層/傳輸層/網絡層/數據鏈路層/物理層

            17. Internet物理地址和IP地址轉換採用什麼協議?

            ARP (Address Resolution Protocol)(地址解析協議)

            18.IP地址的編碼分爲哪倆部分?

            IP地址由兩部分組成,網絡號和主機號。不過是要和「子網掩碼」按位與上以後才能區

            分哪些是網絡位哪些是主機位。

            2.用戶輸入M,N值,從1至N開始順序循環數數,每數到M輸出該數值,直至所有輸出。寫

            出C程序。

            循環鏈表,用取餘操做作

            3.不能作switch()的參數類型是:

            switch的參數不能爲實型。

            華為

            一、局部變量可否和全局變量重名?

            答:能,局部會屏蔽全局。要用全局變量,須要使用"::"

            局部變量能夠與全局變量同名,在函數內引用這個變量時,會用到同名的局部變量,而

            不會用到全局變量。對於有些編譯器而言,在同一個函數內能夠定義多個同名的局部變

            量,好比在兩個循環體內都定義一個同名的局部變量,而那個局部變量的做用域就在那

            個循環體內

            二、如何引用一個已經定義過的全局變量?

            答:extern

            能夠用引用頭文件的方式,也能夠用extern關鍵字,若是用引用頭文件方式來引用某個

            在頭文件中聲明的全局變理,假定你將那個變寫錯了,那麼在編譯期間會報錯,若是你

            用extern方式引用時,假定你犯了一樣的錯誤,那麼在編譯期間不會報錯,而在鏈接期

            間報錯

            三、全局變量可不能夠定義在可被多個.C文件包含的頭文件中?爲何?

            答:能夠,在不一樣的C文件中以static形式來聲明同名全局變量。

            能夠在不一樣的C文件中聲明同名的全局變量,前提是其中只能有一個C文件中對此變量賦

            初值,此時鏈接不會出錯

            四、語句for( ;1 ;)有什麼問題?它是什麼意思?

            答:和while(1)相同。

            五、do……while和while……do有什麼區別?

            答:前一個循環一遍再判斷,後一個判斷之後再循環

661.IP Phone的原理是什麼?

            IPV6

            2.TCP/IP通訊創建的過程怎樣,端口有什麼做用?

            三次握手,肯定是哪一個應用程序使用該協議

            3.1號信令和7號信令有什麼區別,我國某前普遍使用的是那一種?

            4.列舉5種以上的電話新業務?

            微軟亞洲技術中心的面試題!!!

            1.進程和線程的差異。

            線程是指進程內的一個執行單元,也是進程內的可調度實體.

            與進程的區別:

            (1)調度:線程做爲調度和分配的基本單位,進程做爲擁有資源的基本單位

            (2)併發性:不只進程之間能夠併發執行,同一個進程的多個線程之間也可併發執行

            (3)擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但能夠訪問隸屬

            於進程的資源.

            (4)系統開銷:在建立或撤消進程時,因爲系統都要爲之分配和回收資源,致使系統的開

            銷明顯大於建立或撤消線程時的開銷。

            2.測試方法

            人工測試:我的複查、抽查和會審

            機器測試:黑盒測試和白盒測試

            2.Heap與stack的差異。

            Heap是堆,stack是棧。

            Stack的空間由操做系統自動分配/釋放,Heap上的空間手動分配/釋放。

            Stack空間有限,Heap是很大的自由存儲區

            C中的malloc函數分配的內存空間即在堆上,C++中對應的是new操做符。

            程序在編譯期對變量和函數分配內存都在棧上進行,且程序運行過程當中函數調用時參數的

            傳遞也在棧上進行

            3.Windows下的內存是如何管理的?

            4.介紹.Net和.Net的安全性。

            5.客戶端如何訪問.Net組件實現Web Service?

            6.C/C++編譯器中虛表是如何完成的?

            7.談談COM的線程模型。而後討論進程內/外組件的差異。

            8.談談IA32下的分頁機制

            小頁(4K)兩級分頁模式,大頁(4M)一級

            9.給兩個變量,如何找出一個帶環單鏈表中是什麼地方出現環的?

            一個遞增一,一個遞增二,他們指向同一個接點時就是環出現的地方

            10.在IA32中一共有多少種辦法從用戶態跳到內核態?

            經過調用門,從ring3到ring0,中斷從ring3到ring0,進入vm86等等

            11.若是隻想讓程序有一個實例運行,不能運行兩個。像winamp同樣,只能開一個窗

            口,怎樣實現?

            用內存映射或全局原子(互斥變量)、查找窗口句柄..

            FindWindow,互斥,寫標誌到文件或註冊表,共享內存。

67如何截取鍵盤的響應,讓全部的‘a’變成‘b’?
            鍵盤鉤子SetWindowsHookEx
            13.Apartment在COM中有什麼用?爲何要引入?
            14.存儲過程是什麼?有什麼用?有什麼優勢?
            個人理解就是一堆sql的集合,能夠創建很是複雜的查詢,編譯運行,因此運行一次後,
            之後再運行速度比單獨執行SQL快不少
            15.Template有什麼特色?何時用?
            16.談談Windows DNA結構的特色和優勢。
            網絡編程中設計併發服務器,使用多進程與多線程,請問有什麼區別?
            1,進程:子進程是父進程的複製品。子進程得到父進程數據空間、堆和棧的複製品。
            2,線程:相對與進程而言,線程是一個更加接近與執行體的概念,它能夠與同進程的其
            他線程共享數據,但擁有本身的棧空間,擁有獨立的執行序列。
            二者均可以提升程序的併發度,提升程序運行效率和響應時間。
            線程和進程在使用上各有優缺點:線程執行開銷小,但不利於資源管理和保護;而進程
            正相反。同時,線程適合於在SMP機器上運行,而進程則能夠跨機器遷移。
            思科

682.找錯題

 

  試題1:

 

void test1()

{

 char string[10];

 char* str1 = "0123456789";

 strcpy( string, str1 );

}

  試題2:

 

void test2()

{

 char string[10], str1[10];

 int i;

 for(i=0; i<10; i++)

 {

  str1 = 'a';
 }
 strcpy( string, str1 );

}

  試題3:

 

void test3(char* str1)

{

 char string[10];

 if( strlen( str1 ) <= 10 )

 {

  strcpy( string, str1 );

 }

}

  解答:

 

  試題1字符串str1須要11個字節才能存放下(包括末尾的’\0’),而string只有10個字節的空間,strcpy會致使數組越界;

 

  對試題2,若是面試者指出字符數組str1不能在數組內結束能夠給3分;若是面試者指出strcpy(string, str1)調用使得從str1[url=]內存[/url]起復制到string內存起所複製的字節數具備不肯定性能夠給7分,在此基礎上指出庫函數strcpy工做方式的給10分;

 

  對試題3,if(strlen(str1) <= 10)應改成if(strlen(str1) < 10),由於strlen的結果未統計’\0’所佔用的1個字節。

 

  剖析:

 

  考查對基本功的掌握:

 

  (1)字符串以’\0’結尾;

 

  (2)對數組越界把握的敏感度;

 

  (3)庫函數strcpy的工做方式,若是編寫一個標準strcpy函數的總分值爲10,下面給出幾個不一樣得分的答案:

 

  2分

 

void strcpy( char *strDest, char *strSrc )

{

  while( (*strDest++ = * strSrc++) != ‘\0’ );

}

  4分

 

void strcpy( char *strDest, const char *strSrc )

//將源字符串加const,代表其爲輸入參數,加2分

{

  while( (*strDest++ = * strSrc++) != ‘\0’ );

}

  7分

 

void strcpy(char *strDest, const char *strSrc)

{

 //對源地址和目的地址加非0斷言,加3分

 assert( (strDest != NULL) && (strSrc != NULL) );

 while( (*strDest++ = * strSrc++) != ‘\0’ );

}

  10分

 

//爲了實現鏈式操做,將目的地址返回,加3分!

 

char * strcpy( char *strDest, const char *strSrc )

{

 assert( (strDest != NULL) && (strSrc != NULL) );

 char *address = strDest;

 while( (*strDest++ = * strSrc++) != ‘\0’ );

  return address;

}

  從2分到10分的幾個答案咱們能夠清楚的看到,小小的strcpy居然暗藏着這麼多玄機,真不是蓋的!須要多麼紮實的基本功才能寫一個完美的strcpy啊!

 

  (4)對strlen的掌握,它沒有包括字符串末尾的'\0'。

 

  讀者看了不一樣分值的strcpy版本,應該也能夠寫出一個10分的strlen函數了,完美的版本爲: int strlen( const char *str ) //輸入參數const

 

{

 assert( strt != NULL ); //斷言字符串地址非0

 int len;

 while( (*str++) != '\0' )

 {

  len++;

 }

 return len;

}

  試題4:

 

void GetMemory( char *p )

{

 p = (char *) malloc( 100 );

}

 

void Test( void )

{

 char *str = NULL;

 GetMemory( str );

 strcpy( str, "hello world" );

 printf( str );

}

  試題5:

 

char *GetMemory( void )

{

 char p[] = "hello world";

 return p;

}

 

void Test( void )

{

 char *str = NULL;

 str = GetMemory();

 printf( str );

}

  試題6:

 

void GetMemory( char **p, int num )

{

 *p = (char *) malloc( num );

}

 

void Test( void )

{

 char *str = NULL;

 GetMemory( &str, 100 );

 strcpy( str, "hello" );

 printf( str );

}

  試題7:

 

void Test( void )

{

 char *str = (char *) malloc( 100 );

 strcpy( str, "hello" );

 free( str );

 ... //省略的其它語句

}

  解答:

 

  試題4傳入中GetMemory( char *p )函數的形參爲字符串指針,在函數內部修改形參並不能真正的改變傳入形參的值,執行完

 

char *str = NULL;

GetMemory( str );

  後的str仍然爲NULL;

 

  試題5中

 

char p[] = "hello world";

return p;

  的p[]數組爲函數內的局部自動變量,在函數返回後,內存已經被釋放。這是許多程序員常犯的錯誤,其根源在於不理解變量的生存期。

 

  試題6的GetMemory避免了試題4的問題,傳入GetMemory的參數爲字符串指針的指針,可是在GetMemory中執行申請內存及賦值語句

 

*p = (char *) malloc( num );

  後未判斷內存是否申請成功,應加上:

 

if ( *p == NULL )

{

 ...//進行申請內存失敗處理

}

  試題7存在與試題6一樣的問題,在執行

 

char *str = (char *) malloc(100);

  後未進行內存是否申請成功的判斷;另外,在free(str)後未置str爲空,致使可能變成一個「野」指針,應加上:

 

str = NULL;

  試題6的Test函數中也未對malloc的內存進行釋放。

 

  剖析:

 

  試題4~7考查面試者對內存操做的理解程度,基本功紮實的面試者通常都能正確的回答其中50~60的錯誤。可是要徹底解答正確,卻也絕非易事。

 

  對內存操做的考查主要集中在:

 

  (1)指針的理解;

 

  (2)變量的生存期及做用範圍;

 

  (3)良好的動態內存申請和釋放習慣。

 

  再看看下面的一段程序有什麼錯誤:

 

swap( int* p1,int* p2 )

{

 int *p;

 *p = *p1;

 *p1 = *p2;

 *p2 = *p;

}

  在swap函數中,p是一個「野」指針,有可能指向系統區,致使程序運行的崩潰。在VC++中DEBUG運行時提示錯誤「Access Violation」。該程序應該改成:

 

swap( int* p1,int* p2 )

{

 int p;

 p = *p1;

 *p1 = *p2;

 *p2 = p;

}[img=12,12]file:///D:/魚魚軟件/魚魚多媒體日記本/temp/{56068A28-3D3B-4D8B-9F82-AC1C3E9B128C}_arc_d[1].gif[/img] 3.內功題

  試題1:分別給出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)

 

  剖析:

 

  考查對0值判斷的「內功」,BOOL型變量的0判斷徹底能夠寫成if(var==0),而int型變量也能夠寫成if(!var),指針變量的判斷也能夠寫成if(!var),上述寫法雖然程序都能正確運行,可是未能清晰地表達程序的意思。

 通常的,若是想讓if判斷一個變量的「真」、「假」,應直接使用if(var)、if(!var),代表其爲「邏輯」判斷;若是用if判斷一個數值型變量(short、int、long等),應該用if(var==0),代表是與0進行「數值」上的比較;而判斷指針則適宜用if(var==NULL),這是一種很好的編程習慣。

 

  浮點型變量並不精確,因此不可將float變量用「==」或「!=」與數字比較,應該設法轉化成「>=」或「<=」形式。若是寫成if (x == 0.0),則判爲錯,得0分。

 

  試題2:如下爲Windows NT下的32位C++程序,請計算sizeof的值

 

void Func ( char str[100] )

{

 sizeof( str ) = ?

}

 

void *p = malloc( 100 );

sizeof ( p ) = ?

  解答:

 

sizeof( str ) = 4

sizeof ( p ) = 4

  剖析:

 

  Func ( char str[100] )函數中數組名做爲函數形參時,在函數體內,數組名失去了自己的內涵,僅僅只是一個指針;在失去其內涵的同時,它還失去了其常量特性,能夠做自增、自減等操做,能夠被修改。

 

  數組名的本質以下:

 

  (1)數組名指代一種數據結構,這種數據結構就是數組;

 

  例如:

 

char str[10];

cout << sizeof(str) << endl;

  輸出結果爲10,str指代數據結構char[10]。

 

  (2)數組名能夠轉換爲指向其指代實體的指針,並且是一個指針常量,不能做自增、自減等操做,不能被修改;

 

char str[10];

str++; //編譯出錯,提示str不是左值 

  (3)數組名做爲函數形參時,淪爲普通指針。

 

  Windows NT 32位平臺下,指針的長度(佔用內存的大小)爲4字節,故sizeof( str ) 、sizeof ( p ) 都爲4。

 

  試題3:寫一個「標準」宏MIN,這個宏輸入兩個參數並返回較小的一個。另外,當你寫下面的代碼時會發生什麼事?

 

least = MIN(*p++, b);

  解答:

 

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

  MIN(*p++, b)會產生宏的反作用

 

  剖析:

 

  這個面試題主要考查面試者對宏定義的使用,宏定義能夠實現相似於函數的功能,可是它終歸不是函數,而宏定義中括弧中的「參數」也不是真的參數,在宏展開的時候對「參數」進行的是一對一的替換。

 

  程序員對宏定義的使用要很是當心,特別要注意兩個問題:

 

  (1)謹慎地將宏定義中的「參數」和整個宏用用括弧括起來。因此,嚴格地講,下述解答:

 

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

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

  都應判0分;

 

  (2)防止宏的反作用。

 

  宏定義#define MIN(A,B) ((A) <= (B) ? (A) : (B))對MIN(*p++, b)的做用結果是:

 

((*p++) <= (b) ? (*p++) : (*p++))

 

  這個表達式會產生反作用,指針p會做三次++自增操做。

 

  除此以外,另外一個應該判0分的解答是:

 

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

  這個解答在宏定義的後面加「;」,顯示編寫者對宏的概念模糊不清,只能被無情地判0分並被面試官淘汰。

 

  試題4:爲何標準頭文件都有相似如下的結構?

 

#ifndef __INCvxWorksh

#define __INCvxWorksh

#ifdef __cplusplus

 

extern "C" {

#endif

/*...*/

#ifdef __cplusplus

}

 

#endif

#endif /* __INCvxWorksh */

  解答:

 

  頭文件中的編譯宏

 

#ifndef __INCvxWorksh

#define __INCvxWorksh

#endif

  的做用是防止被重複引用。

 

  做爲一種面向對象的語言,C++支持函數重載,而過程式語言C則不支持。函數被C++編譯後在symbol庫中的名字與C語言的不一樣。例如,假設某個函數的原型爲:

 

void foo(int x, int y);

  該函數被C編譯器編譯後在symbol庫中的名字爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字。_foo_int_int這樣的名字包含了函數名和函數參數數量及類型信息,C++就是考這種機制來實現函數重載的。

 

  爲了實現C和C++的混合編程,C++提供了C鏈接交換指定符號extern "C"來解決名字匹配問題,函數聲明前加上extern "C"後,則編譯器就會按照C語言的方式將該函數編譯爲_foo,這樣C語言中就能夠調用C++的函數了。[img=12,12]file:///D:/魚魚軟件/魚魚多媒體日記本/temp/{C74A38C4-432E-4799-B54D-73E2CD3C5206}_arc_d[1].gif[/img]

試題5:編寫一個函數,做用是把一個char組成的字符串循環右移n個。好比原來是「abcdefghi」若是n=2,移位後應該是「hiabcdefgh」

 

  函數頭是這樣的:

 

//pStr是指向以'\0'結尾的字符串的指針

//steps是要求移動的n

 

void LoopMove ( char * pStr, int steps )

{

 //請填充...

}

  解答:

 

  正確解答1:

 

void LoopMove ( char *pStr, int steps )

{

 int n = strlen( pStr ) - steps;

 char tmp[MAX_LEN];

 strcpy ( tmp, pStr + n );

 strcpy ( tmp + steps, pStr);

 *( tmp + strlen ( pStr ) ) = '\0';

 strcpy( pStr, tmp );

}

  正確解答2:

 

void LoopMove ( char *pStr, int steps )

{

 int n = strlen( pStr ) - steps;

 char tmp[MAX_LEN];

 memcpy( tmp, pStr + n, steps );

 memcpy(pStr + steps, pStr, n );

 memcpy(pStr, tmp, steps );

}

  剖析:

 

  這個試題主要考查面試者對標準庫函數的熟練程度,在須要的時候引用庫函數能夠很大程度上簡化程序編寫的工做量。

 

  最頻繁被使用的庫函數包括:

 

  (1) strcpy

 

  (2) memcpy

 

  (3) memset

相關文章
相關標籤/搜索