嵌入式面試考題(這是我在12年編寫的一套題目)
一、填空題(每空2分,共30分)
- 微處理器有兩種匯流排架構,資料和指令同一介面的是++馮洛伊曼結構++,分開的指令和資料介面、取值和資料訪問可以並行進行的是++哈佛結構++。
- ARM微處理器有七種工作模式,它們分為兩類:++特權模式++、++非特權模式++。其中使用者模式屬於++非特權模式++。
- ARM核有兩個指令集,分別是:++ARM指令集++、++Thumb指令集++。
- ARM微處理器復位後,PC(R15)的地址通常是++0x0000++,初始的工作模式是++管理模式++,R13通常用來做++堆疊指標++,R14通常用來做++連結暫存器++。
- 在ARM體系架構中對複雜的記憶體管理是通過系統控制協處理器++儲存管理單元:MMU++和++儲存器保護單元:MPU++來進行的。當系統發生++資料中止異常++和++預取中止異常++時,異常處理程式透過嵌入式作業系統的記憶體管理機制,通過MMU交換實體記憶體和虛擬記憶體的頁面,以保證程式正常執行。
二、問答題(每題3分,共30分)
- 用預處理指令#define宣告一個常數,用以表明1年中有多少秒(忽略閏年問題)。
答:關鍵是括號和UL上面,答案如下:
#define (365*24*60*60UL)
- 寫一個標準巨集MIN,這個巨集輸入兩個引數並返回較小的一個。
答:關鍵是看是否有括號,防止紅展開錯誤:
#define MIN(A,B) ( (A) <= (B) ? (A) : (B))
- ARM常見的定址方式有哪些?
答:共9種定址方式:暫存器定址、立即定址、暫存器移位定址、暫存器間接定址、基址定址、多暫存器定址、堆疊定址、塊拷貝定址、相對定址;除了堆疊定址、塊拷貝定址,其他的定址方式都很常見。
- S3C244BOX I2C匯流排介面操作有哪四種操作方式?
答:主機發送器模式、主機接收器模式、從機發送器模式、從機接收器模式。(其實就是IIC的幾種通訊模式)
- Bootloader在嵌入式系統中主要起什麼作用?完成哪些主要工作?
答:bootloader的作用:Boot Loader是在作業系統核心執行之前執行的一段小程式。通過這段小程式,我們可以初始化硬體裝置,從而將系統的軟硬體環境帶到一個合適的狀態,以便為最終呼叫作業系統核心準備好正確的環境,最後從別處(Flash、乙太網、UART)載入核心映像並跳到入口地址。
bootloader完成的工作:
- 實現基本初始化:如系統時鐘、初始化記憶體、Nand Flash控制器等硬體裝置
- 初始化其他擴充套件功能:比如串列埠、USB、網路埠等方便開發與除錯
- 最終目的是:載入核心到RAM中,併為核心啟動配置條件,最後啟動核心。
- 字元裝置和塊裝置的主要區別是什麼?
答:看對核心驅動基礎的理解:
- 字元裝置只能以位元組為單位訪問,而塊裝置以塊為單位訪問,例如512位元組,1024位元組等;
- b.塊裝置可以隨機訪問,但是字元裝置不可以;
- c.字元和塊沒有訪問量大小的限制,塊也可以以位元組為單位來訪問。
其中,第一點為主要區別。
- 簡要說明嵌入式作業系統多工通訊的常用方式?
答:常用的通訊方式有:無名管道、有名管道、訊號量、訊息佇列、共享記憶體、網路socket通訊。
- 簡述BLX、SWI、STM、LDM、MOV、MVN的含義?
答:它們的含義分別如下(考是否熟悉彙編,在U-BOOT和核心移植時會用到):
- BLX:帶連結的切換跳轉;
- SWI:軟中斷;
- STM:把多個32位的儲存器值存放到記憶體;
- LDM:從記憶體送多個32位字到ARM暫存器;
- MOV:傳送一個32位數到暫存器;
- MVN:把一個32位數的邏輯“非”送到暫存器。
- 簡述下面程式碼輸出是什麼,為什麼?
void foo(void) { unsigned int a = 6; int b = -20; (a+b > 6) ? puts("> 6") : puts("<= 6"); }
答:輸出“>6”,原因是:當表示式中存在有符號型別和無符號型別時所有的運算元都自動轉換為無符號型別。
- 簡述下面程式碼定義的temp變數是什麼型別?
typedef int (*test) ( float * , float*) test tmp;
答:函式的指標,該函式有兩個指向浮點數(float)的指標(pointer)作為引數(arguments),並且函式的返回值型別是整型;Pointer to function having two argument that is pointer to float and return int。
三、設計題(每題1.5分,共15分)
- 給定一個整型變數a,寫兩段程式碼,第一個設定a的bit3,第二個清除a的bit3。在以上兩個操作中,要保持其他位不變。
答:設定a的bit 3:
a |= (0x1<<3);
清除a的bit 3:
a &= ~(0x1<<3)。
- 在某工程中,要求設定一絕對地址為0x67a9的整型變數的值為0xaa66。編譯器是一個純粹的ANSI編譯器,寫程式碼去完成這一任務。
答:程式碼如下:
int *ptr = (int *)0x67a9; *ptr = 0xaa66;
或者:
int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa66; 變數名ptr可隨便取。
- 用變數a給出下面的定義:
- a)一個整型數:++int a++;
- b)一個指向整型數的指標:++int *a++;
- c)一個指向指標的指標,它指向的指標是指向一個整型數:++int **a++;
- d)一個有10個整數的陣列:++int a[10]++;
- e)一個有10個指標的陣列,該指標是指向一個整型數:++int *a[10]++;
- f)一個指向10個整型數陣列的指標:++int (*a)[10]++;
- g)一個指向函式的指標,該函式有一個整型引數並返回一個整型數:++int (*a)(int)++;
- h)一個有10個指標的陣列,該指標指向一個函式,該函式有一個整型引數並返回一個整型數:++int (*a[10])(int)++;
- 以下程式執行到最後,j的值是:
int counter (int i) { static int count =0; count = count +i; return (count ); } main() { int i , j; for (i=0; i <=5; i++) j = counter(i); } (a) 10(b) 15 (c) 6(d) 7
答案是:B
- 以下程式執行後,輸出的結果是:
void f(char**); main() { char * argv[] = { "ab" ,"cd" , "ef" ,"gh", "ij" ,"kl" }; f( argv ); } void f( char **p ) { char* t; t= (p+= sizeof(int))[-1]; printf( "%s" , t); } (a) ab(b) cd (c) ef(d) gh
答案是:B
- 以下程式執行後,輸出的結果是:
void e(int ); main() { int a; a=3; e(a); } void e(int n) { if(n>0) { e(--n); printf("%d" , n); e(--n); } } (a) 0 1 2 0(b) 0 1 2 1 (c) 1 2 0 1(d) 0 2 1 1
答案為:A.
- 以下程式執行後,輸出的結果是:
void f1(int *, int); void f2(int *, int); void(*p[2]) ( int *, int); main() { int a; int b; p[0] = f1; p[1] = f2; a=3; b=5; p[0](&a , b); printf("%d\t %d\t" , a ,b); p[1](&a , b); printf("%d\t %d\t" , a ,b); } void f1( int* p , int q) { int tmp; tmp =*p; *p = q; q= tmp; } void f2( int* p , int q) { int tmp; tmp =*p; *p = q; q= tmp; } (a) 5 5 5 5(b) 3 5 3 5 (c) 5 3 5 3(d) 3 3 3 3
答案是:A。
- 以下這段程式執行後,輸出的結果是:
void foo(int [][3] ); main() { int a [3][3]= { { 1,2,3} , { 4,5,6},{7,8,9}}; foo(a); printf("%d" , a[2][1]); } void foo( int b[][3]) { ++ b; b[1][1] =9; } (a) 8(b) 9 (c) 7(d)以上均不對
答案是:B。
- 以下這段程式執行後,輸出的結果是:
main() { inta[5] = {1,2,3,4,5}; int *ptr =(int*)(&a+1); printf("%d %d" , *(a+1), *(ptr-1) ); } (a) 2 2(b) 2 1 (c) 2 5(d) 以上均不是
正確答案:C;
- 以下這3個函式哪一個最可能引起指標方面的問題:
int *f1(void) { int x =10; return(&x); } int *f2(void) { int*ptr; *ptr =10; return ptr; } int *f3(void) { int *ptr; ptr=(int*) malloc(sizeof(int)); return ptr; } (a) 只有 f3 (b) 只有f1 and f3 (c) 只有f1 and f2 (d) f1 f2 ,f3
答案:C
四、評論題(每題5分,共15分)
- C語言同意一些令人震驚的結構,請問下面的結構合法嗎?如果合法,它是做什麼?
int a = 5, b = 7, c; c = a+++b;
答:是完全合乎語法的,根據“大嘴法”原則,上面程式碼被處理成:
c = a++ +b;
執行之後的結果為a = 6, b = 7, c = 12。
-
簡述const、volatile、extern三個關鍵字的含義和用法。
-
中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴充套件——讓標準C支援中斷。具代表事實是,產生了一個新的關鍵字__interrupt。下面程式碼就使用了__interrupt關鍵字去定義了一箇中斷服務程式(ISR),請評論一下這段程式碼存在的問題。
__interrupt double compute_area(double radius) { double area = PI*radius*radius; printf(“Area = %f”,area); return area; }
答:上面的程式存在如下四點問題:
- 錯誤點1:中斷服務程式是不能傳遞引數的,而此段程式碼帶有引數radius;
- 錯誤點2:中斷服務程式不能返回一個值,而此段程式碼返回了一個運算結果area;
- 錯誤點3:在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額外的暫存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是很不明智的。
- 錯誤點4:與第3點類似,printf()經常有重入和效能上的問題。
其中1、2兩點是基本的,3、4點比較難。