單片機(jī)面試筆試大全_第1頁
單片機(jī)面試筆試大全_第2頁
單片機(jī)面試筆試大全_第3頁
單片機(jī)面試筆試大全_第4頁
單片機(jī)面試筆試大全_第5頁
已閱讀5頁,還剩39頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

單片機(jī)筆試面試白皮書目錄第一部 筆試面試流程 2一、 準(zhǔn)備簡(jiǎn)歷 2二、 簡(jiǎn)歷投遞 2三、 筆試準(zhǔn)備 3四、 面試準(zhǔn)備 3五、 面試練習(xí) 4第二部 C筆試面試題 5第一大塊:基本語法 5第二大塊:變量 8第三大塊:函數(shù) 18第四大塊:指針&內(nèi)存 21第五大塊:鏈表 27第六大塊:算法 30第三部 LSD筆試面試題 37第四部 C++/驅(qū)動(dòng)筆試面試題 38第五部 JAVA筆試面試題 41第六部 android筆試面試題 41第七部 項(xiàng)目面試題 42

筆試面試流程準(zhǔn)備簡(jiǎn)歷在51、智聯(lián)、中華英才網(wǎng)站個(gè)做一份簡(jiǎn)歷。簡(jiǎn)歷內(nèi)容重點(diǎn): 所學(xué)課程(C語言、linux系統(tǒng)程序設(shè)計(jì)、JAVA、android) 所做項(xiàng)目:項(xiàng)目描述一定要完整,清晰。項(xiàng)目是關(guān)鍵點(diǎn)。并且,如果面試的是android,那么把a(bǔ)ndroid項(xiàng)目放在前面?!疽痪湓挃[平】你和應(yīng)屆生的本質(zhì)區(qū)別,就在于你有項(xiàng)目經(jīng)驗(yàn)。 工作背景:如果有技術(shù)相關(guān)背景,寫清楚,只要技術(shù)相關(guān)都可以加分的。 交流背景:以前做過和交流溝通相關(guān)的事情如果是應(yīng)屆生,那么做過的學(xué)生會(huì)工作,組著過活動(dòng)等;如果工作過,那么做過的工作,把閃光點(diǎn)說出來;最起碼,工作溝通、穩(wěn)定性等方面是可以展示出來的。面試官很關(guān)心這個(gè)。 簡(jiǎn)歷書寫注意事項(xiàng): 簡(jiǎn)歷書寫語言簡(jiǎn)潔,多用條例性語言。(第一、第二、第三、...) 簡(jiǎn)歷不能出現(xiàn)經(jīng)歷空白,如中間半年沒有任何經(jīng)歷。簡(jiǎn)歷不能和其他學(xué)員的簡(jiǎn)歷出現(xiàn)相同的內(nèi)容,尤其是項(xiàng)目描述。否則,兩個(gè)人都有可能失去面試機(jī)會(huì)。 簡(jiǎn)歷版面簡(jiǎn)單,字體不要超過3種。不需要相片,不需要花哨的格式。 招聘網(wǎng)站介紹: 51效果最好,重點(diǎn)關(guān)注51招聘網(wǎng)站。 注意關(guān)鍵字:在簡(jiǎn)歷重要多出現(xiàn)重要關(guān)鍵字:C語言、數(shù)據(jù)結(jié)構(gòu)、linux系統(tǒng)開發(fā)(linux系統(tǒng)移植、驅(qū)動(dòng)、arm、C++)、JAVA、android簡(jiǎn)歷投遞 每天早晨(一定要早晨,早晨的效果是最好的,否則可能效果減半)把3個(gè)網(wǎng)站的簡(jiǎn)歷都“刷新”一下。 每天早晨投遞簡(jiǎn)歷,主要搜索,嵌入式開發(fā)、C開發(fā)、linux開發(fā)、JAVA開發(fā)、android開發(fā)、軟件開發(fā)。每天投遞十幾個(gè)公司。 一家公司,如果沒給面試通知,多次投遞。重點(diǎn)關(guān)注的51網(wǎng)站,要在這個(gè)網(wǎng)站上注冊(cè)2-3個(gè)賬號(hào),使用同樣的簡(jiǎn)歷,每天可以更換賬號(hào)投遞。 并不是你投遞的每個(gè)人的簡(jiǎn)歷公司人事都會(huì)看到,如果收到的簡(jiǎn)歷很多,那么人事可能每天只能看到排在收件箱最前面的簡(jiǎn)歷。所以你的投遞必須要人事能最先看到。簡(jiǎn)歷投遞的重點(diǎn)注意:預(yù)處理器標(biāo)識(shí)#error的目的是什么?

如果你不知道答案,直接baidu。字節(jié)對(duì)齊//#pragmapack(4)structst{charc;

inti;

shorts;

};

sizeof(structst);

死循環(huán)(Infiniteloops)嵌入式系統(tǒng)中經(jīng)常要用到無限循環(huán),你怎么樣用C編寫死循環(huán)呢?

這個(gè)問題用幾個(gè)解決方案。我首選的方案是:

while(1){}

一些程序員更喜歡如下方案:

for(;;){}

這個(gè)實(shí)現(xiàn)方式讓我為難,因?yàn)檫@個(gè)語法沒有確切表達(dá)到底怎么回事。如果一個(gè)應(yīng)試者給出這個(gè)作為方案,我將用這個(gè)作為一個(gè)機(jī)會(huì)去探究他們這樣做的基本原理。如果他們的基本答案是:“我被教著這樣做,但從沒有想到過為什么?!边@會(huì)給我留下一個(gè)壞印象。

第三個(gè)方案是用goto

Loop:

...

gotoLoop;

應(yīng)試者如給出上面的方案,最好解釋匯編語言程或BASIC/FORTRAN語言可以這么做。但如果是C編程中出現(xiàn)goto,那么會(huì)留下惡劣的印象。Typedef在C語言中頻繁用以聲明一個(gè)已經(jīng)存在的數(shù)據(jù)類型的同義字。也可以用預(yù)處理器做類似的事。例如,思考一下下面的例子:

#definedPSstructs*

typedefstructs*tPS;

dPSmm,qq;//structs*mm,qq;tPSnn,pp;//structs*mm,*qq;

以上兩種情況的意圖都是要定義dPS和tPS作為一個(gè)指向結(jié)構(gòu)s指針。哪種方法更好呢?(如果有的話)為什么?

這是一個(gè)非常微妙的問題,任何人答對(duì)這個(gè)問題(正當(dāng)?shù)脑颍┦菓?yīng)當(dāng)被恭喜的。答案是:typedef更好。思考下面的例子:

dPSp1,p2;

tPSp3,p4;

第一個(gè)擴(kuò)展為

structs*p1,p2;

上面的代碼定義p1為一個(gè)指向結(jié)構(gòu)的指,p2為一個(gè)實(shí)際的結(jié)構(gòu),這也許不是你想要的。第二個(gè)例子正確地定義了p3和p4兩個(gè)指針。

C語言同意一些令人震驚的結(jié)構(gòu),下面的結(jié)構(gòu)是合法的嗎,如果是它做些什么?

inta=5,b=7,c;

c=a+++b;

這個(gè)問題將做為這個(gè)測(cè)驗(yàn)的一個(gè)愉快的結(jié)尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實(shí)際上會(huì)爭(zhēng)論這個(gè)問題,根據(jù)最處理原則,編譯器應(yīng)當(dāng)能處理盡可能所有合法的用法。因此,上面的代碼被處理成:

c=a+++b;

因此,這段代碼持行后a=6,b=7,c=12。

頭文件中的ifndef/define/endif干什么用?一個(gè)死循環(huán)intmain(){ unsignedchari; for(i=0;i<256;i++) { printf(“i=%d\n”,i);}return0;}程序運(yùn)行結(jié)果如何輸出源文件的標(biāo)題和目前執(zhí)行行的行數(shù)

intline=__LINE__;

char*file=__FILE__;

cout<<"filenameis"<<(file)<<",lineis"<第二大塊:變量變量的存儲(chǔ)空間C程序一直由下列部分組成:(1)正文段——CPU執(zhí)行的機(jī)器指令部分;一個(gè)程序只有一個(gè)副本;只讀,防止程序由于意外事故而修改自身指令;(2)初始化數(shù)據(jù)段(數(shù)據(jù)段)——在程序中所有賦了初值的全局變量,存放在這里。(3)非初始化數(shù)據(jù)段(bss段)——在程序中沒有初始化的全局變量;內(nèi)核將此段初始化為0。(4)?!鲩L方向:自頂向下增長;自動(dòng)變量以及每次函數(shù)調(diào)用時(shí)所需要保存的信息(返回地址;環(huán)境信息)。(5)堆——?jiǎng)討B(tài)存儲(chǔ)分配。||高地址|||棧 |||||||\|/||/|\|||||||堆||||未初始化||||初始化||||正文段|低地址問題:局部變量存放區(qū)域?初始化全局變量存放區(qū)域?未初始化的全局變量存放區(qū)域?靜態(tài)局部變量存放區(qū)域?動(dòng)態(tài)分配內(nèi)存是在那個(gè)區(qū)域?堆和棧的區(qū)別【一句話擺平】堆是手動(dòng),但容易導(dǎo)致內(nèi)存碎片;棧是自動(dòng),但空間有限。

一般認(rèn)為在c中分為這幾個(gè)存儲(chǔ)區(qū)

(1)、棧-有編譯器自動(dòng)分配釋放

(2)、堆-一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時(shí)可能由OS回收

(3)、全局區(qū)(靜態(tài)區(qū)),全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜

態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。

-程序結(jié)束釋放

(4)、另外還有一個(gè)專門放常量的地方。-程序結(jié)束釋放

在函數(shù)體中定義的變量通常是在棧上,用malloc,calloc,realloc等分配內(nèi)存的函數(shù)分

配得到的就是在堆上。在所有函數(shù)體外定義的是全局量,加了static修飾符后不管在哪

里都存放在全局區(qū)(靜態(tài)區(qū)),在所有函數(shù)體外定義的static變量表示在該文件中有效,

不能extern到別的文件用,在函數(shù)體內(nèi)定義的static表示只在該函數(shù)體內(nèi)有效。另外,

函數(shù)中的"adgfdf"這樣的字符串存放在常量區(qū)。

比如:

inta=0;全局初始化區(qū)

char*p1;全局未初始化區(qū)

main()

{

intb;棧

chars[]="abc";棧

char*p2;棧

char*p3="123456";123456\0在常量區(qū),p3在棧上。

staticintc=0;全局(靜態(tài))初始化區(qū)

p1=(char*)malloc(10);

p2=(char*)malloc(20);

分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。

strcpy(p1,"123456");123456\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"12345

6"優(yōu)化成一塊。

}

還有就是函數(shù)調(diào)用時(shí)會(huì)在棧上有一系列的保留現(xiàn)場(chǎng)及傳遞參數(shù)的操作。

棧的空間大小有限定,vc的缺省是2M。棧不夠用的情況一般是程序中分配了大量數(shù)組和

遞歸函數(shù)層次太深。有一點(diǎn)必須知道,當(dāng)一個(gè)函數(shù)調(diào)用完返回后它會(huì)釋放該函數(shù)中所有

的棧空間。棧是由編譯器自動(dòng)管理的,不用你操心。

堆是動(dòng)態(tài)分配內(nèi)存的,并且你可以分配使用很大的內(nèi)存。但是用不好會(huì)產(chǎn)生內(nèi)存泄漏。

并且頻繁地malloc和free會(huì)產(chǎn)生內(nèi)存碎片(有點(diǎn)類似磁盤碎片),因?yàn)閏分配動(dòng)態(tài)內(nèi)存時(shí)

是尋找匹配的內(nèi)存的。而用棧則不會(huì)產(chǎn)生碎片。

在棧上存取數(shù)據(jù)比通過指針在堆上存取數(shù)據(jù)快些。

一般大家說的堆棧和棧是一樣的,就是棧(stack),而說堆時(shí)才是堆heap.

棧是先入后出的,一般是由高地址向低地址生長。

堆(heap)和棧(stack)是C/C++/JAVA編程不可避免會(huì)碰到的兩個(gè)基本概念。首先,這兩個(gè)概念都可以在講數(shù)據(jù)結(jié)構(gòu)的書中找到,他們都是基本的數(shù)據(jù)結(jié)構(gòu),雖然棧更為簡(jiǎn)單一些。

在具體的C/C++/JAVA編程框架中,這兩個(gè)概念并不是并行的。對(duì)底層機(jī)器代碼的研究可以揭示,棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),而堆則是C/C++/JAVA函數(shù)庫提供的。

具體地說,現(xiàn)代計(jì)算機(jī)(串行執(zhí)行機(jī)制),都直接在代碼底層支持棧的數(shù)據(jù)結(jié)構(gòu)。這體現(xiàn)

在,有專門的寄存器指向棧所在的地址,有專門的機(jī)器指令完成數(shù)據(jù)入棧出棧的操作。

這種機(jī)制的特點(diǎn)是效率高,支持的數(shù)據(jù)有限,一般是整數(shù),指針,浮點(diǎn)數(shù)等系統(tǒng)直接支

持的數(shù)據(jù)類型,并不直接支持其他的數(shù)據(jù)結(jié)構(gòu)。因?yàn)闂5倪@種特點(diǎn),對(duì)棧的使用在程序

中是非常頻繁的。對(duì)子程序的調(diào)用就是直接利用棧完成的。機(jī)器的call指令里隱含了把

返回地址推入棧,然后跳轉(zhuǎn)至子程序地址的操作,而子程序中的ret指令則隱含從堆棧中

彈出返回地址并跳轉(zhuǎn)之的操作。C/C++/JAVA中的自動(dòng)變量是直接利用棧的例子,這也就是為什么當(dāng)函數(shù)返回時(shí),該函數(shù)的自動(dòng)變量自動(dòng)失效的原因(因?yàn)轭亾Q指戳說饔們暗狀態(tài))。

和棧不同,堆的數(shù)據(jù)結(jié)構(gòu)并不是由系統(tǒng)(無論是機(jī)器系統(tǒng)還是操作系統(tǒng))支持的,而是由

函數(shù)庫提供的?;镜膍alloc/realloc/free函數(shù)維護(hù)了一套內(nèi)部的堆數(shù)據(jù)結(jié)構(gòu)。當(dāng)程序

使用這些函數(shù)去獲得新的內(nèi)存空間時(shí),這套函數(shù)首先試圖從內(nèi)部堆中尋找可用的內(nèi)存空

間,如果沒有可以使用的內(nèi)存空間,則試圖利用系統(tǒng)調(diào)用來動(dòng)態(tài)增加程序數(shù)據(jù)段的內(nèi)存

大小,新分配得到的空間首先被組織進(jìn)內(nèi)部堆中去,然后再以適當(dāng)?shù)男问椒祷亟o調(diào)用者

。當(dāng)程序釋放分配的內(nèi)存空間時(shí),這片內(nèi)存空間被返回內(nèi)部堆結(jié)構(gòu)中,可能會(huì)被適當(dāng)?shù)?/p>

處理(比如和其他空閑空間合并成更大的空閑空間),以更適合下一次內(nèi)存分配申請(qǐng)。這

套復(fù)雜的分配機(jī)制實(shí)際上相當(dāng)于一個(gè)內(nèi)存分配的緩沖池(Cache),使用這套機(jī)制有如下若

干原因:

1.系統(tǒng)調(diào)用可能不支持任意大小的內(nèi)存分配。有些系統(tǒng)的系統(tǒng)調(diào)用只支持固定大小及其

倍數(shù)的內(nèi)存請(qǐng)求(按頁分配);這樣的話對(duì)于大量的小內(nèi)存分類來說會(huì)造成浪費(fèi)。

2.系統(tǒng)調(diào)用申請(qǐng)內(nèi)存可能是代價(jià)昂貴的。系統(tǒng)調(diào)用可能涉及用戶態(tài)和核心態(tài)的轉(zhuǎn)換。

3.沒有管理的內(nèi)存分配在大量復(fù)雜內(nèi)存的分配釋放操作下很容易造成內(nèi)存碎片。

堆和棧的對(duì)比

從以上知識(shí)可知,棧是系統(tǒng)提供的功能,特點(diǎn)是快速高效,缺點(diǎn)是有限制,數(shù)據(jù)不靈活

;而棧是函數(shù)庫提供的功能,特點(diǎn)是靈活方便,數(shù)據(jù)適應(yīng)面廣泛,但是效率有一定降低

。棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),對(duì)于進(jìn)程/線程是唯一的;堆是函數(shù)庫內(nèi)部數(shù)據(jù)結(jié)構(gòu),不一定唯一

。不同堆分配的內(nèi)存無法互相操作。??臻g分靜態(tài)分配和動(dòng)態(tài)分配兩種。靜態(tài)分配是編

譯器完成的,比如自動(dòng)變量(auto)的分配。動(dòng)態(tài)分配由alloc函數(shù)完成。棧的動(dòng)態(tài)分配

無需釋放(是自動(dòng)的),也就沒有釋放函數(shù)。為可移植的程序起見,棧的動(dòng)態(tài)分配操作是

不被鼓勵(lì)的!堆空間的分配總是動(dòng)態(tài)的,雖然程序結(jié)束時(shí)所有的數(shù)據(jù)空間都會(huì)被釋放回

系統(tǒng),但是精確的申請(qǐng)內(nèi)存/釋放內(nèi)存匹配是良好程序的基本要素。

所以計(jì)算機(jī)中的堆和棧經(jīng)常時(shí)放一塊講的

node一般不是必要就不要?jiǎng)討B(tài)創(chuàng)建,最討厭是,C++中,把new出來的東西當(dāng)局部變量用,用完了馬上delete的做法;最惡劣的是,JAVA中不斷地new,然后等著GC來回收。

理由

1.棧分配比堆快,只需要一條指令就呢給配所有的局部變量

2.棧不會(huì)出現(xiàn)內(nèi)存碎片

3.棧對(duì)象好管理

當(dāng)然,某些情況下也要那么寫,比如

1.對(duì)象很大

2.對(duì)象需要在某個(gè)特定的時(shí)刻構(gòu)造或析夠

3.類只允許對(duì)象動(dòng)態(tài)創(chuàng)建,比如VCL的大多數(shù)類

當(dāng)然,必須用堆對(duì)象時(shí)也不能躲避

對(duì)于類的申明(還沒有定義)來說,可以有限的方式使用它。如我們可以聲明指向該類類型的指針或引用。允許指針和引用是因?yàn)樗鼈兌加泄潭ǖ拇笮。c它們指向的對(duì)象的大小無關(guān)。只有到完全定義了該類才能對(duì)這些指針和引用解引用。

只有對(duì)類定義了,才能聲明該類類型對(duì)象。在程序中還沒有看到類定義之前,數(shù)據(jù)成員只能是該類類型的指針或引用。

當(dāng)一個(gè)類的類頭被看到時(shí),它就被視為已經(jīng)聲明了,所以一個(gè)類可以有指向自身類型的指針或引用作為數(shù)據(jù)成員。只有一個(gè)類的類體已經(jīng)完整時(shí),它才被視為已經(jīng)被定義。

所以可以有如下形式:

classLinkScreen{

Screenwindow;

LinkScreen*next;

LinkScreen*prev;

}關(guān)鍵字static的作用是什么?

這個(gè)簡(jiǎn)單的問題很少有人能回答完全。在C語言中,關(guān)鍵字static有三個(gè)明顯的作用:

1).在函數(shù)內(nèi),一個(gè)被聲明為靜態(tài)的變量在這一函數(shù)被調(diào)用過程中維持其值不變。

2).在模塊內(nèi)(但在函數(shù)外),一個(gè)被聲明為靜態(tài)的變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問。它是一個(gè)本地的全局變量。

3).在模塊內(nèi),一個(gè)被聲明為靜態(tài)的函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用。那就是,這個(gè)函數(shù)被限制在聲明它的模塊的本地范圍內(nèi)使用。

大多數(shù)應(yīng)試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個(gè)應(yīng)試者的嚴(yán)重的缺點(diǎn),因?yàn)樗@然不懂得本地化數(shù)據(jù)和代碼范圍的好處和重要性。靜態(tài)變量(1)、靜態(tài)全局變量

在全局變量前,加上關(guān)鍵字static,該變量就被定義成為一個(gè)靜態(tài)全局變量。靜態(tài)全局變量有以下特點(diǎn):

該變量在全局?jǐn)?shù)據(jù)區(qū)分配內(nèi)存;

未經(jīng)初始化的靜態(tài)全局變量會(huì)被程序自動(dòng)初始化為0(自動(dòng)變量的值是隨機(jī)的,除非它被顯式初始化);

靜態(tài)全局變量在聲明它的整個(gè)文件都是可見的,而在文件之外是不可見的;

靜態(tài)變量都在全局?jǐn)?shù)據(jù)區(qū)分配內(nèi)存,包括后面將要提到的靜態(tài)局部變量。對(duì)于一個(gè)完整的程序代碼區(qū)

全局?jǐn)?shù)據(jù)區(qū)

堆區(qū)

棧區(qū)

一般程序的由new產(chǎn)生的動(dòng)態(tài)數(shù)據(jù)存放在堆區(qū),函數(shù)內(nèi)部的自動(dòng)變量存放在棧區(qū)。自動(dòng)變量一般會(huì)隨著函數(shù)的退出而釋放空間,靜態(tài)數(shù)據(jù)(即使是函數(shù)內(nèi)部的靜態(tài)局部變量)也存放在全局?jǐn)?shù)據(jù)區(qū)。全局?jǐn)?shù)據(jù)區(qū)的數(shù)據(jù)并不會(huì)因?yàn)楹瘮?shù)的退出而釋放空間。

的確,定義全局變量就可以實(shí)現(xiàn)變量在文件中的共享,但定義靜態(tài)全局變量還有以下好處:

靜態(tài)全局變量不能被其它文件所用;

其它文件中可以定義相同名字的變量,不會(huì)發(fā)生沖突;

(2)、靜態(tài)局部變量

【一句話解決】靜態(tài)局部變量具有記憶性

在局部變量前,加上關(guān)鍵字static,該變量就被定義成為一個(gè)靜態(tài)局部變量。通常,在函數(shù)體內(nèi)定義了一個(gè)變量,每當(dāng)程序運(yùn)行到該語句時(shí)都會(huì)給該局部變量分配棧內(nèi)存。但隨著程序退出函數(shù)體,系統(tǒng)就會(huì)收回棧內(nèi)存,局部變量也相應(yīng)失效。

但有時(shí)候我們需要在兩次調(diào)用之間對(duì)變量的值進(jìn)行保存。通常的想法是定義一個(gè)全局變量來實(shí)現(xiàn)。但這樣一來,變量已經(jīng)不再屬于函數(shù)本身了,不再僅受函數(shù)的控制,給程序的維護(hù)帶來不便。

靜態(tài)局部變量正好可以解決這個(gè)問題。靜態(tài)局部變量保存在全局?jǐn)?shù)據(jù)區(qū),而不是保存在棧中,每次的值保持到下一次調(diào)用,直到下次賦新值。

example:

voidfoo()

{

staticinta;

a++;

cout<<a<<endl;//C++語法,和C語言中的printf類似

}

intmain()

{

foo();

foo();

foo();

return0;

}

結(jié)果是123每次foo()退出后,并未銷毀變量a,因?yàn)樗谴娣旁谌謹(jǐn)?shù)據(jù)區(qū)的,不是??臻g。

靜態(tài)局部變量有以下特點(diǎn):

該變量在全局?jǐn)?shù)據(jù)區(qū)分配內(nèi)存;

靜態(tài)局部變量在程序執(zhí)行到該對(duì)象的聲明處時(shí)被首次初始化,即以后的函數(shù)調(diào)用不再進(jìn)行初始化;

靜態(tài)局部變量一般在聲明處初始化,如果沒有顯式初始化,會(huì)被程序自動(dòng)初始化為0;

它始終駐留在全局?jǐn)?shù)據(jù)區(qū),直到程序運(yùn)行結(jié)束。但其作用域?yàn)榫植孔饔糜?,?dāng)定義它的函數(shù)或語句塊結(jié)束時(shí),其作用域隨之結(jié)束;關(guān)鍵字const是什么含意?只要一聽到被面試者說:“const意味著常數(shù)”,我就知道我正在和一個(gè)業(yè)余者打交道。

constinta;

intconsta;

constint*a;

int*consta;

intconst*a;

前兩個(gè)的作用是一樣,a是一個(gè)常整型數(shù)。第三個(gè)意味著a是一個(gè)指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個(gè)意思a是一個(gè)指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后一個(gè)意味著a是一個(gè)指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時(shí)指針也是不可修改的)。如果應(yīng)試者能正確回答這些問題,那么他就給我留下了一個(gè)好印象。順帶提一句,也許你可能會(huì)問,即使不用關(guān)鍵字const,也還是能很容易寫出功能正確的程序,那么我為什么還要如此看重關(guān)鍵字const呢?我也如下的幾下理由:

1).關(guān)鍵字const的作用是為給讀你代碼的人傳達(dá)非常有用的信息,實(shí)際上,聲明一個(gè)參數(shù)為常量是為了告訴了用戶這個(gè)參數(shù)的應(yīng)用目的。如果你曾花很多時(shí)間清理其它人留下的垃圾,你就會(huì)很快學(xué)會(huì)感謝這點(diǎn)多余的信息。(當(dāng)然,懂得用const的程序員很少會(huì)留下的垃圾讓別人來清理的。)

2).通過給優(yōu)化器一些附加的信息,使用關(guān)鍵字const也許能產(chǎn)生更緊湊的代碼。

3).合理地使用關(guān)鍵字const可以使編譯器很自然地保護(hù)那些不希望被改變的參數(shù),防止其被無意的代碼修改。簡(jiǎn)而言之,這樣可以減少bug的出現(xiàn)。

關(guān)鍵字volatile有什么含意并給出三個(gè)不同的例子//register

一個(gè)定義為volatile的變量是說這變量可能會(huì)被意想不到地改變,這樣,編譯器就不會(huì)去假設(shè)這個(gè)變量的值了。精確地說就是,優(yōu)化器在用到這個(gè)變量時(shí)必須每次都小心地重新讀取這個(gè)變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個(gè)例子:

1).并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)

2).一個(gè)中斷服務(wù)子程序中會(huì)訪問到的非自動(dòng)變量(Non-automaticvariables)

3).多線程應(yīng)用中被幾個(gè)任務(wù)共享的變量

回答不出這個(gè)問題的人是不會(huì)被雇傭的。我認(rèn)為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。嵌入式系統(tǒng)程序員經(jīng)常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內(nèi)容將會(huì)帶來災(zāi)難。

假設(shè)被面試者正確地回答了這是問題(嗯,懷疑這否會(huì)是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。

1).一個(gè)參數(shù)既可以是const還可以是volatile嗎?解釋為什么。

2).一個(gè)指針可以是volatile嗎?解釋為什么。

3).下面的函數(shù)有什么錯(cuò)誤:

intsquare(volatileint*ptr)

{

return*ptr**ptr;

}

下面是答案:

1).是的。一個(gè)例子是只讀的狀態(tài)寄存器。它是volatile因?yàn)樗赡鼙灰庀氩坏降馗淖儭K莄onst因?yàn)槌绦虿粦?yīng)該試圖去修改它。

2).是的。盡管這并不很常見。一個(gè)例子是當(dāng)一個(gè)中服務(wù)子程序修該一個(gè)指向一個(gè)buffer的指針時(shí)。

3).這段代碼的有個(gè)惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個(gè)volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:

intsquare(volatileint*ptr)

{

inta,b;

a=*ptr;

b=*ptr;

returna*b;

}

由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結(jié)果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

longsquare(volatileint*ptr)

{

inta;

a=*ptr;

returna*a;

}下面的代碼輸出的是什么?為什么?

voidfoo(void)

{

unsignedinta=6;

intb=-20;

(a+b>6)?puts(">6"):puts("<=6");

}

這個(gè)問題測(cè)試你是否懂得C語言中的整數(shù)自動(dòng)轉(zhuǎn)換原則,我發(fā)現(xiàn)有些開發(fā)者懂得極少這些東西。不管如何,這無符號(hào)整型問題的答案是輸出是“>6”。原因是當(dāng)表達(dá)式中存在有符號(hào)類型和無符號(hào)類型時(shí)所有的操作數(shù)都自動(dòng)轉(zhuǎn)換為無符號(hào)類型。因此-20變成了一個(gè)非常大的正整數(shù),所以該表達(dá)式計(jì)算出的結(jié)果大于6。這一點(diǎn)對(duì)于應(yīng)當(dāng)頻繁用到無符號(hào)數(shù)據(jù)類型的嵌入式系統(tǒng)來說是豐常重要的。如果你答錯(cuò)了這個(gè)問題,你也就到了得不到這份工作的邊緣。/u/20090216/20/bfa12d10-4800-4998-987d-31ac21dbc4fd.html

位操作(Bitmanipulation)位操作嵌入式系統(tǒng)總是要用戶對(duì)變量或寄存器進(jìn)行位操作。給定一個(gè)整型變量a,寫兩段代碼,第一個(gè)設(shè)置a的bit3,第二個(gè)清除a的bit3。在以上兩個(gè)操作中,要保持其它位不變。

對(duì)這個(gè)問題有三種基本的反應(yīng)

1).不知道如何下手。該被面者從沒做過任何嵌入式系統(tǒng)的工作。

2).用bitfields。Bitfields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時(shí)也保證了的你的代碼是不可重用的。我最近不幸看到Infineon為其較復(fù)雜的通信芯片寫的驅(qū)動(dòng)程序,它用到了bitfields因此完全對(duì)我無用,因?yàn)槲业木幾g器用其它的方式來實(shí)現(xiàn)bitfields的。從道德講:永遠(yuǎn)不要讓一個(gè)非嵌入式的家伙粘實(shí)際硬件的邊。

3).用#defines和bitmasks操作。這是一個(gè)有極高可移植性的方法,是應(yīng)該被用到的方法。最佳的解決方案如下:

#defineBIT3(0x1<<3)//00001000

staticinta;

voidset_bit3(void)

{

a|=BIT3;

}

voidclear_bit3(void)

{

a&=~BIT3;//10011011&=11110111//a=10010011

}

一些人喜歡為設(shè)置和清除值而定義一個(gè)掩碼同時(shí)定義一些說明常數(shù),這也是可以接受的。我希望看到幾個(gè)要點(diǎn):說明常數(shù)、|=和&=~操作。

訪問固定的內(nèi)存位置(Accessingfixedmemorylocations)

評(píng)價(jià)下面的代碼片斷:

unsignedintzero=0;

unsignedintcompzero=0xFFFF;

/*1'scomplementofzero*/

對(duì)于一個(gè)int型不是16位的處理器為說,上面的代碼是不正確的。應(yīng)編寫如下:

unsignedintcompzero=~0;

這一問題真正能揭露出應(yīng)試者是否懂得處理器字長的重要性。在我的經(jīng)驗(yàn)里,好的嵌入式程序員非常準(zhǔn)確地明白硬件的細(xì)節(jié)和它的局限,然而PC機(jī)程序往往把硬件作為一個(gè)無法避免的煩惱。

到了這個(gè)階段,應(yīng)試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應(yīng)試者不是很好,那么這個(gè)測(cè)試就在這里結(jié)束了。但如果顯然應(yīng)試者做得不錯(cuò),那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優(yōu)秀的應(yīng)試者能做得不錯(cuò)。提出這些問題,我希望更多看到應(yīng)試者應(yīng)付問題的方法,而不是答案。不管如何,你就當(dāng)是這個(gè)娛樂吧…#include<stdio.h>

#include<stdlib.h>

int

main(void)

{

unionA{

chara;

chary:3;

charz:3;

charx:2;

}a;

a.a=0x67;

printf("a.a=%0xa.x=%0x\ta.y=%0x\ta.z=%0x\n",a.a,a.x,a.y,a.z);

return0;

}

結(jié)果

a.a=67a.x=ffffffff

a.y=ffffffffa.z=ffffffff

a.a=64a.x=0a.y=fffffffca.z=fffffffc

a.a=65a.x=1a.y=fffffffda.z=fffffffd

如果單從位域來理解這個(gè)還是簡(jiǎn)單,問題的關(guān)鍵是理解其在計(jì)算機(jī)內(nèi)的存取規(guī)則。

對(duì)a.a=64,單從取位(二進(jìn)制)上可知a.x=00,a.y=101,a.z=101.目前通用計(jì)算機(jī)x86大都是32位機(jī),我的機(jī)器也是32位,在存取上默認(rèn)是存取32位。對(duì)每個(gè)數(shù)而言第一位是符號(hào)位,補(bǔ)碼存儲(chǔ)。那么可以理解a.x的補(bǔ)碼就是00000000,a.y的補(bǔ)碼就是11111100,a.z的補(bǔ)碼就是11111100.這樣看比較自然,但如果輸出結(jié)果是10進(jìn)制,就會(huì)覺得難以理解。當(dāng)然關(guān)鍵還是對(duì)數(shù)據(jù)的存取規(guī)則和編碼的熟悉。

a.a=0x64的10進(jìn)制結(jié)果是a.x=0,a.y=-4,a.z=-4

補(bǔ)充一點(diǎn),union內(nèi)的變量順序?qū)Y(jié)果不影響(每次只可能有一種解釋是合理的,這個(gè)跟struct顯然不同)

2、

關(guān)于位域在結(jié)構(gòu)體的應(yīng)用主要要注意內(nèi)存對(duì)齊規(guī)則的理解和空域的理解

使用位域的主要目的是壓縮存儲(chǔ),其大致規(guī)則為:

1)

如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個(gè)字段存儲(chǔ),直到不能容納為止;

2)

如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲(chǔ)單元開始,其偏移量為其類型大小的整數(shù)倍;

3)

如果相鄰的位域字段的類型不同,則各編譯器的具體實(shí)現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式;

4)

如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮;

5)

整個(gè)結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。

#include<stdio.h>

intmain()

{

union

{

struct

{

unsignedshorts1:3;

unsignedshorts2:3;

unsignedshorts3:3;

}x;

charc;

}v;

v.c=100;

printf("%d\n",sizeof(v));

printf("s1=%d\n",v.x.s1);

printf("s2=%d\n",v.x.s2);

printf("s3=%d\n",v.x.s3);

return0;

}

fc6--linux下gcc-4.1.1

2

s1=4

s2=4

s3=50000000001100100 01100100

windowsxp2下vc6.0

2

s1=4

s2=4

s3=1

可見s3的結(jié)果并不一樣vc6.0的結(jié)果如果只是按位取,就很好理解,這樣跟之前的union的存取規(guī)則又不一樣了~~而對(duì)于gcc-4.1.1,s3=5還沒想出該結(jié)果的原因。同時(shí)考慮

struct{

unsignedshorts1:3;

unsignedshorts2:3;

unsignedshorts3:3;

unsignedshorts4:7;

}x;第三大塊:函數(shù)通過函數(shù)修改變量的值具體面試題示例:如下,通過修改change函數(shù)的定義和調(diào)用,讓第4行執(zhí)行完,a的值編程13,p指向b。voidchange()//第5行,可以改{}voidmain(){inta=10;//第1行,不許改intb=11;//第2行,不許改int*p=&a;//第3行,不許改change();//第4行,可以改}【面試考察點(diǎn)】規(guī)則:如果通過函數(shù)修改一個(gè)變量的值,則需要傳給這個(gè)函數(shù)變量的地址。這里的陷阱是,既要改變變量的值,還要改變指針的值。80%的學(xué)員錯(cuò)在:沒有成功修改指針的值。其實(shí)這很好理解,指針不也是一種變量嗎?voidchange(int**pp,int*p_b)//第5行,可以改{**pp=13;*p=p_b;}voidmain(){inta=10;//第1行,不許改intb=11;//第2行,不許改int*p=&a;//第3行,不許改change(&p,&b);//第4行,可以改}【一句話擺平】通過函數(shù)改變變量的值,則需要傳遞變量的地址(即一級(jí)指針);改變指針的值,則需要傳遞指針的地址(即二級(jí)指針);改變二級(jí)指針的值,則需要傳遞二級(jí)指針的地址(即三級(jí)指針);以此類推。這個(gè)題目是區(qū)分應(yīng)屆生和有編程經(jīng)驗(yàn)人的標(biāo)志。庫函數(shù)實(shí)現(xiàn)這里有三道題,我只重點(diǎn)分析第一道,其它兩題的代碼僅供參考(1).已知strcpy的函數(shù)原型:char*strcpy(char*strDest,constchar*strSrc)其中strDest是目的字符串,strSrc是源字符串。不調(diào)用C++/C的字符串庫函數(shù),請(qǐng)編寫函數(shù)strcpy?!军c(diǎn)評(píng)】此問題或類似問題被問到的頻率極高;首先要全部背上,一個(gè)字不能錯(cuò)(默寫幾遍);其次要徹底理解。答案:【注意點(diǎn)1】有的公司不提供函數(shù)聲明,這里面試官考察兩點(diǎn):其一,是否有const,這涉及到參數(shù)的安全性,是否會(huì)有可能被函數(shù)修改(原則上,不允許被函數(shù)修改的入?yún)?,都需要用const限定?。?;其二,入?yún)⒚欠褚?guī)范,首先從名字能看出誰是目的誰是源頭,其次是從名字能看出是指針。char*strcpy(char*p_dst,constchar*p_src){【注意點(diǎn)2】這是函數(shù)step1:變量定義和賦值。這個(gè)代碼塊的原則是:變量定義放在函數(shù)開始,且要初始化。在本函數(shù)中,先記錄下指針,用來返回用char*p_temp=p_dst;【注意點(diǎn)3】這是函數(shù)step2:入?yún)⒂行詸z查,沒有這一步的人直接被判死刑。這個(gè)代碼塊的原則是:所有有可能的風(fēng)險(xiǎn)全部檢測(cè)一遍,能發(fā)現(xiàn)多少就多少在本函數(shù)中,本代碼塊須判斷是否為空和地址是否重疊。判斷為空時(shí)NULL放在等于號(hào)的前面,這是良好的工程習(xí)慣,常量在前。if(NULL==p_dst||NULL==p_src){returnNULL;}if(p_dst==p_src){returnp_dst;}【注意點(diǎn)4】這是函數(shù)step3:業(yè)務(wù)功能代碼塊。這是函數(shù)的功能主體。這個(gè)代碼塊的原則是:用簡(jiǎn)潔易懂的語言來實(shí)現(xiàn)業(yè)務(wù)功能,且無漏洞在本函數(shù)中,本代碼塊考察核心是:p_dst最后是否以‘\0’結(jié)尾。盡管代碼寫的沒有突出這一點(diǎn),但應(yīng)聘時(shí)需要記?。核凶址僮?,都不要忘記結(jié)尾的‘\0’!很多學(xué)員面試卡在這個(gè)地方而不自知! while((*p_dst++=*p_src++)!=‘\0’);【注意點(diǎn)5】這是函數(shù)step4:結(jié)尾處理。這個(gè)代碼塊的原則是:函數(shù)功能已實(shí)現(xiàn)完畢,如何正確地收尾在本函數(shù)中,本代碼塊就一句話,返回正確的地址。returnp_temp;}(2).已知memcpy的函數(shù)原型:void*memcpy(void*dest,void*src,unsignedintcount);其中dest是目的地址,src是源地址,count是拷貝內(nèi)存長度。void*memcpy(void*dst,constvoid*src,unsignedintlen){registerchar*d;registerchar*s;if(len==0)returndst;if(is_overlap(dst,src,len,len))complain3("memcpy",dst,src,len);if(dst>src){d=(char*)dst+len-1;s=(char*)src+len-1;while(len>=4){*d--=*s--;*d--=*s--;*d--=*s--;*d--=*s--;len-=4;}while(len--){*d--=*s--;}}elseif(dst<src){d=(char*)dst;s=(char*)src;while(len>=4){*d++=*s++;*d++=*s++;*d++=*s++;*d++=*s++;len-=4;}while(len--){*d++=*s++;}}returndst;}(3).已知atoi的函數(shù)原型:intatoi(constchar*string);其中string是數(shù)字字符串。intatoi(charconst*string)

{

intvalue;

value=0;while(*string>='0'&&*string<='9'){

value*=10;value+=*string-'0';

++string;}

if(*string!='0')

value=0;

returnvalue;}第四大塊:指針&內(nèi)存指針的身份要明確,這是前提用變量a給出下面的定義:

a)一個(gè)整型數(shù)(Aninteger)

b)一個(gè)指向整型數(shù)的指針(Apointertoaninteger)

c)一個(gè)指向指針的的指針,它指向的指針是指向一個(gè)整型數(shù)(Apointertoapointertoaninteger)

d)一個(gè)有10個(gè)整型數(shù)的數(shù)組(Anarrayof10integers)

e)一個(gè)有10個(gè)指針的數(shù)組,該指針是指向一個(gè)整型數(shù)的(Anarrayof10pointerstointegers)

f)一個(gè)指向有10個(gè)整型數(shù)數(shù)組的指針(Apointertoanarrayof10integers)

g)一個(gè)指向函數(shù)的指針,該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(shù)(Apointertoafunctionthattakesanintegerasanargumentandreturnsaninteger)

h)一個(gè)有10個(gè)指針的數(shù)組,該指針指向一個(gè)函數(shù),該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(shù)(Anarrayoftenpointerstofunctionsthattakeanintegerargumentandreturnaninteger)

答案是:

a)inta;//Aninteger

b)int*a;//Apointertoaninteger

c)int**a;//Apointertoapointertoaninteger

d)inta[10];//Anarrayof10integers

e)int*a[10];//Anarrayof10pointerstointegers

f)int(*a)[10];//Apointertoanarrayof10integers

g)int(*a)(int);//Apointertoafunctionathattakesanintegerargumentandreturnsaninteger

h)int(*a[10])(int);//Anarrayof10pointerstofunctionsthattakeanintegerargumentandreturnaninteger

人們經(jīng)常聲稱這里有幾個(gè)問題是那種要翻一下書才能回答的問題,我同意這種說法。當(dāng)我寫這篇文章時(shí),為了確定語法的正確性,我的確查了一下書。

但是當(dāng)我被面試的時(shí)候,我期望被問到這個(gè)問題(或者相近的問題)。因?yàn)樵诒幻嬖嚨倪@段時(shí)間里,我確定我知道這個(gè)問題的答案。應(yīng)試者如果不知道所有的答案(或至少大部分答案),那么也就沒有為這次面試做準(zhǔn)備,如果該面試者沒有為這次面試做準(zhǔn)備,那么他又能為什么出準(zhǔn)備呢?

【面試點(diǎn)評(píng)】在嚴(yán)格的面試官面前,僅僅知道這些“是什么”還遠(yuǎn)遠(yuǎn)不夠,因?yàn)楹?jiǎn)單的記憶大部分人都能答出來,還停留在“變量”那個(gè)層次,還不是指針??赡苓€會(huì)追問如下問題(以函數(shù)指針為例):追問1:如何對(duì)它們進(jìn)行賦值?intfunc(int);int(*p_func)(int);p_func=func;//分析:函數(shù)名本身是個(gè)地址;指針也是個(gè)地址,因此可以賦值。有人寫作p_func=&func,別這么寫。因?yàn)槟闳ッ嬖囀菫榱俗C明你這個(gè)知識(shí)點(diǎn)我懂、能編程干活即可,而不是記憶很多自己不好理解的、真正面試時(shí)自己容易全混淆了的東東。追問2:如何通過它來調(diào)用函數(shù)?p_func(10);//分析:既然p_func就是func賦值來的,當(dāng)然可以替代。有人寫作*p_func(10),同理,別這么寫。

【函數(shù)指針分析】

一個(gè)面試題是:隨便寫一個(gè)函數(shù),然后在寫一個(gè)函數(shù)指針指向它。范題:為如下的函數(shù)寫出相應(yīng)的指針voidfunA();int*funB(inttemp);constint*funC(int*p_temp,int(*p)())答案:void(*p_funA)();int*(*p_funB)(inttemp);constint*(*p_funC)(int*p_temp,int(*p)())有沒有發(fā)現(xiàn)規(guī)律?C語言是一個(gè)結(jié)構(gòu)性語言,等級(jí)森嚴(yán);同時(shí)也是一個(gè)形而上的語言,套用結(jié)構(gòu)即可。如果你還沒有對(duì)比發(fā)現(xiàn)出規(guī)律,那說明你沒有在認(rèn)真準(zhǔn)備面試,而是在急躁地背題目,真正面試機(jī)會(huì)來了你也很可能把握不住。規(guī)律就是,把“函數(shù)名”換成“*p”即可,其它的任何地方都不要?jiǎng)?。【結(jié)構(gòu)分析】首先找到變量或函數(shù)名,分別是funA,funB,funC;然后看這些變量名先和誰結(jié)合?規(guī)則:同一優(yōu)先級(jí)下,右結(jié)合原則。2)中的funB先和“()”結(jié)合,所以身份明確,是個(gè)函數(shù)(帶括號(hào)的一定和函數(shù)有關(guān));那么,是個(gè)什么樣的函數(shù)呢?“int*”表明,這是一個(gè)返回int型指針的函數(shù);5)中的p_funB,因?yàn)槔ㄌ?hào)的關(guān)系,先和前面的“*”結(jié)合,所以身份明確,是個(gè)指針;那么,是個(gè)什么樣的指針呢?后面的括號(hào)表明,這是一個(gè)指向函數(shù)的指針;那么,指向什么樣的函數(shù)呢?“int*”表明,這個(gè)函數(shù)返回int型指針。上門的h)同樣分析:帶”[]”一定和數(shù)組有關(guān)系,大家做類型的推演,然后寫代碼在機(jī)器上跑一下“定義、賦值、調(diào)用”三步驟。什么是回調(diào)函數(shù)。如何使用,使用的特點(diǎn)。數(shù)組和指針inta[5]={1,2,3,4,5};int*p1=(int*)(&a6+1);int*p2=(int*)(a+1);printf(“%d”,*(p1-1));

printf(“%d”,*(p2-1));程序運(yùn)行結(jié)果?a是數(shù)組首元素的首地址,其類型為int[5],&a為數(shù)組的首地址,類型為int(*)[5]。a的值==&a的值,他們都表示一個(gè)地址。

但是請(qǐng)注意區(qū)分他們的類型。

&a+1,向后偏了sizeof(a)那么多bytes

而a+1,指向后便宜了sizeof(int)bytes內(nèi)存空間計(jì)算:(1)、charstr[]=“Hello”;char*p=str;intn=10;//請(qǐng)計(jì)算sizeof(str)=6sizeof(p)=4sizeof(n)= 4strlen(str)= 5strlen(p)= 5(2)、voidFunc(charstr[100]){//請(qǐng)計(jì)算sizeof(str)=4}(3)、void*p=malloc(100);//請(qǐng)計(jì)算sizeof(p)= 4幾道經(jīng)典面試題,用了十幾年了:(1)、voidGetMemory(char*p){p=(char*)malloc(100);}voidTest(void){char*str=NULL;GetMemory(str); strcpy(str,"helloworld");printf(str);}請(qǐng)問運(yùn)行Test函數(shù)會(huì)有什么樣的結(jié)果?答:程序崩潰。因?yàn)镚etMemory并不能傳遞動(dòng)態(tài)內(nèi)存,Test函數(shù)中的str一直都是NULL。strcpy(str,"helloworld");將使程序崩潰。(2)、voidGetMemory(char**p,intnum){*p=(char*)malloc(num);}voidTest(void){char*str=NULL;GetMemory(&str,100);strcpy(str,"hello"); printf(str); }請(qǐng)問運(yùn)行Test函數(shù)會(huì)有什么樣的結(jié)果?答:(1)能夠輸出hello(2)內(nèi)存泄漏(3)、char*GetMemory(void){ charp[]="helloworld";returnp;}voidTest(void){char*str=NULL;str=GetMemory(); printf(str);}請(qǐng)問運(yùn)行Test函數(shù)會(huì)有什么樣的結(jié)果?答:可能是亂碼。因?yàn)镚etMemory返回的是指向“棧內(nèi)存”的指針,該指針的地址不是NULL,但其原現(xiàn)的內(nèi)容已經(jīng)被清除,新內(nèi)容不可知。(4)、voidTest(void){char*str=(char*)malloc(100); strcpy(str,“hello”); free(str); if(str!=NULL) { strcpy(str,“world”); printf(str);}}請(qǐng)問運(yùn)行Test函數(shù)會(huì)有什么樣的結(jié)果?答:篡改動(dòng)態(tài)內(nèi)存區(qū)的內(nèi)容,后果難以預(yù)料,非常危險(xiǎn)。因?yàn)閒ree(str);之后,str成為野指針,if(str!=NULL)語句不起作用。認(rèn)識(shí)什么是字符串嗎?這道題沒有一眼看出來,說明這一塊你很危險(xiǎn)intmian(){ charstr[10]; charstr2[10];inti;for(i=0;i<10;i++){ str[i]=‘a(chǎn)’+i;}strcpy(str2,str);}請(qǐng)問運(yùn)行程序會(huì)有什么樣的結(jié)果?指針和地址,你理解嗎?嵌入式系統(tǒng)經(jīng)常具有要求程序員去訪問某特定的內(nèi)存位置的特點(diǎn)。在某工程中,要求設(shè)置一絕對(duì)地址為0x67a9的整型變量的值為0xaa66。編譯器是一個(gè)純粹的ANSI編譯器。寫代碼去完成這一任務(wù)。

這一問題測(cè)試你是否知道為了訪問一絕對(duì)地址把一個(gè)整型數(shù)強(qiáng)制轉(zhuǎn)換(typecast)為一指針是合法的。這一問題的實(shí)現(xiàn)方式隨著個(gè)人風(fēng)格不同而不同。典型的類似代碼如下:

int*ptr;

ptr=(int*)0x67a9;

*ptr=0xaa55;

一個(gè)較晦澀的方法是:

*(int*const)(0x67a9)=0xaa55;

即使你的品味更接近第二種方案,但我建議你在面試時(shí)使用第一種方案。盡管不像非嵌入式計(jì)算機(jī)那么常見,嵌入式系統(tǒng)還是有從堆(heap)中動(dòng)態(tài)分配內(nèi)存的過程的。那么嵌入式系統(tǒng)中,動(dòng)態(tài)分配內(nèi)存可能發(fā)生的問題是什么?這里,我期望應(yīng)試者能提到內(nèi)存碎片,碎片收集的問題,變量的持行時(shí)間等等。這個(gè)主題已經(jīng)在ESP雜志中被廣泛地討論過了(主要是P.J.Plauger,他的解釋遠(yuǎn)遠(yuǎn)超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應(yīng)試者進(jìn)入一種虛假的安全感覺后,我拿出這么一個(gè)小節(jié)目:下面的代碼片段的輸出是什么,為什么?

char*ptr;

if((ptr=(char*)malloc(0))==NULL)

puts("Gotanullpointer");

else

puts("Gotavalidpointer");

這是一個(gè)有趣的問題。最近在我的一個(gè)同事不經(jīng)意把0值傳給了函數(shù)malloc,得到了一個(gè)合法的指針之后,我才想到這個(gè)問題。這就是上面的代碼,該代碼的輸出是“Gotavalidpointer”。我用這個(gè)來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。第五大塊:鏈表設(shè)計(jì)一個(gè)用鏈表表示的直接插入排序算法typedefstructnode{intkey;structnode*next;}NODE;sort(NODE*h){NODE*p,*h1,*t,*q;h1=h->next->next;h->next->next=null;while(h1!=null){t=h1;h1=h1->next;q=h;p=h->next;while(p->num>t->num&&p!=null){q=p;p=p->next;}t->next=p;q->next=t;}}鏈表題:一個(gè)鏈表的結(jié)點(diǎn)結(jié)構(gòu)structNode{intdata;Node*next;};typedefstructNodeNode;(1)已知鏈表的頭結(jié)點(diǎn)head,寫一個(gè)函數(shù)把這個(gè)鏈表逆序(Intel)Node*ReverseList(Node*head)//鏈表逆序{if(head==NULL||head->next==NULL)returnhead;Node*p1=head;Node*p2=p1->next;Node*p3=p2->next;p1->next=NULL;while(p3!=NULL){p2->next=p1;p1=p2;p2=p3;p3=p3->next;}p2->next=p1;head=p2;returnhead;}(2)已知兩個(gè)鏈表head1和head2各自有序,請(qǐng)把它們合并成一個(gè)鏈表依然有序。(保留所有結(jié)點(diǎn),即便大小相同)Node*Merge(Node*head1,Node*head2){if(head1==NULL)returnhead2;if(head2==NULL)returnhead1;Node*head=NULL;Node*p1=NULL;Node*p2=NULL;if(head1->data<head2->data){head=head1;p1=head1->next;p2=head2;}else{head=head2;p2=head2->next;p1=head1;}Node*pcurrent=head;while(p1!=NULL&&p2!=NULL){if(p1->data<=p2->data){pcurrent->next=p1;pcurrent=p1;p1=p1->next;}else{pcurrent->next=p2;pcurrent=p2;p2=p2->next;}}if(p1!=NULL)pcurrent->next=p1;if(p2!=NULL)pcurrent->next=p2;returnhead;}(3)已知兩個(gè)鏈表head1和head2各自有序,請(qǐng)把它們合并成一個(gè)鏈表依然有序,這次要求用遞歸方法進(jìn)行。(Autodesk)答案:Node*MergeRecursive(Node*head1,Node*head2){if(head1==NULL)returnhead2;if(head2==NULL)returnhead1;Node*head=NULL;if(head1->data<head2->data){head=head1;head->next=MergeRecursive(head1->next,head2);}else{head=head2;head->next=MergeRecursive(head1,head2->next);}returnhead;}(4)單鏈表刪除:假設(shè)表中有數(shù)據(jù)281169,題目是:找到數(shù)據(jù)為11的節(jié)點(diǎn)并將之從鏈表中刪除;【題目誤區(qū)】這里要注意,只能用一次循環(huán)!如果用了兩次,那就被面試官判為錯(cuò)誤如何判斷一個(gè)單鏈表是有環(huán)的?(注意不能用標(biāo)志位,最多只能用兩個(gè)額外指針)structnode{charval;node*next;}boolcheck(constnode*head){}//returnfalse:無環(huán);true:有環(huán)一種O(n)的辦法就是(搞兩個(gè)指針,一個(gè)每次遞增一步,一個(gè)每次遞增兩步,如果有環(huán)的話兩者必然重合,反之亦然):boolcheck(constnode*head){if(head==NULL)returnfalse;node*low=head,*fast=head->next;while(fast!=NULL&&fast->next!=NULL){low=low->next;fast=fast->next->next;if(low==fast)returntrue;}returnfalse;}雙向鏈表的刪除結(jié)點(diǎn)第六大塊:算法寫在具體之前:我們一個(gè)學(xué)員去面試,面試官問:你的項(xiàng)目中用到算法了嗎?他想了想說,沒有。結(jié)果可想而知。后來我問他,你《XXX管理系統(tǒng)中》,沒有排序嗎?他說有啊,我說那里面沒有用到算法嗎?他說,冒泡是算法嗎?無語?。。 疽痪湓挃[平】所有排序、查找,必然用到算法查找算法#include<stdio.h>#include<stdlib.h>intMAX=100000;/*二分查找*/intbs(intdata[],intdvalue){intl,u,m;intp;l=0;u=MAX;for(;;){if(l>u)return-1;m=(l+u)/2;if(data[m]==dvalue){returnm;}elseif(data[m]>dvalue){u=m-1;}else{l=m+1;}}}/*順序查找*/intss(intdata[],intdvalue){inti;//intl=len(data);for(i=0;i<MAX;i++){//printf("%d\n",data[i]);if(data[i]==dvalue){returni;}}return-1;}intmain(){//intMAX=1000;intdata[MAX];inti;//給數(shù)組賦值for(i=0;i<MAX;i++){data[i]=i*10;}//要查找的值intvalue=300;//順序查找ints=ss(data,value);printf("%d\n",s);//二分查找intd=bs(data,value);printf("%d\n",d);return0;}快速排序編寫算法判斷二叉樹是否是完全二叉樹(此題看情況)分析:完全二叉樹是指在一棵二叉樹中除最后一層外,其余層都是滿的,并且最后一層或者是滿的,或者在右邊缺少連續(xù)若干結(jié)點(diǎn)。要判定一棵二叉樹是否完全二叉樹,應(yīng)先建立一棵二叉樹,此例采用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的先序算法建立一棵二叉樹。判定一棵二叉樹是否是完全二叉樹,可以使用隊(duì)列,在層次遍歷的過程中利用完全二叉樹“若某結(jié)點(diǎn)無左孩子,就一定沒有右孩子”的原則進(jìn)行判斷。答案:#include<stdio.h>typedefcharElementType;typedefstructnode{ElementTypedata;structnode*LChild,*RChild;}BinNode,*BinTree;voidCreateBinTree(BinTree*root){charch;ch=getchar();if(ch==’#’)*root=NULL;else{*root=(BinTree)malloc(sizeof(BinNode));(*root)->data=ch;CreateBinTree(&((*root)->LChild));CreateBinTree(&((*root)->RChild));}}intJudgeComplete(BinTreebt)/*判斷二叉樹是否是完全二叉樹,是返回1,不是返回0*/{inttag=0,front,rear;BinTreep=bt,Q[50]; /*Q是隊(duì)列,元素是二叉樹結(jié)點(diǎn)指針,容量足夠大*/if(p==NULL)return1;front=rear=0;Q[++rear]=p;/*初始化隊(duì)伍,根結(jié)點(diǎn)指針入隊(duì)*/while(front!=rear){p=Q[++front];if(p->LChild&&!tag)Q[++rear]=p->LChild; /*左孩子入隊(duì)*/elseif(p->LChild)return0;/*前邊已有結(jié)點(diǎn)為空,本結(jié)點(diǎn)不空*/elsetag=1; /*首次出現(xiàn)結(jié)構(gòu)為空*/if(p->RChild&&!tag)Q[++rear]=p->RChild; /*右孩子入隊(duì)*/elseif(p->RChild)return0;elsetag=1;}return1;}main(){inty;BinTreebt;printf("請(qǐng)輸入結(jié)點(diǎn)數(shù)據(jù):");CreateBinTree(&bt);y=JudgeComplete(bt);if(y==1)printf("該二叉樹是完全二叉樹\n");elseprintf("該二叉樹不是完全二叉樹\n");}已知數(shù)據(jù)序列為(12,5,9,20,6,31,24),對(duì)該數(shù)據(jù)序列進(jìn)行排序,試寫出插入排序和冒泡排序unsignedintintvert(unsignedintx,intp,intn)實(shí)現(xiàn)對(duì)x的進(jìn)行轉(zhuǎn)換,p為起始轉(zhuǎn)化位,n為需要轉(zhuǎn)換的長度,假設(shè)起始點(diǎn)在右邊.如x=0b00010001,p=4,n=3轉(zhuǎn)換后x=0b01100001unsignedintintvert(unsignedintx,intp,intn){unsignedint_t=0;unsignedint_a=1;for(inti=0;i<n;++i){_t|=_a;_a=_a<<1;}_t=_t<<p;x^=_t;returnx;}

單詞反轉(zhuǎn)如:thisisapig反轉(zhuǎn)后:pigaisthis寫一個(gè)函數(shù)實(shí)現(xiàn)該反轉(zhuǎn)。【點(diǎn)評(píng)】要求不能增加復(fù)雜度寫一個(gè)程序,判斷運(yùn)行程序的系統(tǒng)的是大字節(jié)序還是小字節(jié)序?寫函數(shù)實(shí)現(xiàn)大小字節(jié)序轉(zhuǎn)換。

voidcheckSystem(){unioncheck{inti;charch;}c;c.i=1;if((char)1==c.ch)printf("系統(tǒng)是小字節(jié)序\n");elseprintf("系統(tǒng)是大字節(jié)序\n");}intmain(){checkSystem();}用C語言完成以下這個(gè)子程序,要求:1,定義一個(gè)一維數(shù)組,數(shù)組大小為24。2,產(chǎn)生0-23的隨機(jī)數(shù)。3,將產(chǎn)生的隨機(jī)數(shù)存入數(shù)組,要求數(shù)組中的每個(gè)數(shù)據(jù)不能相同。4,補(bǔ)充說明:這個(gè)子程序要求每次調(diào)用后,這個(gè)數(shù)組里面就存放了0-23這些數(shù)據(jù),而且這些數(shù)據(jù)沒有重復(fù)的。5,注意,C語言有隨機(jī)數(shù)函數(shù),可以用函數(shù)產(chǎn)生隨機(jī)數(shù)。RAND(N)voidRandArrayN(int*a,intn){inti,t,;n=n-1;for(i=0;i<=n;i++)a[i]=0;//全部清零,還沒有置數(shù)的狀態(tài)。for(i=0;i<n;i++){//只循環(huán)了n-1次,每次置入的數(shù)據(jù)分別為n-1,n-2,...,1,最后剩余未置(非零)數(shù)的那個(gè)位置就本身已經(jīng)置入0了。while(a[t=rand(n)]!=0);//每次找一個(gè)未置數(shù)位置//若該位置已經(jīng)放下一個(gè)數(shù)(不等于0),則找下一個(gè)位置,直到找到為0的位置(還沒有置入一個(gè)數(shù))a[t]=n-i;//第i次找到的位置置入數(shù)n-i,}}voidmain(){inta[N];RandArrayN(a,N);}intmain(){intt,i;inta[24];memset(a,0,sizeof(a));srand((int)time(0));for(i=0;i<=22;i++){while(a[t=rand()%23]!=0);a[t]=23-i;}for(i=0;i<24;i++){printf("%d",a[i]);}}有一個(gè)數(shù)組a[1000]存放0--1000;要求每隔二個(gè)數(shù)刪掉一個(gè)數(shù),到末尾時(shí)循環(huán)至開頭繼續(xù)進(jìn)行,求最后一個(gè)被刪掉的數(shù)的原始下標(biāo)位置。以7個(gè)數(shù)為例:{0,1,2,3,4,5,6,7}0-->1-->2(刪除)-->3-->4-->5(刪除)-->6-->7-->0(刪除),如此循環(huán)直到最后一個(gè)數(shù)被刪除。方法1:數(shù)組#includeusingnamespacestd;#definenull1000intmain(){intarr[1000];for(inti=0;i<1000;++i)arr[i]=i;intj=0;intcount=0;while(count<999){while(arr[j%1000]==null)j=(++j)%1000;j=(++j)%1000;while(arr[j%1000]==null)j=(++j)%1000;j=(++j)%1000;while(arr[j%1000]==null)j=(++j)%1000;arr[j]=null;++count;}while(arr[j]==null)j=(++j)%1000;cout<<<p>return0;}方法2:鏈表#includeusingnamespacestd;#definenull0structnode{intdata;node*next;};intmain(){node*head=newnode;head->data=0;head->next=null;node*p=head;for(inti=1;i<

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論