有意思的12道C面試題及答案

有意思的12道C面試題及答案

這篇文章涉及到的都是一些C語言的基礎知識,是從一篇英文文章中部分翻譯和加入我自己的認識!!!!

1. gets()函數
Q:下面的代碼中隱含着安全問題,能發現嗎?

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

 

A:問題在於gets()函數,這個函數是接收標準輸入的一串字符串,並且沒有檢查字符串緩衝區的大小就
直接拷貝到buff數組中,這可能導致在寫入buff內存時溢出,可以使用fgets()函數代替這個函數,

char *fgets( char *str,int n,FILE *stream );

 

2.strcpy()函數
Q:下面代碼是一個密碼驗證的過程,是否可以在不知道密碼的情況下驗證通過

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

 

A:strcpy()函數沒有驗證輸入的字符串長度,所有在執行時可能出現寫內存出現溢出,這很危險,如這代碼,
flag是初始化爲0的,當內存溢出時可能會寫到flag內存中,這會使得flag內存不爲0,即使不執行if語句的
比較flag也爲真,所以就相當於密碼正確

如: $ ./psswd aaaaaaaaaaaaa
輸出: Password cracked

可以使用strncpy()函數代替
現在編譯器也發現這種情況,所以在爲程序分配內存時是分散的分配內存,如果要看到上面執行的情況,使用gcc的話
可以使用 ‘-fno-stack-protector’參數(我沒驗證)

3.main()函數的返回類型
Q:下面代碼是否能編譯通過?是否還存在其他問題

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

A:對於現在的編譯器這段代碼是可以編譯通過的,不過是會有警告,main()返回類型最好使用int類型,
當一個函數執行結束時最後返回一個狀態值,現在C/C++返回一個0值表示程序正常退出,否則有異常.

4.內存泄露
Q:下面代碼執行結果會出現內存泄露嗎?

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

A:其實內存泄露是個很嚴重的問題,其實上面代碼執行結果不會出現內存泄露,雖然沒有使用free()
回收內存,但是當程序執行結束後程序裏分配的內存會自動釋放,如果是分配的內存放到一個死循環裏
就會出現嚴重的內存泄露,或者程序一直執行着,動態分配的內存會一直佔有着無法釋放.
有篇文章介紹了內存泄露的檢測方法:http://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html

5.free()函數
Q:下面代碼執行時輸入如 ‘freeze’會崩潰但輸入如t ‘zebra’就不會爲什麼?

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

A:這個問題主要是指針移到的問題,當輸入如‘zebra’這樣的字符串('z'開頭)時,ptr指針沒有移到,所以
ptr指針指向的內存還是malloc分配的原內存的起始地址,但是輸入‘freeze’時,ptr指針移到了,已經不是指向原來
分配的內存的起始地址了,所有free時就會出錯

題外話:在實現如strcpy的函數時,如下

複製代碼
 1 char *strcpy(char *strDest, const char *strSrc)
 2 {
 3     assert((strDest != NULL) && (strSrc != NULL));
 4     
 5     if(strDest == strSrc)
 6     return strDest; //注意這個.. 
 7 
 8     char *pstr = strDest; //保存原始地址
 9     while((*strDest++ = *strSrc++) != '\0');
10     return pstr;
11 }
複製代碼

定義了一個指針pstr保存了原始地址,然後再執行移到操作,結束後再返回寫入的原始地址以便進行鏈式操作,
我實現這個函數的時候就是出現了一個很二的錯誤,就是移到操作後直接返回strDest指針,現在的strDest指針
已經移到字符串末尾了,如果那樣的話要釋放strDest內存就會出錯了

6.atexit和_exit
Q:下面代碼中,atexit()函數沒有被調用,why?

複製代碼
 1 #include<stdio.h>
 2 void func(void)
 3 {
 4     printf("\n Cleanup function called \n");
 5     return;
 6 }
 7 
 8 int main(void)
 9 {
10     int i = 0;
11 
12     atexit(func);
13 
14     for(;i<0xffffff;i++);
15 
16     _exit(0);
17 }
複製代碼

A:主要是因爲_exit()函數,這個函數是直接終止程序,沒有調用一些如atexit()的清理函數,要想調用
atexit()函數,就需要調用exit()函數或者return返回操作退出程序.

MSDN給出了exit()和_exit()函數的區別

The exit and _exit functions terminate the calling process. exit calls, in last-in-first-out (LIFO) order, <BR>the functions registered by atexit and _onexit, then flushes all file buffers 
before terminating the process. _exit terminates the process without processing atexit 
or _onexit or flushing stream buffers. The status value is typically set to 0 to indicate
a normal exit and set to some other value to indicate an error

7.void* 和 C 結構體
Q:能否設計一個函數能夠接受任何類型的參數? 也可以傳遞多個參數給這個函數?
A:

int func(void *ptr);

傳遞的時候需要強制轉換爲void*類型,到函數內以後再強制轉換回來,如果要傳多個參數,可以定義一個
結構體,將要傳遞的參數放到結構體裏,定義一個結構體對象,對成員賦值後,將對象傳給函數.
注:我不知道在哪裏看到有人說C/C++是不安全的語言,因爲可以類型強制轉換,但在這裏發現強制轉換
帶來的好處.


8. * 和 ++ 操作
Q:下面的代碼輸出的結果是什麼? and Why?

複製代碼
 1 #include<stdio.h>
 2 
 3 int main(void)
 4 {
 5     char *ptr = "Linux";
 6     printf("\n [%c] \n",*ptr++);
 7     printf("\n [%c] \n",*ptr);
 8 
 9     return 0;
10 }
複製代碼

A:輸入的結果:
[L]
[i]
Why? 這個涉及到*號和++的優先級問題,*和++的優先級是一樣的,編譯器在識別時是先右後左,
所有先檢測到++後到*,不過++是在ptr後面,所以自加在*ptr後執行,輸出L,然後移動到i處,下
一條語句執行的時候輸出i.
原作者給出來的解釋我不太理解
(ptr++ is evaluated first and then *ptr. So both these operations result in ‘L’),
爲什麼說兩個操作結果都是'L'???

9.改變代碼區(只讀區)
Q:下面代碼爲什麼會崩潰?

複製代碼
 1 #include<stdio.h>
 2 
 3 int main(void)
 4 {
 5     char *ptr = "Linux";
 6     *ptr = 'T';
 7 
 8     printf("\n [%s] \n", ptr);
 9 
10     return 0;
11 }
複製代碼

A:因爲 *ptr = 'T' 操作嘗試改變在代碼區的 "Linux"的字符串,這是不合法的,其實如果要改變的話
可以先動態分配一塊內存(在堆區),然後再Copy "Linux"到這塊內存中,可以就可以執行*ptr = 'T'操作

10.程序改變自己的名字
Q:如何實現一個程序在運行的時候改變自己的名字
A:這個需要知道main()函數的兩個參數的意義了

複製代碼
 1 #include<stdio.h>
 2 #include <string.h>
 3 int main(int argc, char *argv[])
 4 {
 5     int i = 0;
 6     char buff[100];
 7 
 8     memset(buff,0,sizeof(buff));
 9 
10     strncpy(buff, argv[0], sizeof(buff));
11     memset(argv[0],0,strlen(buff));
12 
13     strncpy(argv[0], "NewName", 7);
14 
15     // Simulate a wait. Check the process
16     // name at this point.
17     for(;i<0xffffffff;i++);
18 
19     return 0;
20 }
複製代碼

main()函數的argc表示的是傳進來的參數個數,argv[]保存的是參數的內容,但是
argv[0]保存的是程序自己名字

11.返回局部變量地址
Q:下面的代碼是否存在問題?如果有的話那如何修改?

複製代碼
 1 #include<stdio.h>
 2 
 3 int* inc(int val)
 4 {
 5   int a = val;
 6   a++;
 7   return &a;
 8 }
 9 
10 int main(void)
11 {
12     int a = 10;
13 
14     int *val = inc(a);
15 
16     printf("\n Incremented value is equal to [%d] \n", *val);
17 
18     return 0;
19 }
複製代碼

A:問題是返回了一個局部變量的地址,a的作用域只在inc()函數中,函數結束後a的內存會釋放,如果使用
一個已經被釋放了的內存相當危險,解決方法可以傳給inc()函數的參數修改爲傳地址或者引用(C++)
不使用值傳遞,int* inc(int *val)或者int* inc(int &val)

12.printf()函數參數的執行
Q:代碼的輸出結果是什麼?

複製代碼
 1 #include<stdio.h>
 2 
 3 int main(void)
 4 {
 5     int a = 10, b = 20, c = 30;
 6 
 7     printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2));
 8 
 9     return 0;
10 }
複製代碼

A:輸出的結果爲
110..40..60
函數的參數是從右到左執行的,但是打印是從左到右的

發佈了31 篇原創文章 · 獲贊 9 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章