c語言面試常見題


一、typedef

為現有類型創建一個新的名字, 使用最多的地方是創建易於記憶的類型名
typedef int size;此聲明定義了一個 int 的同義字,名字為 size

想看http://baike.baidu.com/view/1283800.htm

12:考查typedef類型定義,函數指針

1typedef int (*test) ( float * , float*)
test tmp;

 tmp 的類型是
(a) 函數的指針,該函數以 兩個指向浮點數(float)的指針(pointer)作為參數(arguments)
      Pointer to function of having two arguments that is pointer to float
(b) 整型
(c) 函數的指針,該函數以 兩個指向浮點數(float)的指針(pointer)作為參數(arguments),並且函數的返回值類型是整型 
      Pointer to function having two argument that is pointer to float and return int
(d) 以上都不是

另外一例:

typedef void*ptr_tp_func(int);/*它表示ptr_tp_func是函數指針,該函數接受一個int參數,返回值為void*/

ptr_tp_func signal(int, ptr_tp_func);/*表示signal是一個函數,接受兩個參數,一個int和一個ptr_tp_func,返回值是ptr_tp_func類型*/

二、按位運算小技巧

1統計i有多少位為1for( ; i ; i&=i-1) k++;

2宏寫出swap(x,y)

 #define swap(x,y)

      x=x+y;      y=x-y;      x=x-y;

3、一句實現判斷x是不是2的若干次冪:x&(x-1)false:true;

4、實現左移循環移n位:a=(a<<n)|(a>>(8*sizeof(int)-n));

5、快速求一個數的7倍:X<<3-X

三、編譯宏

      #define     定義宏
         #undef      取消已定義的宏
         #if         如果給定條件為真,則編譯下面代碼
         #ifdef      如果宏已經定義,則編譯下面代碼
         #ifndef     如果宏沒有定義,則編譯下面代碼
         #elif       如果前面的#if給定條件不為真,當前條件為真,則編譯下面代碼
         #endif      結束一個#if……#else條件編譯塊
         #error      停止編譯並顯示錯誤信息

四、指針

1 10個指針的數組 int *p[10];

     int (*p)[10]p是一個指針。它的類型是:指向int x[10]這樣的一維數組的指針。

    指向函數的指針,函數有一個整型參數並返回一個整數型 int (*p)(int);

一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數 int (*p[10])(int);

2、char a[9]="abcdefg";     char *p="abcdefg";

其它區別:1.指針保存數據的地址,如p保存的是常量字符串“abcdefg”地址,對p內容的修改是非法的 ;數組保存的是數據,對數組的內容可以直接修改。

五、限定符的用法

1const  

 1)作用:a.告知參數的應用目的

                 b.使優化器產生緊湊的代碼

                 c.保護不希望被改變的參數:防止參數意外改變

   2)例子:a.  const int a;  a是一整型常量

                  b. int const a;  同上

                 c. const int *a; 指針a指向的內容不可變

                 d. int * const a;指針a指向的內容可變,但指針不可變

                 e. int const * a const; 都不可變

面試的問題:

     a.一個參數既可以是const又是volatile嗎?

     b.一個指針可以是volatile嗎?

    答:a.可以,例子:只讀的狀態寄存器,它是volatile因為可能被意想不到的改變,是const因為不希望程序試圖去改變它。

           b.可以。一個例子是中斷服務子程序修改一個指向一個buffer的指針時。

此類考題記住volatile型變量可能隨時改變即可。

2restrict

restrict關鍵字允許編譯器優化某部分代碼以更好地支持計算。它只能用於指針,表明該指針是訪問該對象唯一且初始的方式。

int ar[10];

int * restrict restar= (int *) malloc(10 * sizeof(int));

 int * par= ar;

for (n=0; n<10; n++) {

par[n]+=5;

restar[n] +=5;

ar[n] *=2;

par[n] +=3;

restar[n] +=3;

 }

restar是訪問它所指向的數據塊的唯一且初始的方式,編譯器可以把涉及restar的兩條語句替換成下面的語句,效果相同:

restar[n] +=8;/*可以進行替換*/

3、register

register關鍵字請求讓編譯器將變量a直接放入寄存器里面,以提高讀取速度,因為register變量可能不存放在內存中,C語言中register關鍵字修飾的變量不可以被取地址,但是c++中進行了優化。

六、精度和優先級

1類型不同的操作數運算,精度向低級自動轉換。無符號+有符號=無符號

2.int a=12,則執行完語句 a+=a-=a*a后,a的值是-264.

所有的賦值符(包括復合賦值)都具有右結合性,就是在表達式中最右邊的操作最先執行,然后從右到左依次執行。

3c中函數參數默認是從右向左壓棧的,printf計算參數時也是

     int arr[]={6,7,8,9,10};     int *ptr=arr;    *(ptr++)+=123;

     printf("%d,%d\n",*ptr,*(++ptr));結果為8

  printf內的參數從右向左運算。

4位運算要考慮機器字長,算術運算符優先級高於移位運算符

unsigned char = 0xA5;   unsigned char  b = ~a>>4+1; b250.   

5三個float:a,b,c .問值 (a+b)+c==(b+a)+c (a+b)+c==(a+c)+b

不一定相等,float存在大數淹沒小數的情況,如 
float   a=100000000000,b=-100000000000,c=0.00000000001; 
cout < <a+b+c < <endl; 
cout < <a+c+b < <endl; 
結果是1e-0110 
盡量把數量級相近的數先相加

 

七、大小端問題

  大端模式:是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中。armpowerpc

  小端模式:是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中。intelx86

 

大小端測試程序1

       int checkSystem( )

       {union check

          {int i;    char ch;

          }c;       c.i=1;        return (c.ch==1);

       }

 測試大小端的程序2

 

在使用little endian的系統中 這些函數會把字節序進行轉換

 define HTONS(n) ((((u16_t)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))

ltons unsigned short類型從主機序轉換到網絡序

htonl unsigned long類型從主機序轉換到網絡序

ntohs unsigned short類型從網絡序轉換到主機序

ntohl unsigned long類型從網絡序轉換到主機序

在使用big endian類型的系統中 這些函數會定義成空宏

利用linux中自帶的宏進行判斷:

#if __BYTE_ORDER == __LITTLE_ENDIAN 
// 小頭字節序 
#elif __BYTE_ORDER == __BIG_ENDIAN 
// 大字節序

八、內存

1、申請內存的函數

void *calloc ( size_t num_elements, size_t element_size );

void *realloc (void *ptr, size_t new_size );

realloc函數用於修改一個原先已經分配的內存塊的大小,可以使一塊內存的擴大或縮小。當起始空間的地址為空,即*ptr = NULL,則同malloc。callocmalloc相比:calloc分配的內存被初始化為0calloc兩個參數:元素個數,元素大小

2、內存分布

BSS段:未初始化的全局變量和靜態變量;數據段:初始化的全局變量和靜態變量;代碼段(或文本段):可執行文件的指令;局部變量運行時創建並存儲於棧,函數結束即釋放;靜態變量和全局變量則在程序結束后釋放;可執行文件中包含BSS段所需要的大小,但不是BSS段,堆棧等在程序運行時創建

九、中斷

1 找出下面一段ISR的問題。
      __interrupt double compute_area (double radius)
  {
      double area = PI * radius * radius;
      printf("\nArea = %f", area);
      return area;
  }
 1ISR不能傳遞參數。
 2ISR不能有返回值。
 3ISR應該短且有效率,在ISR中做浮點運算不明智。

4ISR中不應該有重入和性能上的問題,因此不應該使用pintf()函數。

不可重入函數不可以在它還沒有返回就再次被調用。例如printfmallocfree等都是不可重入函數。因為中斷可能在任何時候發生,例如在printf執行過程中,因此不能在中斷處理函數里調用printf,否則printf將會被重入。 函數不可重入大多數是因為在函數中引用了全局變量。例如,printf會引用全局變量stdoutmallocfree會引用全局的內存分配表。

十、鏈表

一個鏈表不知道頭結點,有一個指針指向其中一個結點,請問如何刪除這個指針指向的結點:將這個節點復制成下一個節點的值,然后刪除下一個節點

typedef struct LinkList {

         int Element;

         LinkList * next;

}LinkList;

初始化:

linklist *  List_init(){

    linklist *HeadNode= (linklist*)malloc(sizeof(linklist));

    if(HeadNode == NULL) {

        printf("空間緩存不足");

        return HeadNode;

    }

    HeadNode->Element= 0;

    HeadNode->next= NULL;

Return HeadNode;

}

創建鏈表:

void CreatList(linklist *HeadNode,int *InData,int DataNum)

{

    int i = 0;

    linklist *CurrentNode = (linklist*) HeadNode;

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

    {

        CurrentNode->Element = InData[i];

        if(i< DataNum-1)// 由於每次賦值后需要新建結點,為了保證沒有多余的廢結點

        {

            CurrentNode->next =(linklist *)malloc(sizeof(linklist));

            CurrentNode= CurrentNode->next;

        }

    }

    CurrentNode->next= NULL;

}

插入節點:

bool InsertList(linklist *HeadNode,int LocateIndex,int InData){

    int i=1;// 由於起始結點HeadNode是頭結點,所以計數從1開始

    linklist *CurrentNode= (linklist *) HeadNode;

    //CurrentNode指向待插入位置的前一個結點(index -1

    while(CurrentNode&& i<LocateIndex-1){

        CurrentNode= CurrentNode->next;

        i++;

    }

    linklist *NodeToInsert=(linklist*)malloc(sizeof(linklist));

    if(NodeToInsert == NULL){

        printf("空間緩存不足");

        return ERROR;

    }

    NodeToInsert->Element= InData;

    NodeToInsert->next = CurrentNode->next;

    CurrentNode->next = NodeToInsert;

    return OK;

}

具體參考:https://blog.csdn.net/u012531536/article/details/80170893

十一、內聯函數

內聯函數(inline

帶參數的宏

編譯(匯編)時展開 

預編譯時替換

嚴格的參數類型檢查   

簡單的替換

函數體不能太大,不能包含大循環和遞歸

沒要求

當編譯器發現某段代碼在調用一個內聯函數時,它不是去調用該函數,而是將該函數的代碼,整段插入到當前位置。這樣做的好處是省去了調用的過程,加快程序運行速度。內聯函數適用於函數體較小且頻繁使用的函數,調用時進行代碼的復制,無需跳轉、入棧等操作,以空間換時間而且因為函數有嚴格的參數類型檢查,比宏要安全;內聯函數在編譯時不單獨產生代碼  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM