天天看點

嵌入式經典面試題

1. 關鍵字volatile有什麼含意 并給出三個不同的例子。

一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精确地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用儲存在寄存器裡的備份。下面是volatile變量的幾個例子:

1). 并行裝置的硬體寄存器(如:狀态寄存器)

2). 一個中斷服務子程式中會通路到的非自動變量(Non-automatic variables)

3). 多線程應用中被幾個任務共享的變量

2, 一個指針可以是volatile 嗎?解釋為什麼。

2). 是的。盡管這并不很常見。一個例子是當一個中服務子程式修該一個指向一個buffer的指針時。

9. 嵌入式系統總是要使用者對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設定a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。

#define BIT3 (0x1<<3)

static int a;

void set_bit3(void)

{

a |= BIT3;

}

void clear_bit3(void)

a &= ~BIT3;

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

11. 中斷是嵌入式系統中重要的組成部分,這導緻了很多編譯開發商提供一種擴充—讓标準C支援中斷。具代表事實是,産生了一個新的關鍵字 __interrupt。下面的代碼就使用了__interrupt關鍵字去定義了一個中斷服務子程式(ISR),請評論一下這段代碼的。

__interrupt double compute_area (double radius)

double area = PI * radius * radius;

printf(" Area = %f", area);

return area;

這個函數有太多的錯誤了,以至讓人不知從何說起了:

1). ISR 不能傳回一個值。如果你不懂這個,那麼你不會被雇用的。

2). ISR 不能傳遞參數。如果你沒有看到這一點,你被雇用的機會等同第一項。

3). 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。

4). 與第三點一脈相承,printf()經常有重入和性能上的問題。如果你丢掉了第三和第四點,我不會太為難你的。不用說,如果你能得到後兩點,那麼你的被雇用前景越來越光明了。

13. 評價下面的代碼片斷:

unsigned int zero = 0;

unsigned int compzero = 0xFFFF;

/*1's complement of zero */

對于一個int型不是16位的處理器為說,上面的代碼是不正确的。應編寫如下:

unsigned int compzero = ~0;

這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經驗裡,好的嵌入式程式員非常準确地明白硬體的細節和它的局限,然而PC機程式往往把硬體作為一個無法避免的煩惱。

到了這個階段,應試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那麼這個測試就在這裡結束了。但如果顯然應試者做得不錯,那麼我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…

1、線形表a、b為兩個有序升序的線形表,編寫一程式,使兩個有序線形表合并成一個有序升序線形表h;

答案在 請化大學 嚴銳敏《資料結構第二版》第二章例題,資料結構當中,這個叫做:兩路歸并排序

Linklist *unio(Linklist *p,Linklist *q){

linklist *R,*pa,*qa,*ra;

pa=p;

qa=q;

R=ra=p;

while(pa->next!=NULL&&qa->next!=NULL){

if(pa->data>qa->data){

ra->next=qa;

qa=qa->next;

else{

ra->next=pa;

pa=pa->next;

if(pa->next!=NULL)

if(qa->next!=NULL)

ra->next==qa;

return R;

3、用遞歸算法判斷數組a[N]是否為一個遞增數組。

遞歸的方法,記錄目前最大的,并且判斷目前的是否比這個還大,大則繼續,否則傳回false結束:

bool fun( int a[], int n )

if( n= =1 )

return true;

if( n= =2 )

return a[n-1] >= a[n-2];

return fun( a,n-1) && ( a[n-1] >= a[n-2] );

2.單連表的建立,把'a'--'z'26個字母插入到連表中,并且倒叙,還要列印!

方法1:

typedef struct val

{    int date_1;

     struct val *next;

}*p;

void main(void)

{    char c;

     for(c=122;c>=97;c--)

        { p.date=c;

          p=p->next;

         }

     p.next=NULL;

方法2:

node *p = NULL;

node *q = NULL;

node *head = (node*)malloc(sizeof(node));

head->data = ' ';head->next=NULL;

node *first = (node*)malloc(sizeof(node));

first->data = 'a';first->next=NULL;head->next = first;

p = first;

int longth = 'z' - 'b';

int i=0;

while ( i<=longth )

node *temp = (node*)malloc(sizeof(node));

temp->data = 'b'+i;temp->next=NULL;q=temp;

head->next = temp; temp->next=p;p=q;

i++;

print(head);

一個遞規反向輸出字元串的例子,可謂是反序的經典例程.

void inverse(char *p)

     if( *p = = '\0' )

return;

     inverse( p+1 );

     printf( "%c", *p );

int main(int argc, char *argv[])

     inverse("abc\0");

     return 0;

2。運作的結果為什麼等于15

#include "stdio.h"

#include "string.h"

void main()

char aa[10];

printf("%d",strlen(aa));

答案:sizeof()和初不初始化,沒有關系;strlen()和初始化有關。

6。int a,b,c 請寫函數實作C=a+b ,不可以改變資料類型,如将c改為long int,關鍵是如何處理溢出問題

答案:bool add (int a, int b,int *c)

*c=a+b;

return (a>0 && b>0 &&(*c<a || *c<b) || (a<0 && b<0 &&(*c>a || *c>b)));

7。分析:

struct bit

{    int a:3;

     int b:2;

     int c:3;

};

int main()

   bit s;

   char *c=(char*)&s;

    cout<<sizeof(bit)<<endl;

   *c=0x99;

    cout << s.a <<endl <<s.b<<endl<<s.c<<endl;

      int a=-1;

    printf("%x",a);

   return 0;

輸出為什麼是?

答案:4

1

-1

-4

ffffffff

因為0x99在記憶體中表示為 100 11 001 , a = 001, b = 11, c = 100(在vc環境中,一般是由右到左進行配置設定的)

當c為有符合數時, c = 100, 最高1為表示c為負數,負數在計算機用補碼表示,是以c = -4;同理

b = -1;

當c為有符合數時, c = 100,即 c = 4,同理 b = 3

9。下面這個程式執行後會有什麼錯誤或者效果:

#define MAX 255

    unsigned char A[MAX],i; //i被定義為unsigned char

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

       A[i]=i;

答案:死循環加數組越界通路(C/C++不進行數組越界檢查)

MAX=255

數組A的下标範圍為:0..MAX-1,這是其一..

其二.當i循環到255時,循環内執行:

   A[255]=255;

這句本身沒有問題..但是傳回for (i=0;i<=MAX;i++)語句時,

由于unsigned char的取值範圍在(0..255),i++以後i又為0了..無限循環下去.

2.對于一個頻繁使用的短小函數,在C語言中應用什麼實作,在C++中應用什麼實作?

c用宏定義,c++用inline

6.軟體測試都有那些種類?

黑盒:針對系統功能的測試    白合:測試函數功能,各函數接口

  1. 程序和線程的差别。

線程是指程序内的一個執行單元,也是程序内的可排程實體.

與程序的差別:

(1)排程:線程作為排程和配置設定的基本機關,程序作為擁有資源的基本機關

(2)并發性:不僅程序之間可以并發執行,同一個程序的多個線程之間也可并發執行

(3)擁有資源:程序是擁有資源的一個獨-立機關,線程不擁有系統資源,但可以通路隸屬于程序的資源.

(4)系統開銷:在建立或撤消程序時,由于系統都要為之配置設定和回收資源,導緻系統的開銷明顯大于建立或撤消線程時的開銷。

11.程序死鎖的原因

資源競争及程序推進順序非法

12.死鎖的4個必要條件

互斥、請求保持、不可剝奪、環路

13.死鎖的處理

鴕鳥政策、預防政策、避免政策、檢測與解除死鎖

9.純虛函數如何定義?使用時應注意什麼?

virtual void f()=0;

是接口,子類必須要實作

2:int main()

   {

    int x=3;

    printf("%d",x);

    return 1;

   }

問函數既然不會被其它函數調用,為什麼要傳回1?

mian中,c标準認為0表示成功,非0表示錯誤。具體的值是某中具體出錯資訊

  1. 要對絕對位址0x100000指派,我們可以用

(unsigned int*)0x100000 = 1234;

那麼要是想讓程式跳轉到絕對位址是0x100000去執行,應該怎麼做?

*((void (*)( ))0x100000 ) ( );

首先要将0x100000強制轉換成函數指針,即:

(void (*)())0x100000

然後再調用它:

*((void (*)())0x100000)();

用typedef可以看得更直覺些:

typedef void(*)() voidFuncPtr;

*((voidFuncPtr)0x100000)();

位域:  

有些資訊在存儲時,并不需要占用一個完整的位元組,而隻需占幾個或一個二進制位。例如在存放一個開關量時,隻有0和1 兩種狀态,用一位二進位即可。為了節省存儲空間,并使處理簡便,C語言又提供了一種資料結構,稱為“位域”或“位段”。所謂“位域”是把一個位元組中的二進位劃分為幾個不同的區域,并說明每個區域的位數。每個域有一個域名,允許在程式中按域名進行操作。這樣就可以把幾個不同的對象用一個位元組的二進制位域來表示。一、位域的定義和位域變量的說明位域定義與結構定義相仿,其形式為:    

struct 位域結構名    

{ 位域清單 };   

其中位域清單的形式為:類型說明符位域名:位域長度    

例如:    

struct bs   

{   

int a:8;   

int b:2;   

int c:6;   

};   

位域變量的說明與結構變量說明的方式相同。可采用先定義後說明,同時定義說明或者直接說明這三種方式。例如:    

}data;   

說明data為bs變量,共占兩個位元組。其中位域a占8位,位域b占2位,位域c占6位。對于位域的定義尚有以下幾點說明:   

1. 一個位域必須存儲在同一個位元組中,不能跨兩個位元組。如一個位元組所剩空間不夠存放另一位域時,應從下一單元起存放該位域。也可以有意使某位域從下一單元開始。例如:    

unsigned a:4   

unsigned :0 /*空域*/   

unsigned b:4 /*從下一單元開始存放*/   

unsigned c:4   

}   

在這個位域定義中,a占第一位元組的4位,後4位填0表示不使用,b從第二位元組開始,占用4位,c占用4位。

2. 由于位域不允許跨兩個位元組,是以位域的長度不能大于一個位元組的長度,也就是說不能超過8位二進位。   

3. 位域可以無位域名,這時它隻用來作填充或調整位置。無名的位域是不能使用的。例如:    

struct k   

int a:1   

int :2 /*該2位不能使用*/   

int b:3   

int c:2   

從以上分析可以看出,位域在本質上就是一種結構類型,不過其成員是按二進位配置設定的。   

二、位域的使用位域的使用和結構成員的使用相同,其一般形式為:位域變量名•位域名位域允許用各種格式輸出。   

main(){   

unsigned a:1;   

unsigned b:3;   

unsigned c:4;   

} bit,*pbit;   

bit.a=1;   

bit.b=7;   

bit.c=15;   

pri

1、頭檔案中的 ifndef/define/endif 幹什麼用?(5分)

答:防止該頭檔案被重複引用。

(2)strcpy能把strSrc的内容複制到strDest,為什麼還要char * 類型的傳回值?

答:為了實作鍊式表達式。

六、編寫類String的構造函數、析構函數和指派函數(25分)

已知類String的原型為:

    class String

    {

      public:

       String(const char *str = NULL); // 普通構造函數

       String(const String &other);        // 拷貝構造函數

       ~ String(void);                     // 析構函數

       String & operate =(const String &other);  // 指派函數

      private:

       char   *m_data;             // 用于儲存字元串

    };

    請編寫String的上述4個函數。

标準答案:

// String的析構函數

   String::~String(void)               // 3分

  delete [] m_data;                      

// 由于m_data是内部資料類型,也可以寫成 delete m_data;

    // String的普通構造函數            

   String::String(const char *str)      // 6分

  if(str==NULL)                         

  {

     m_data = new char[1];    // 若能加 NULL 判斷則更好

     *m_data = ‘\0’;                     

  }                                        

  else

     int length = strlen(str);          

     m_data = new char[length+1];  // 若能加 NULL 判斷則更好     

     strcpy(m_data, str);               

  }

// 拷貝構造函數

   String::String(const String &other)   // 3分

   { 

  int length = strlen(other.m_data);

  m_data = new char[length+1];      // 若能加 NULL 判斷則更好   

  strcpy(m_data, other.m_data);        

// 指派函數

   String & String::operate =(const String &other)    // 13分

      // (1) 檢查自指派                     // 4分

      if(this == &other)

         return *this;

// (2) 釋放原有的記憶體資源            // 3分

      delete [] m_data;

      // (3)配置設定新的記憶體資源,并複制内容 // 3分

  m_data = new char[length+1];         // 若能加 NULL 判斷則更好

      strcpy(m_data, other.m_data);

      // (4)傳回本對象的引用            // 3分

      return *this;

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

來自為知筆記(Wiz)

繼續閱讀