1. 程式人生 > >12個C語言面試題

12個C語言面試題

1.gets()函式

問:請找出下面程式碼裡的問題:

  1. #include<stdio.h>
  2. int main(void
  3.     char buff[10]; 
  4.     memset(buff,0,sizeof(buff)); 
  5.     gets(buff); 
  6.     printf("\n The buffer entered is [%s]\n",buff); 
  7.     return 0; 

答:上面程式碼裡的問題在於函式gets()的使用,這個函式從stdin接收一個字串而不檢查它所複製的快取的容積,這可能會導致快取溢位。這裡推薦使用標準函式fgets()代替。

2.strcpy()函式

問:下面是一個簡單的密碼保護功能,你能在不知道密碼的情況下將其破解嗎?

  1. #include<stdio.h>
  2. int main(int argc, char *argv[]) 
  3.     int flag = 0; 
  4.     char passwd[10]; 
  5.     memset(passwd,0,sizeof(passwd)); 
  6.     strcpy(passwd, argv[1]); 
  7.     if(0 == strcmp("LinuxGeek", passwd)) 
  8.     { 
  9.         flag = 1; 
  10.     } 
  11.     if(flag) 
  12.     { 
  13.         printf("\n Password cracked \n"
    ); 
  14.     } 
  15.     else
  16.     { 
  17.         printf("\n Incorrect passwd \n"); 
  18.     } 
  19.     return 0; 

答:破解上述加密的關鍵在於利用攻破strcpy()函式的漏洞。所以使用者在向“passwd”快取輸入隨機密碼的時候並沒有提前檢查“passwd”的容量是否足夠。所以,如果使用者輸入一個足夠造成快取溢位並且重寫“flag”變數預設值所存在位置的記憶體的長“密碼”,即使這個密碼無法通過驗證,flag驗證位也變成了非零,也就可以獲得被保護的資料了。例如:

  1. $ ./psswd aaaaaaaaaaaaa 
  2. Password cracked 

雖然上面的密碼並不正確,但我們仍然可以通過快取溢位繞開密碼安全保護。

要避免這樣的問題,建議使用 strncpy()函式。

作者注:最近的編譯器會在內部檢測棧溢位的可能,所以這樣往棧裡儲存變數很難出現棧溢位。在我的gcc裡預設就是這樣,所以我不得不使用編譯命令‘-fno-stack-protector’來實現上述方案。

3.main()的返回型別

問:下面的程式碼能 編譯通過嗎?如果能,它有什麼潛在的問題嗎?

  1. #include<stdio.h>
  2. void main(void
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return
  8.     } 
  9.     else
  10.     { 
  11.         // Do some processing
  12.         free(ptr); 
  13.     } 
  14.     return

答:因為main()方法的返回型別,這段程式碼的錯誤在大多數編譯器裡會被當作警告。main()的返回型別應該是“int”而不是“void”。因為“int”返回型別會讓程式返回狀態值。這點非常重要,特別當程式是作為依賴於程式成功執行的指令碼的一部分執行時。

4.記憶體洩露

問:下面的程式碼會導致記憶體洩漏嗎?

  1. #include<stdio.h>
  2. void main(void
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return
  8.     } 
  9.     else
  10.     { 
  11.         // Do some processing
  12.     } 
  13.     return

答:儘管上面的程式碼並沒有釋放分配給“ptr”的記憶體,但並不會在程式退出後導致記憶體洩漏。在程式結束後,所有這個程式分配的記憶體都會自動被處理掉。但如果上面的程式碼處於一個“while迴圈”中,那將會導致嚴重的記憶體洩漏問題!

提示:如果你想知道更多關於記憶體洩漏的知識和記憶體洩漏檢測工具,可以來看看我們在Valgrind上的文章。

5.free()函式

問:下面的程式會在使用者輸入'freeze'的時候出問題,而'zebra'則不會,為什麼?

  1. #include<stdio.h>
  2. int main(int argc, char *argv[]) 
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return -1; 
  8.     } 
  9.     elseif(argc == 1) 
  10.     { 
  11.         printf("\n Usage  \n"); 
  12.     } 
  13.     else
  14.     { 
  15.         memset(ptr, 0, 10); 
  16.         strncpy(ptr, argv[1], 9); 
  17.         while(*ptr != 'z'
  18.         { 
  19.             if(*ptr == ''
  20.                 break
  21.             else
  22.                 ptr++; 
  23.         } 
  24.         if(*ptr == 'z'
  25.         { 
  26.             printf("\n String contains 'z'\n"); 
  27.             // Do some more processing
  28.         } 
  29.        free(ptr); 
  30.     } 
  31.     return 0; 

答:這裡的問題在於,程式碼會(通過增加“ptr”)修改while迴圈裡“ptr”儲存的地址。當輸入“zebra”時,while迴圈會在執行前被終止,因此傳給free()的變數就是傳給malloc()的地址。但在“freeze”時,“ptr”儲存的地址會在while迴圈裡被修改,因此導致傳給free()的地址出錯,也就導致了seg-fault或者崩潰。

6.使用_exit退出

問:在下面的程式碼中,atexit()並沒有被呼叫,為什麼?

  1. #include<stdio.h>
  2. void func(void
  3.     printf("\n Cleanup function called \n"); 
  4.     return
  5. int main(void
  6.     int i = 0; 
  7.     atexit(func); 
  8.     for(;i<0xffffff;i++); 
  9.     _exit(0); 

這是因為_exit()函式的使用,該函式並沒有呼叫atexit()等函式清理。如果使用atexit()就應當使用exit()或者“return”與之相配合。