c語言面試常見題

1、typedef

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

想看http://baike.baidu.com/view/1283800.htmc++

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類型*/函數

2、按位運算小技巧

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;

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

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

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

3、編譯宏

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

4、指針

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

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

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

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

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

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

5、限定符的用法

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;/*能夠進行替換*/

三、register

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

6、精度和優先級

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 
儘可能把數量級相近的數先相加

 

7、大小端問題

  大端模式:是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中。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 
// 大字節序

8、內存

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段,堆棧等在程序運行時建立

9、中斷

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會引用全局的內存分配表。

10、鏈表

一個鏈表不知道頭結點,有一個指針指向其中一個結點,請問如何刪除這個指針指向的結點:將這個節點複製成下一個節點的值,而後刪除下一個節點

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

11、內聯函數

內聯函數(inline

帶參數的宏

編譯(彙編)時展開 

預編譯時替換

嚴格的參數類型檢查   

簡單的替換

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

沒要求

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

相關文章
相關標籤/搜索