數據聲明(Data declarations) 5. 用變量a給出下面的定義 a) 一個整型數(An integer) b)一個指向整型數的指針( A pointer to an integer) c)一個指向指針的的指針,它指向的指針是指向一個整型數( A pointer to a pointer to an intege)r d)一個有10個整型數的數組( An array of 10 integers) e) 一個有10個指針的數組,該指針是指向一個整型數的。(An array of 10 pointers to integers) f) 一個指向有10個整型數數組的指針( A pointer to an array of 10 integers) g) 一個指向函數的指針,該函數有一個整型參數并返回一個整型數(A pointer to a function that takes an integer as an argument and returns an integer) h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數并返回一個整型數( 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 人們經常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者假如不知道所有的答案(或至少大部分答案),那么也就沒有為這次面試做預備,假如該面試者沒有為這次面試做預備,那么他又能為什么出預備呢?
Const 7.要害字const有什么含意? 我只要一聽到被面試者說:"const意味著常數",我就知道我正在和一個業余者打交道。去年Dan Saks已經在他的文章里完全概括了const的所有用法,因此ESP(譯者:Embedded Systems PRogramming)的每一位讀者應該非常熟悉const能做什么和不能做什么.假如你從沒有讀到那篇文章,只要能說出const意味著"只讀"就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(假如你想知道更具體的答案,仔細讀一下Saks的文章吧。) 假如應試者能正確回答這個問題,我將問他一個附加的問題:下面的聲明都是什么意思? const int a; int const a; const int *a; int * const a; int const * a const; /******/ 前兩個的作用是一樣,a是一個常整型數。第三個意味著a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針可以)。第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的)。最后一個意味著a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。假如應試者能正確回答這些問題,那么他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用要害字 ,也還是能很輕易寫出功能正確的程序,那么我為什么還要如此看重要害字const呢?我也如下的幾下理由: 1) 要害字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數為常量是為了告訴了用戶這個參數的應用目的。假如你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。) 2) 通過給優化器一些附加的信息,使用要害字const也許能產生更緊湊的代碼。 3) 合理地使用要害字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。
int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; } 由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下: long square(volatile int *ptr) { int a; a = *ptr; return a * a; }
代碼例子(Code examples) 12 . 下面的代碼輸出是什么,為什么? void foo(void) { unsigned int a = 6; int b = -20; (a+b > 6) ? puts("> 6") : puts("<= 6"); } 這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是 ">6"。原因是當表達式中存在有符號類型和無符號類型時所有的操作數都自動轉換為無符號類型。因此-20變成了一個非常大的正整數,所以該表達式計算出的結果大于6。這一點對于應當頻繁用到無符號數據類型的嵌入式系統來說是豐常重要的。假如你答錯了這個問題,你也就到了得不到這份工作的邊緣。
13. 評價下面的代碼片斷: unsigned int zero = 0; unsigned int compzero = 0xFFFF; /*1's complement of zero */ 對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下: unsigned int compzero = ~0; 這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經驗里,好的嵌入式程序員非常準確地明白硬件的細節和它的局限,然而PC機程序往往把硬件作為一個無法避免的煩惱。 到了這個階段,應試者或者完全沒精打采了或者信心滿滿志在必得。假如顯然應試者不是很好,那么這個測試就在這里結束了。但假如顯然應試者做得不錯,那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧...
動態內存分配(Dynamic memory allocation) 14. 盡管不像非嵌入式計算機那么常見,嵌入式系統還是有從堆(heap)中動態分配內存的過程的。那么嵌入式系統中,動態分配內存可能發生的問題是什么?這里,我期望應試者能提到內存碎片,碎片收集的問題,變量的持行時間等等。這個主題已經在ESP雜志中被廣泛地討論過了(主要是 P.J. Plauger, 他的解釋遠遠超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應試者進入一種虛假的安全感覺后,我拿出這么一個小節目:下面的代碼片段的輸出是什么,為什么? char *ptr; if ((ptr = (char *)malloc(0)) == NULL) puts("Got a null pointer"); else puts("Got a valid pointer"); 這是一個有趣的問題。最近在我的一個同事不經意把0值傳給了函數malloc,得到了一個合法的指針之后,我才想到這個問題。這就是上面的代碼,該代碼的輸出是"Got a valid pointer"。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。
晦澀的語法 16 . C語言同意一些令人震動的結構,下面的結構是合法的嗎,假如是它做些什么? int a = 5, b = 7, c; c = a+++b; 這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理盡可能所有合法的用法。因此,上面的代碼被處理成:c = a++ + b;
因此, 這段代碼持行后a = 6, b = 7, c = 12。 假如你知道答案,或猜出正確答案,做得好。假如你不知道答案,我也不把這個當作問題。我發現這個問題的最大好處是這是一個關于代碼編寫風格,代碼的可讀性,代碼的可修改性的好的話題。