深圳一家做ARM嵌入式開發的面(mian)試題
時間:2016-05-31 來源:未知
上(shang)個星(xing)期,去(qu)深圳(zhen)一(yi)家(jia)搞(gao)ARM開發的公(gong)司面試(shi),HR叫我做了一(yi)份(fen)卷子,里面都(dou)(dou)是C編程,心中暗(an)喜,因(yin)為(wei)這(zhe)(zhe)些題基本上(shang)都(dou)(dou)在程序員面試(shi)寶典里見過。后來回到(dao)學校,在網上(shang)搜索,原來這(zhe)(zhe)些題都(dou)(dou)是嵌入式工程師的經(jing)典面試(shi)題目,很多(duo)網站(zhan)上(shang)都(dou)(dou)可以找得到(dao)。現把他貼出來,附上(shang)網上(shang)的答案,跟大家(jia)分享(xiang),因(yin)為(wei)這(zhe)(zhe)些題實(shi)在太經(jing)典了。
預處理器(Preprocessor)
1 . 用(yong)預處理(li)指(zhi)令#define 聲明一(yi)個常數,用(yong)以表明1年中(zhong)有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在(zai)這想看到(dao)幾件事情:
1) #define 語法(fa)的基本(ben)知識(例如(ru):不(bu)能以分號(hao)(hao)結束,括號(hao)(hao)的使用,等等)
2) 懂得預處理(li)器將為你(ni)計(ji)算常數表達式(shi)的(de)值,因此直接寫出(chu)你(ni)如何計(ji)算一年中有多少秒而不(bu)是(shi)計(ji)算出(chu)實(shi)際的(de)值,是(shi)更清晰(xi)而沒(mei)有代價的(de)。
3) 意識到這個(ge)表達式將使一個(ge)16位(wei)機的(de)整型數溢(yi)出-因此要用到長整型符號L,告(gao)訴編(bian)譯器這個(ge)常數是的(de)長整型數。
4) 如果你(ni)在你(ni)的表達(da)式中用到UL(表示無(wu)符號(hao)長(chang)整(zheng)型),那么(me)你(ni)有了一個好的起點。記住,第一印象很重要。
2 . 寫(xie)一個(ge)"標準"宏(hong)MIN ,這個(ge)宏(hong)輸入(ru)兩個(ge)參數并返回較小(xiao)的一個(ge)。
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
這個測試是為下面的(de)目(mu)的(de)而設(she)的(de):
1) 標(biao)識#define在(zai)宏中應用的(de)(de)基本(ben)知識。這(zhe)是(shi)很重(zhong)要(yao)的(de)(de)。因為(wei)在(zai) 嵌入(inline)操作(zuo)符 變為(wei)標(biao)準C的(de)(de)一部分之前,宏是(shi)方(fang)(fang)便產(chan)生嵌入代碼的(de)(de)唯一方(fang)(fang)法,對于嵌入式(shi)系統(tong)來說,為(wei)了能達到要(yao)求的(de)(de)性能,嵌入代碼經常是(shi)必須的(de)(de)方(fang)(fang)法。
2) 三(san)重條(tiao)件操作符的(de)(de)(de)知識。這(zhe)(zhe)個操作符存(cun)在C語言中的(de)(de)(de)原因是它使得編譯器(qi)能產生比if-then-else更優的(de)(de)(de)代碼,了解(jie)這(zhe)(zhe)個用法(fa)是很重要的(de)(de)(de)。
3) 懂得在宏(hong)中小心地把參數(shu)用括號括起來(lai)
4) 我也用這個問(wen)題開(kai)始(shi)討論宏(hong)的副(fu)作用,例如:當你寫下面(mian)的代碼時會(hui)發生什么事(shi)?
least = MIN(*p++, b);
3. 預處理器標識#error的目(mu)的是什么?
如(ru)果(guo)你不(bu)知道答(da)案,請(qing)看參考文(wen)獻1。這(zhe)問題對區分(fen)一個(ge)(ge)正常的(de)(de)伙計和一個(ge)(ge)書呆子(zi)是很有用的(de)(de)。只(zhi)有書呆子(zi)才會讀(du)C語(yu)言課本的(de)(de)附錄去找出象這(zhe)種問題的(de)(de)答(da)案。當然如(ru)果(guo)你不(bu)是在找一個(ge)(ge)書呆子(zi),那么應試(shi)者好希望(wang)自己不(bu)要知道答(da)案。
死(si)循環(Infinite loops)
4. 嵌入式系統(tong)中經(jing)常要用(yong)到(dao)無限(xian)循(xun)環,你(ni)怎(zen)么樣用(yong)C編寫死循(xun)環呢(ni)?
這個問(wen)題(ti)用幾個解決方案。我首選的方案是:
while(1)
{
}
一些程序員更喜歡(huan)如下方案:
for(;;)
{
}
這(zhe)個(ge)實現方(fang)式(shi)讓我為難,因為這(zhe)個(ge)語法沒有確(que)切表達到(dao)底怎么(me)(me)回事(shi)。如果一個(ge)應(ying)試(shi)者給出(chu)這(zhe)個(ge)作(zuo)為方(fang)案(an),我將用這(zhe)個(ge)作(zuo)為一個(ge)機(ji)會去(qu)探究(jiu)他們這(zhe)樣做的(de)基本原理。如果他們的(de)基本答案(an)是(shi):"我被教著這(zhe)樣做,但從沒有想到(dao)過為什么(me)(me)。"這(zhe)會給我留下一個(ge)壞印象。
第三個方案是(shi)用 goto
Loop:
...
goto Loop;
應試者(zhe)如(ru)給出上面的方案,這(zhe)說明或者(zhe)他是(shi)(shi)一個匯(hui)編語(yu)言(yan)程序(xu)(xu)員(這(zhe)也許是(shi)(shi)好事(shi))或者(zhe)他是(shi)(shi)一個想進入新領域的BASIC/FORTRAN程序(xu)(xu)員。
數據聲明(Data declarations)
5. 用變量a給出下面的定義(yi)
a) 一個整型數(An integer)
b)一個指向(xiang)整(zheng)型數的指針( A pointer to an integer)
c)一(yi)個指向(xiang)(xiang)指針的(de)的(de)指針,它(ta)指向(xiang)(xiang)的(de)指針是指向(xiang)(xiang)一(yi)個整型數(shu)( A pointer to a pointer to an intege)r
d)一個有10個整型數(shu)的數(shu)組( An array of 10 integers)
e) 一(yi)個有10個指針的(de)數組,該指針是指向(xiang)一(yi)個整(zheng)型數的(de)。(An array of 10 pointers to integers)
f) 一個指(zhi)向有10個整型數(shu)(shu)數(shu)(shu)組(zu)的指(zhi)針( A pointer to an array of 10 integers)
g) 一個(ge)指向(xiang)函數(shu)的指針(zhen),該函數(shu)有一個(ge)整型參數(shu)并返回一個(ge)整型數(shu)(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一(yi)個(ge)有10個(ge)指(zhi)針(zhen)的數(shu)組,該指(zhi)針(zhen)指(zhi)向一(yi)個(ge)函數(shu),該函數(shu)有一(yi)個(ge)整型參數(shu)并返回一(yi)個(ge)整型數(shu)( An array of ten pointers to functions that take an integer argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人們(men)經常聲稱這(zhe)里有幾個(ge)問題是那(nei)種要翻一(yi)下(xia)書才能回(hui)答的(de)(de)(de)問題,我(wo)(wo)同(tong)意(yi)這(zhe)種說法。當我(wo)(wo)寫這(zhe)篇文章(zhang)時,為(wei)了(le)確(que)(que)定(ding)語法的(de)(de)(de)正確(que)(que)性,我(wo)(wo)的(de)(de)(de)確(que)(que)查了(le)一(yi)下(xia)書。但是當我(wo)(wo)被面試的(de)(de)(de)時候(hou),我(wo)(wo)期望被問到這(zhe)個(ge)問題(或(huo)(huo)者(zhe)相近的(de)(de)(de)問題)。因為(wei)在被面試的(de)(de)(de)這(zhe)段時間里,我(wo)(wo)確(que)(que)定(ding)我(wo)(wo)知(zhi)道這(zhe)個(ge)問題的(de)(de)(de)答案。應(ying)試者(zhe)如(ru)果不知(zhi)道所有的(de)(de)(de)答案(或(huo)(huo)至(zhi)少(shao)大部分答案),那(nei)么也就沒有為(wei)這(zhe)次面試做準備(bei),如(ru)果該面試者(zhe)沒有為(wei)這(zhe)次面試做準備(bei),那(nei)么他又能為(wei)什么出準備(bei)呢?
Static
6. 關鍵字static的(de)作用(yong)是什么? 這個簡單的(de)問(wen)題(ti)很少有人能回(hui)答(da)完全(quan)。在C語言中,關鍵字static有三個明顯的(de)作用(yong):
1) 在(zai)函數(shu)體,一個被聲明(ming)為靜態(tai)的變(bian)量在(zai)這一函數(shu)被調(diao)用過程中維持其(qi)值不(bu)變(bian)。
2) 在(zai)模塊內(但在(zai)函數(shu)體(ti)外),一(yi)個被(bei)聲明(ming)為靜態的變(bian)(bian)量可以被(bei)模塊內所(suo)用函數(shu)訪問,但不能被(bei)模塊外其它(ta)函數(shu)訪問。它(ta)是一(yi)個本地的全局變(bian)(bian)量。
3) 在模塊內(nei)(nei),一(yi)個被聲明為(wei)靜態的函(han)數只(zhi)可(ke)被這一(yi)模塊內(nei)(nei)的其它函(han)數調用。那(nei)就是,這個函(han)數被限制在聲明它的模塊的本地(di)范圍內(nei)(nei)使用。
大(da)多數(shu)(shu)應試(shi)者(zhe)能(neng)正(zheng)確回答第一部分,一部分能(neng)正(zheng)確回答第二(er)部分,同是(shi)很少的(de)人能(neng)懂(dong)得第三(san)部分。這是(shi)一個應試(shi)者(zhe)的(de)嚴重的(de)缺點,因(yin)為(wei)他顯然不懂(dong)得本地化數(shu)(shu)據和(he)代碼范圍的(de)好處和(he)重要性。
Const
7.關鍵字const有什么含意?
我(wo)只(zhi)要一聽到(dao)被(bei)面(mian)試者說:"const意味(wei)著(zhu)常數",我(wo)就知(zhi)道我(wo)正在和(he)(he)一個(ge)(ge)業(ye)余者打交道。去年Dan Saks已經在他的文章(zhang)(zhang)里完全概括了const的所(suo)有(you)用(yong)法,因此ESP(譯者:Embedded Systems Programming)的每一位讀者應該非(fei)常熟(shu)悉const能做什么(me)和(he)(he)不(bu)能做什么(me).如果你(ni)從沒有(you)讀到(dao)那篇文章(zhang)(zhang),只(zhi)要能說出const意味(wei)著(zhu)"只(zhi)讀"就可(ke)以了。盡(jin)管這個(ge)(ge)答案(an)不(bu)是完全的答案(an),但我(wo)接受它作為一個(ge)(ge)正確的答案(an)。(如果你(ni)想知(zhi)道更詳細的答案(an),仔細讀一下(xia)Saks的文章(zhang)(zhang)吧。)
如(ru)果應試者能正確回答這個問(wen)題,我將問(wen)他一(yi)個附加(jia)的問(wen)題:
下面的聲(sheng)明都是(shi)什(shen)么(me)意(yi)思(si)?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
/******/
前(qian)兩個(ge)(ge)(ge)的(de)作用(yong)是(shi)(shi)(shi)一(yi)(yi)樣,a是(shi)(shi)(shi)一(yi)(yi)個(ge)(ge)(ge)常(chang)(chang)整(zheng)型(xing)數。第三個(ge)(ge)(ge)意味著a是(shi)(shi)(shi)一(yi)(yi)個(ge)(ge)(ge)指(zhi)(zhi)(zhi)向(xiang)(xiang)(xiang)常(chang)(chang)整(zheng)型(xing)數的(de)指(zhi)(zhi)(zhi)針(也(ye)就(jiu)(jiu)是(shi)(shi)(shi),整(zheng)型(xing)數是(shi)(shi)(shi)不(bu)可(ke)(ke)(ke)(ke)修(xiu)改的(de),但指(zhi)(zhi)(zhi)針可(ke)(ke)(ke)(ke)以)。第四個(ge)(ge)(ge)意思a是(shi)(shi)(shi)一(yi)(yi)個(ge)(ge)(ge)指(zhi)(zhi)(zhi)向(xiang)(xiang)(xiang)整(zheng)型(xing)數的(de)常(chang)(chang)指(zhi)(zhi)(zhi)針(也(ye)就(jiu)(jiu)是(shi)(shi)(shi)說,指(zhi)(zhi)(zhi)針指(zhi)(zhi)(zhi)向(xiang)(xiang)(xiang)的(de)整(zheng)型(xing)數是(shi)(shi)(shi)可(ke)(ke)(ke)(ke)以修(xiu)改的(de),但指(zhi)(zhi)(zhi)針是(shi)(shi)(shi)不(bu)可(ke)(ke)(ke)(ke)修(xiu)改的(de))。后一(yi)(yi)個(ge)(ge)(ge)意味著a是(shi)(shi)(shi)一(yi)(yi)個(ge)(ge)(ge)指(zhi)(zhi)(zhi)向(xiang)(xiang)(xiang)常(chang)(chang)整(zheng)型(xing)數的(de)常(chang)(chang)指(zhi)(zhi)(zhi)針(也(ye)就(jiu)(jiu)是(shi)(shi)(shi)說,指(zhi)(zhi)(zhi)針指(zhi)(zhi)(zhi)向(xiang)(xiang)(xiang)的(de)整(zheng)型(xing)數是(shi)(shi)(shi)不(bu)可(ke)(ke)(ke)(ke)修(xiu)改的(de),同時指(zhi)(zhi)(zhi)針也(ye)是(shi)(shi)(shi)不(bu)可(ke)(ke)(ke)(ke)修(xiu)改的(de))。如果(guo)應試者能正確(que)回答這些(xie)問(wen)題,那么(me)他就(jiu)(jiu)給我留下(xia)了(le)一(yi)(yi)個(ge)(ge)(ge)好(hao)印象。順帶提一(yi)(yi)句,也(ye)許你可(ke)(ke)(ke)(ke)能會問(wen),即使不(bu)用(yong)關鍵字(zi) const,也(ye)還(huan)是(shi)(shi)(shi)能很容易寫出(chu)功能正確(que)的(de)程序,那么(me)我為什么(me)還(huan)要如此(ci)看重關鍵字(zi)const呢(ni)?我也(ye)如下(xia)的(de)幾下(xia)理由:
1) 關鍵(jian)字const的(de)(de)作用(yong)是為(wei)(wei)給讀你(ni)代碼的(de)(de)人傳達(da)非常有用(yong)的(de)(de)信息(xi),實際上,聲明一個參(can)數(shu)為(wei)(wei)常量(liang)是為(wei)(wei)了(le)告訴了(le)用(yong)戶這(zhe)(zhe)個參(can)數(shu)的(de)(de)應(ying)用(yong)目的(de)(de)。如果(guo)你(ni)曾花很多時(shi)間清理其它人留(liu)(liu)下(xia)的(de)(de)垃圾(ji)(ji),你(ni)就(jiu)會很快學會感謝這(zhe)(zhe)點(dian)多余的(de)(de)信息(xi)。(當然,懂得用(yong)const的(de)(de)程(cheng)序員很少會留(liu)(liu)下(xia)的(de)(de)垃圾(ji)(ji)讓別人來清理的(de)(de)。)
2) 通過給優化器一些附(fu)加的信息,使(shi)用關鍵字const也許(xu)能(neng)產生更緊湊(cou)的代碼。
3) 合理地使用關鍵(jian)字(zi)const可以(yi)使編譯(yi)器很自然地保(bao)護(hu)那(nei)些不(bu)希望被(bei)改變的(de)參數,防止其(qi)被(bei)無(wu)意的(de)代碼修改。簡而言之(zhi),這樣(yang)可以(yi)減(jian)少bug的(de)出現。
Volatile
8. 關鍵字(zi)volatile有什么含意?并給出三個不同的例子。
一個(ge)(ge)(ge)定義為(wei)volatile的變(bian)(bian)量(liang)(liang)(liang)是(shi)說這(zhe)變(bian)(bian)量(liang)(liang)(liang)可能會被(bei)意想不到地(di)改變(bian)(bian),這(zhe)樣(yang),編(bian)譯器就不會去假設這(zhe)個(ge)(ge)(ge)變(bian)(bian)量(liang)(liang)(liang)的值了。精(jing)確地(di)說就是(shi),優化器在(zai)用(yong)到這(zhe)個(ge)(ge)(ge)變(bian)(bian)量(liang)(liang)(liang)時(shi)必須每次都(dou)小(xiao)心地(di)重新讀取這(zhe)個(ge)(ge)(ge)變(bian)(bian)量(liang)(liang)(liang)的值,而不是(shi)使(shi)用(yong)保存在(zai)寄存器里的備份。下面(mian)是(shi)volatile變(bian)(bian)量(liang)(liang)(liang)的幾個(ge)(ge)(ge)例子:
1) 并行設備的硬件(jian)寄(ji)存(cun)器(qi)(如:狀態(tai)寄(ji)存(cun)器(qi))
2) 一個中(zhong)斷服務子程(cheng)序中(zhong)會(hui)訪問到的非自動變量(Non-automatic variables)
3) 多(duo)線程應用中被幾個任務共(gong)享的變量
回答不(bu)出(chu)這個問題(ti)的(de)人是不(bu)會(hui)被(bei)雇傭的(de)。我認為這是區分C程(cheng)序員(yuan)和嵌(qian)(qian)入(ru)式系統(tong)程(cheng)序員(yuan)的(de)基本(ben)的(de)問題(ti)。搞(gao)嵌(qian)(qian)入(ru)式的(de)家伙們經常同硬件(jian)、中斷(duan)、RTOS等等打(da)交道,所有這些都(dou)要求用到volatile變量(liang)。不(bu)懂得(de)volatile的(de)內容將會(hui)帶來(lai)災(zai)難。
假設被面試者正確地回答了這(zhe)是(shi)問(wen)題(嗯,懷疑是(shi)否會是(shi)這(zhe)樣),我將稍微深究(jiu)一下,看一下這(zhe)家伙(huo)是(shi)不是(shi)直正懂(dong)得volatile完全的(de)重要性。
1) 一個參(can)數既可以(yi)是const還可以(yi)是volatile嗎?解(jie)釋為什(shen)么。
2) 一個指針(zhen)可以(yi)是volatile 嗎?解釋(shi)為什么。
3) 下面的函(han)數有什(shen)么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1) 是的(de)。一個例子是只(zhi)讀的(de)狀態寄(ji)存(cun)器。它是volatile因(yin)為它可能被意想不(bu)到(dao)地改(gai)變。它是const因(yin)為程(cheng)序(xu)不(bu)應(ying)該試圖去修改(gai)它。
2) 是(shi)(shi)的。盡管這(zhe)并不很(hen)常(chang)見(jian)。一個例子(zi)是(shi)(shi)當一個中服務子(zi)程序修(xiu)該一個指向一個buffer的指針時。
3) 這(zhe)段代碼有點(dian)變(bian)態。這(zhe)段代碼的(de)目的(de)是用來返指針*ptr指向(xiang)值的(de)平方,但是,由于(yu)*ptr指向(xiang)一個volatile型參數,編譯(yi)器(qi)將產生類似下(xia)面(mian)的(de)代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的(de)值可能(neng)被意(yi)想不(bu)到地(di)該變,因此(ci)a和b可能(neng)是(shi)不(bu)同的(de)。結果,這段代碼可能(neng)返不(bu)是(shi)你(ni)所期望的(de)平方(fang)值!正確(que)的(de)代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulation)
9. 嵌入(ru)式系(xi)統總是要用戶對變(bian)量或寄存器(qi)進(jin)行位操作。給定一個(ge)整型(xing)變(bian)量a,寫(xie)兩(liang)段代碼,第一個(ge)設置(zhi)a的bit 3,第二個(ge)清(qing)除a 的bit 3。在以(yi)上(shang)兩(liang)個(ge)操作中,要保持其它位不變(bian)。
對這個問題有三種基本的反應(ying)
1) 不知道(dao)如(ru)何(he)下手。該被面者從沒做(zuo)過(guo)任何(he)嵌入式系統的工作。
2) 用(yong)bit fields。Bit fields是被(bei)扔到C語言(yan)死角的(de)東西,它保證(zheng)你的(de)代碼在不同(tong)(tong)編譯器之間是不可(ke)移(yi)植(zhi)的(de),同(tong)(tong)時(shi)也保證(zheng)了的(de)你的(de)代碼是不可(ke)重(zhong)用(yong)的(de)。我(wo)近不幸看到 Infineon為其較復(fu)雜(za)的(de)通信芯片寫的(de)驅動(dong)程序,它用(yong)到了bit fields因此(ci)完全(quan)對(dui)我(wo)無用(yong),因為我(wo)的(de)編譯器用(yong)其它的(de)方式(shi)來實現bit fields的(de)。從道(dao)德講:永遠不要讓一個非嵌入式(shi)的(de)家伙(huo)粘實際硬件的(de)邊。
3) 用 #defines 和(he) bit masks 操作。這(zhe)是一(yi)個(ge)有極(ji)高可移植性的(de)方法,是應(ying)該被用到的(de)方法。佳的(de)解(jie)決方案如下:
#define BIT3 (0x1 << 3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些人喜(xi)歡為設(she)置和(he)清除(chu)值而定義一個掩碼同時定義一些說明常數,這(zhe)也是可以接受的。我(wo)希望看到幾個要點:說明常數、|=和(he)&=~操作。
訪問(wen)固定的(de)內存位置(Accessing fixed memory locations)
10. 嵌入式系統(tong)經常具(ju)有要(yao)求程(cheng)序員(yuan)去訪(fang)問(wen)某特(te)定的(de)(de)內存位(wei)置(zhi)的(de)(de)特(te)點。在某工程(cheng)中,要(yao)求設置(zhi)一(yi)絕對(dui)地(di)址為0x67a9的(de)(de)整型變量的(de)(de)值為0xaa66。編譯(yi)器是一(yi)個純粹(cui)的(de)(de)ANSI編譯(yi)器。寫(xie)代碼去完成這一(yi)任務。
這(zhe)一問題測試你是否(fou)知道為了訪(fang)問一絕(jue)對地址把一個整型(xing)數強(qiang)制轉換(typecast)為一指針是合(he)法的(de)(de)。這(zhe)一問題的(de)(de)實(shi)現方式隨著個人風格不(bu)同而不(bu)同。典(dian)型(xing)的(de)(de)類似代碼如(ru)下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
A more obscure approach is:
一(yi)個較晦澀(se)的方法是:
*(int * const)(0x67a9) = 0xaa55;
即(ji)使(shi)你的品味更接近第二種(zhong)方(fang)案,但我建議(yi)你在(zai)面試時(shi)使(shi)用第一種(zhong)方(fang)案。
中(zhong)斷(Interrupts)
11. 中斷(duan)是嵌入式系統中重要的(de)組(zu)成部分,這導致了很多編譯(yi)開發(fa)商提供一(yi)種擴展—讓(rang)標準(zhun)C支(zhi)持中斷(duan)。具代(dai)表(biao)事實是,產生了一(yi)個新的(de)關鍵字 __interrupt。下(xia)面的(de)代(dai)碼就使用了__interrupt關鍵字去(qu)定義了一(yi)個中斷(duan)服務子(zi)程序(ISR),請評論一(yi)下(xia)這段代(dai)碼的(de)。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}
這個函數(shu)有太多的錯誤了,以(yi)至讓人不知從何(he)說(shuo)起了:
1) ISR 不(bu)能返回一個值。如(ru)果你不(bu)懂(dong)這個,那么你不(bu)會(hui)被雇用(yong)的。
2) ISR 不能傳(chuan)遞參數。如果你(ni)沒(mei)有看到這一點(dian),你(ni)被雇(gu)用的機(ji)會等同(tong)第一項(xiang)。
3) 在許多的處理器/編譯(yi)器中(zhong),浮點(dian)一般都是(shi)不可重入的。有(you)些處理器/編譯(yi)器需要(yao)讓額處的寄存器入棧,有(you)些處理器/編譯(yi)器就是(shi)不允許在ISR中(zhong)做浮點(dian)運算。此外,ISR應該是(shi)短而有(you)效率的,在ISR中(zhong)做浮點(dian)運算是(shi)不明智(zhi)的。
4) 與第三(san)點一脈相承,printf()經常有重(zhong)入(ru)和性(xing)能上的(de)問(wen)題(ti)。如果(guo)你丟掉了第三(san)和第四點,我(wo)不會太為難你的(de)。不用說(shuo),如果(guo)你能得(de)到(dao)后兩點,那么你的(de)被雇用前景越來(lai)越光明了。
代碼例子(Code examples)
12 . 下面的代碼輸出(chu)是(shi)什(shen)么,為什(shen)么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
這個(ge)問題測試你(ni)是(shi)(shi)(shi)否懂得C語言中(zhong)的(de)(de)(de)整數自動轉換原則,我發現(xian)有些開發者懂得極(ji)少這些東西(xi)。不管如何,這無(wu)(wu)符(fu)(fu)號(hao)(hao)整型問題的(de)(de)(de)答案是(shi)(shi)(shi)輸出是(shi)(shi)(shi) ">6"。原因是(shi)(shi)(shi)當表達式中(zhong)存在有符(fu)(fu)號(hao)(hao)類型和(he)無(wu)(wu)符(fu)(fu)號(hao)(hao)類型時所有的(de)(de)(de)操作(zuo)數都自動轉換為無(wu)(wu)符(fu)(fu)號(hao)(hao)類型。因此-20變成了(le)一(yi)個(ge)非常大(da)的(de)(de)(de)正整數,所以該表達式計算(suan)出的(de)(de)(de)結(jie)果大(da)于6。這一(yi)點對于應當頻繁用到(dao)無(wu)(wu)符(fu)(fu)號(hao)(hao)數據類型的(de)(de)(de)嵌入(ru)式系統來說是(shi)(shi)(shi)豐常重要的(de)(de)(de)。如果你(ni)答錯了(le)這個(ge)問題,你(ni)也就到(dao)了(le)得不到(dao)這份工(gong)作(zuo)的(de)(de)(de)邊緣。
13. 評價下面的代碼(ma)片(pian)斷(duan):
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1''s complement of zero */
對于(yu)一個int型不(bu)是16位的(de)處理器為說,上面的(de)代碼是不(bu)正確(que)的(de)。應編寫如下:
unsigned int compzero = ~0;
這一(yi)(yi)問(wen)題真正能揭露(lu)出應(ying)試者是否懂得處(chu)理器字長的重要(yao)性。在我的經驗里,好的嵌(qian)入式(shi)程序(xu)員非常準確地(di)明白硬件的細(xi)節和(he)它的局限(xian),然(ran)而PC機程序(xu)往往把硬件作為一(yi)(yi)個無法避免(mian)的煩惱(nao)。
到了(le)這個(ge)階段,應(ying)(ying)試者(zhe)(zhe)(zhe)或(huo)者(zhe)(zhe)(zhe)完(wan)全垂頭喪氣(qi)了(le)或(huo)者(zhe)(zhe)(zhe)信心滿滿志在必得。如果顯(xian)然應(ying)(ying)試者(zhe)(zhe)(zhe)不(bu)是(shi)很好,那(nei)么這個(ge)測試就在這里結束了(le)。但如果顯(xian)然應(ying)(ying)試者(zhe)(zhe)(zhe)做得不(bu)錯,那(nei)么我就扔出下面的(de)追加問(wen)題(ti),這些問(wen)題(ti)是(shi)比較難的(de),我想僅(jin)僅(jin)非常(chang)優秀的(de)應(ying)(ying)試者(zhe)(zhe)(zhe)能做得不(bu)錯。提(ti)出這些問(wen)題(ti),我希望(wang)更多看到應(ying)(ying)試者(zhe)(zhe)(zhe)應(ying)(ying)付問(wen)題(ti)的(de)方法(fa),而不(bu)是(shi)答案(an)。不(bu)管如何,你(ni)就當是(shi)這個(ge)娛樂(le)吧...
動態內存分配(pei)(Dynamic memory allocation)
14. 盡(jin)管不(bu)像非嵌入(ru)(ru)式(shi)(shi)(shi)計(ji)算機那(nei)么(me)常見,嵌入(ru)(ru)式(shi)(shi)(shi)系統(tong)還是有從堆(dui)(heap)中動態分(fen)(fen)配內存(cun)的(de)過程(cheng)的(de)。那(nei)么(me)嵌入(ru)(ru)式(shi)(shi)(shi)系統(tong)中,動態分(fen)(fen)配內存(cun)可能發生的(de)問題是什(shen)么(me)?
這(zhe)(zhe)(zhe)里(li),我(wo)期望應試者(zhe)能提到(dao)(dao)內存(cun)碎(sui)(sui)片(pian),碎(sui)(sui)片(pian)收集(ji)的(de)(de)(de)問題,變(bian)量的(de)(de)(de)持行時間等(deng)等(deng)。這(zhe)(zhe)(zhe)個(ge)(ge)主題已經在(zai)ESP雜志(zhi)中被廣泛地討論過(guo)了(主要是 P.J. Plauger, 他的(de)(de)(de)解釋(shi)(shi)遠遠超(chao)過(guo)我(wo)這(zhe)(zhe)(zhe)里(li)能提到(dao)(dao)的(de)(de)(de)任(ren)何解釋(shi)(shi)),所有回過(guo)頭看一(yi)下這(zhe)(zhe)(zhe)些雜志(zhi)吧!讓應試者(zhe)進入一(yi)種虛假的(de)(de)(de)安全感覺后,我(wo)拿(na)出這(zhe)(zhe)(zhe)么一(yi)個(ge)(ge)小節目:
下面的代碼片段的輸出(chu)是什(shen)么,為什(shen)么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
這(zhe)(zhe)(zhe)(zhe)是(shi)一個(ge)(ge)(ge)有趣的(de)問(wen)題(ti)。近在我(wo)的(de)一個(ge)(ge)(ge)同事不經意把0值傳給了函數malloc,得(de)到(dao)(dao)(dao)了一個(ge)(ge)(ge)合(he)法的(de)指針之后(hou),我(wo)才想(xiang)到(dao)(dao)(dao)這(zhe)(zhe)(zhe)(zhe)個(ge)(ge)(ge)問(wen)題(ti)。這(zhe)(zhe)(zhe)(zhe)就是(shi)上面的(de)代碼(ma),該代碼(ma)的(de)輸(shu)出是(shi)"Got a valid pointer"。我(wo)用這(zhe)(zhe)(zhe)(zhe)個(ge)(ge)(ge)來開始(shi)討論(lun)這(zhe)(zhe)(zhe)(zhe)樣(yang)的(de)一問(wen)題(ti),看(kan)看(kan)被面試者是(shi)否想(xiang)到(dao)(dao)(dao)庫(ku)例程這(zhe)(zhe)(zhe)(zhe)樣(yang)做是(shi)正(zheng)(zheng)確。得(de)到(dao)(dao)(dao)正(zheng)(zheng)確的(de)答案(an)固然重要,但解決(jue)問(wen)題(ti)的(de)方法和你做決(jue)定的(de)基本原(yuan)理更重要些。
Typedef
15 Typedef 在(zai)C語(yu)言(yan)中頻(pin)繁(fan)用以聲明一個(ge)已經存在(zai)的數據類型的同義(yi)字(zi)。也可以用預處理器做(zuo)類似的事。例(li)如,思考一下(xia)(xia)下(xia)(xia)面的例(li)子:
#define dPS struct s *
typedef struct s * tPS;
以上(shang)兩種情(qing)況的意圖(tu)都是要定義(yi)dPS 和 tPS 作為一個指向結構s指針(zhen)。哪(na)種方法更好呢?(如(ru)果有的話)為什么?
這是(shi)一個非(fei)常微妙的問(wen)題,任何(he)人答(da)(da)對這個問(wen)題(正當(dang)的原因(yin))是(shi)應當(dang)被恭(gong)喜的。答(da)(da)案是(shi):typedef更好(hao)。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一個擴展為
struct s * p1, p2;
.
上面的(de)代碼定義p1為一(yi)(yi)個(ge)指(zhi)向結構(gou)的(de)指(zhi),p2為一(yi)(yi)個(ge)實際的(de)結構(gou),這也許(xu)不是你(ni)想要的(de)。第二個(ge)例(li)子正確地定義了(le)p3 和p4 兩個(ge)指(zhi)針。
晦澀的語法
16 . C語言同意一些令人(ren)震驚的結構,下面的結構是合法(fa)的嗎,如果是它做(zuo)些什(shen)么?
int a = 5, b = 7, c;
c = a+++b;
這(zhe)(zhe)個(ge)問(wen)(wen)題將做為這(zhe)(zhe)個(ge)測(ce)驗的(de)(de)一個(ge)愉快的(de)(de)結尾。不(bu)管你相不(bu)相信,上面(mian)的(de)(de)例(li)子是(shi)完全合乎語(yu)法(fa)的(de)(de)。問(wen)(wen)題是(shi)編譯器如何處(chu)(chu)理它?水平不(bu)高的(de)(de)編譯作者(zhe)實(shi)際上會(hui)爭論(lun)這(zhe)(zhe)個(ge)問(wen)(wen)題,根據(ju)處(chu)(chu)理原則,編譯器應當能處(chu)(chu)理盡可能所(suo)有合法(fa)的(de)(de)用(yong)法(fa)。因此,上面(mian)的(de)(de)代碼被處(chu)(chu)理成:
c = a++ + b;
因此, 這段代碼(ma)持行后a = 6, b = 7, c = 12。
如果(guo)你知道答案(an),或猜出正確答案(an),做(zuo)得好。如果(guo)你不知道答案(an),我也不把這個當(dang)作問題(ti)。我發(fa)現這個問題(ti)的大好處是這是一個關(guan)于代碼(ma)編(bian)寫風格,代碼(ma)的可(ke)讀性,代碼(ma)的可(ke)修改性的好的話題(ti)。
送給(gei)那些奮斗(dou)在(zai)嵌(qian)入式學(xue)習一(yi)線的(de)未來超級(ji)工程師們(men)!

