面試題

問答題
用預(yù)處理指令#define 聲明一個(gè)常數(shù),用以表明1年中有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
注意事項(xiàng):

1). #define 語法的基本知識(shí)(例如:不能以分號(hào)結(jié)束,括號(hào)的使用,等等)

2). 懂得預(yù)處理器將為你計(jì)算常數(shù)表達(dá)式的值,因此,直接寫出你是如何計(jì)算一年中有多少秒而不是計(jì)算出實(shí)際的值,是更清晰而沒有代價(jià)的。

3). 意識(shí)到這個(gè)表達(dá)式將使一個(gè)16位機(jī)的整型數(shù)溢出-因此要用到長整型符號(hào)L,告訴編譯器這個(gè)常數(shù)是的長整型數(shù)。

4). 如果你在你的表達(dá)式中用到UL(表示無符號(hào)長整型),那么你有了一個(gè)好的起點(diǎn)。


寫一個(gè)“標(biāo)準(zhǔn)”宏MIN,這個(gè)宏輸入兩個(gè)參數(shù)并返回較小的一個(gè)
#define MIN(A, B) ((A)<=(B) ?(A) : (B))
考查的重點(diǎn):

1). 標(biāo)識(shí)#define在宏中應(yīng)用的基本知識(shí)。這是很重要的,因?yàn)橹钡角度?inline)操作符變?yōu)闃?biāo)準(zhǔn)C的一部分,宏是方便產(chǎn)生嵌入代碼的唯一方法,對于嵌入式系統(tǒng)來說,為了能達(dá)到要求的性能,嵌入代碼經(jīng)常是必須的方法。

2). 三重條件操作符的知識(shí)。這個(gè)操作符存在C語言中的原因是它使得編譯器能產(chǎn)生比if-then-else更優(yōu)化的代碼,了解這個(gè)用法是很重要的。

3). 懂得在宏中小心地把參數(shù)用括號(hào)括起來


預(yù)處理器標(biāo)識(shí)#error的目的是什么?

編譯程序時(shí),只要遇到 #error 就會(huì)跳出一個(gè)編譯錯(cuò)誤,其目的就是保證程序是按照你所設(shè)想的那樣進(jìn)行編譯的。


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

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

while(1) { }

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

for(;;) { }

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

Loop:
…
goto Loop;

用變量a給出下面的定義

a) 一個(gè)整型數(shù)(An integer)
b) 一個(gè)指向整型數(shù)的指針(A pointer to an integer)
c) 一個(gè)指向指針的的指針,它指向的指針是指向一個(gè)整型數(shù)(A pointer to a pointer to an integer)
d) 一個(gè)有10個(gè)整型數(shù)的數(shù)組(An array of 10 integers)
e) 一個(gè)有10個(gè)指針的數(shù)組,該指針是指向一個(gè)整型數(shù)的(An array of 10 pointers to integers)
f) 一個(gè)指向有10個(gè)整型數(shù)數(shù)組的指針(A pointer to an array of 10 integers)
g) 一個(gè)指向函數(shù)的指針,該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(shù)(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個(gè)有10個(gè)指針的數(shù)組,該指針指向一個(gè)函數(shù),該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(shù)( 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


關(guān)鍵字static的作用是什么?
在C語言中,關(guān)鍵字static有三個(gè)明顯的作用:

1). 在函數(shù)體,一個(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)使用。


關(guān)鍵字const是什么含意?下面的聲明都是什么意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

回答:

const是一個(gè)限定關(guān)鍵字,用const定義和聲明的變量為常量,必須在定義時(shí)初始化,在其生存期內(nèi)變量的值不能改變
const修飾基本數(shù)據(jù)類型,此時(shí)這些變量為常量,不能再修改或賦值
const修飾指針,const在*前說明是指向常量的指針,指向內(nèi)容不可變; const在*之后說明指針為常指針,指針值不可變,指向內(nèi)容可變;前后都有const說明指針為指向常量的常指針,指針值和指向內(nèi)容均不可變

前兩個(gè)的作用是一樣,a是一個(gè)常整型數(shù)。第三個(gè)意味著a是一個(gè)指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個(gè)意思a是一個(gè)指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后一個(gè)意味著a是一個(gè)指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時(shí)指針也是不可修改的)。


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

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

1). 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
2). 一個(gè)中斷服務(wù)子程序中會(huì)訪問到的非自動(dòng)變量(Non-automatic variables)
3). 多線程應(yīng)用中被幾個(gè)任務(wù)共享的變量


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

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

define BIT3 (0x1<<3) 
static int a; 
void set_bit3(void) 
{ 
  a |= BIT3; 
} 
void clear_bit3(void) 
{ 
  a &= ~BIT3; 
}

嵌入式系統(tǒng)經(jīng)常具有要求程序員去訪問某特定的內(nèi)存位置的特點(diǎn)。在某工程中,要求設(shè)置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個(gè)純粹的ANSI編譯器,寫代碼去完成這一任務(wù)。

為了訪問一絕對地址把一個(gè)整型數(shù)強(qiáng)制轉(zhuǎn)換(typecast)為一指針是合法的。典型的類似代碼如下:

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

也可以寫為:

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

中斷是嵌入式系統(tǒng)中重要的組成部分,這導(dǎo)致了很多編譯開發(fā)商提供一種擴(kuò)展—讓標(biāo)準(zhǔn)C支持中斷。具代表事實(shí)是,產(chǎn)生了一個(gè)新的關(guān)鍵字__interrupt。下面的代碼就使用了__interrupt關(guān)鍵字去定義了一個(gè)中斷服務(wù)子程序(ISR),請?jiān)u論一下這段代碼的。

__interrupt double compute_area (double radius)
{
  double area = PI * radius * radius;
  printf(” Area = %f”, area);
  return area;
}

這個(gè)函數(shù)有太多的錯(cuò)誤了:

1). ISR 不能返回一個(gè)值。
2). ISR 不能傳遞參數(shù)。
3). 在許多的處理器/編譯器中,浮點(diǎn)一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點(diǎn)運(yùn)算。此外,ISR應(yīng)該是短而有效率的,在ISR中做浮點(diǎn)運(yùn)算是不明智的。
4). 與第三點(diǎn)一脈相承,printf()經(jīng)常有重入和性能上的問題。


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

void foo(void) 
{ 
  unsigned int a = 6; 
  int b = -20; 
  (a+b > 6)? puts("> 6") : puts("<= 6"); 
}

問題的答案是輸出是“>6”。原因是當(dāng)表達(dá)式中存在有符號(hào)類型和無符號(hào)類型時(shí)所有的操作數(shù)都自動(dòng)轉(zhuǎn)換為無符號(hào)類型。因此-20變成了一個(gè)非常大的正整數(shù),所以該表達(dá)式計(jì)算出的結(jié)果大于6


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

unsigned int zero = 0; 
unsigned int compzero = 0xFFFF; 
/*1's complement of zero */

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

unsigned int compzero = ~0;


常見編程題---將字符串反轉(zhuǎn)

void reserverString(char str[]){
  char  tmp;
  int i, j;
  for(i = 0; j = strlen(str)-1; i<j){
      tmp = str[i];
      str[i] = str[j];
      str[j] = tmp;
  }
}

常見編程題---判斷大小端

int main(void)

int main(void)
{
    int a = 0x1234;
    char b = *((char*)&a);
    if (b == 0x12)
        printf("BIGEND\n");
    if (b == 0x34)
        printf("littleEND\n");
     system("PAUSE");
  return 0;
}

常見編程題 ---實(shí)現(xiàn)函數(shù)memcpy(), strcpy(), strcmp(), strcat()

memcpy()


void *memcpy(void *dest, const void *src, size_t n)
{
  if (NULL == dest || NULL == src)
    return NULL;
 
  char* tmp = (char*)dest;
  const char* s = (const char*)src;//這里注意下
  while (n--)
  {
    *tmp++ = *s++;
  }
  return dest;
}

strcpy()


char * strcpy(char *dst,const char *src)   
{
    if((dst==NULL)||(src==NULL))
         
           return NULL; 
 
    char *ret = dst; 
 
    while ((*dst++=*src++)!='\0'); 
 
    return ret;
}

strcmp()

char* strcat(char *dest,const char *src)
{
  for (; *dest == *src; dest++, src++)//注意for循環(huán),能循環(huán)下去的條件的兩者元素對應(yīng)相等,一旦不相等立即退出
    if (*dest == '\0')
      return 0;//表示相等
  return *dest - *src;
}

strcat

char* strcat(char *dest,const char *src)
{
  char* tmp = dest;
  assert((NULL != dest) && (NULL != src));
  while (*tmp)
  {
    tmp++;
  }//循環(huán)結(jié)束時(shí),指針指向字符'\0',后面會(huì)覆蓋
  while (*tmp++ = *src++) 
  {
  }
  return dest;
}

常見編程題---設(shè)計(jì)函數(shù) int atoi(char *s),void itoa(int n, char s[])

{
  int i, n, sign;
  for (i = 0; isspace(s[i]); i++); /*跳過空白符:空格時(shí)會(huì)一直循環(huán),直到遇到不是空格*/
 
  sign = (s[i] == '-') ? -1 : 1;//遇到的如果不是-號(hào),比如是數(shù)字,則說明不是負(fù)數(shù)
 
  if (s[i] == '+' || s[i] == '-') /* 跳過符號(hào) */
    i++;
 
  for (n = 0; isdigit(s[i]); i++)
    n = 10 * n + (s[i] - '0');
  return sign * n;
}//注意判斷空格、是否是數(shù)字的函數(shù),你懂寫嗎?

void itoa(int n, char s[])

void itoa(int n, char s[])
{
  int i, sign;
 
  if ((sign = n) < 0) //記錄符號(hào)
    n = -n; //使n成為正數(shù)
  
  i = 0;
  do 
  { //以反序生成數(shù)字          //假設(shè)n=987654
    s[i++] = n % 10 + '0'; //n % 10 + '0'表示數(shù)字對應(yīng)的字符,比如第一次的數(shù)字是4,變成字符4,是4+'0'
                           
  } while ((n /= 10) > 0);   //此時(shí)n變成了98765
  
  if (sign < 0)
    s[i++] = '-';
  s[i] = '\0';
  
  reverse(s);
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容