一套幫助你理解C語言的測試題(轉)

前言

      原文連接:http://www.nowamagic.net/librarys/veda/detail/775html

內容

      在這個網站(http://stevenkobes.com/ctest.html)上發現一套頗有趣的C語言測試題,若是你招聘C語言相關開發人員,或者正在學習C語言,很值得作一作。node

若是沒有作,下面內容暫時不要看,最好本身先完成一遍。數組

      OK,假設你作的答案沒有徹底正確,那你能夠繼續看下去了,不然,後面內容對你來講就是小菜一碟,不值得看。 函數

      第一題    

 1 #include <setjmp.h>
 2 
 3 static jmp_buf buf;
 4 
 5 int main(void)
 6 {
 7     volatile int b = 3;
 8     if (setjmp(buf) != 0)
 9     {
10         printf("%dn", b);
11         exit(0);
12     }
13     b = 5;
14     longjmp(buf, 1);
15 }

      輸出結果爲A)3 B)5 C)0 D)都不是學習

      答案爲B,也就是輸出5。測試

      關鍵點在於理解setjmp以及longjmp,(http://en.wikipedia.org/wiki/Setjmp.h )第一次運行到setjmp,會設置jmp_buf,而後返回0。當調用longjmp時,會把longjmp裏面的非0值做爲setjmp的返回值返回(若是longjmp的value參數爲0,setjmp恢復後返回1,也就是當恢復到setjmp存儲點的時候,setjmp必定不會返回0)。網站

      setjmp-longjmp組合的用處相似於遊戲中的存盤讀盤功能,常常被用於相似C++的異常恢復操做。spa

      第二題

struct node
{
    int a;      
    int b;      
    int c;
};
struct node s = { 3, 5, 6 };
struct node *pt = &s;
printf("%dn", *(int*)pt);

     返回結果爲3,這個算是比較簡單,pt爲指向結構s的指針,而後將pt轉換爲int指針,進行dereference,取出一個int值,那就是結構中第一個數。.net

     咱們將題目改動一下,以下代碼:debug

 1 struct node
 2 {
 3     char a;   
 4     char b;  
 5     short c;   
 6     int d;
 7 };
 8 
 9 struct node s = { 3, 5, 6, 99 };
10 struct node *pt = &s;
11 printf("%Xn", *(int*)pt);

      須要注意的是通常32位C編譯器都認爲char是8bit,short是16bit,int爲32bit,因此node在內存中應該正好是對齊的,也就是abc這幾個成員之間沒有空隙。最終結果應該爲60503,若是不是,歡迎你告訴我你具體的編譯環境以及硬件配置。

      第三題

 1 int foo(int x, int n)
 2 {
 3     int val = 1;
 4     if (n > 0)
 5     {
 6         if (n % 2 == 1) val *= x;
 7         val *= foo(x * x, n / 2);
 8     }
 9     return val;
10 }

      這道題其實最簡單的辦法就是在紙上作一個推演計算,一步一步跑一下,就能獲得答案了,這裏面沒有任何複雜的C語言概念。

      第四題

1 int a[5] = { 1, 2, 3, 4, 5 };
2 int *ptr = (int*)(&a + 1);
3 printf("%d %dn", *(a + 1), *(ptr – 1));

     這道題考的實際上是指向數組的指針,&a是一個隱式的指向int [5]數組的指針,它和int* ptr是不同的,若是真要定義這個指針,應該是int (*ptoa)[5]。因此ptoa每一次加一操做都至關於跨越int a[5]的內存步長(也就是5個int長度),也就是說&a + 1其實就是指向了a[5]這個位置,實際上內存裏面這個位置是非法的,可是對ptr的強制轉換致使了後面ptr-1的內存步長改成了1個int長度,因此ptr-1實際指向了a[4]。至於*(a+1)沒什麼好說的,值就是2。

     第五題

 1 void foo(int[][3]);
 2 
 3 int main(void)
 4 {
 5     int a[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
 6     foo(a);
 7     printf("%dn", a[2][1]);
 8     return 0;
 9 }
10 
11 void foo(int b[][3])
12 {
13     ++b;
14     b[1][1] = 9;
15 }

     其實和前一題有殊途同歸之妙,++b的步長其實是3個int,也就是++b運算之後,b指向{4,5,6}這個數組的開始,而b[1]就是{7,8,9}, b[1][1]實際上就是8這個值也就是main函數中的a[2][1].

     第六題

1 int a, b, c, d;
2 a = 3;
3 b = 5;
4 c = a, b;
5 d = (a, b);
6 printf("c=%d  ", c);
7 printf("d=%dn", d);

    這個其實有兩個C語言知識點,一個是等號操做符優先級高於逗號操做符,另外一個是逗號操做符至關於運算逗號前半部後半部的表達式,而後返回後半部表達式的值。因此c等於a(先計算等號),而d等於b(逗號表達式返回b)。

     第七題

1 int a[][3] = {1, 2, 3, 4, 5, 6};
2 int (*ptr)[3] = a;
3 printf("%d %d ", (*ptr)[1], (*ptr)[2]);
4 ++ptr;
5 printf("%d %dn", (*ptr)[1], (*ptr)[2]);

     依然是2維數組相關題目,ptr爲指向int [3]數組的指針,首先指向a[0],因此(*ptr)[1], (*ptr)[2]就是a[0][1], a[0][2].而後++ptr,至關於ptr指向了a[1],這時獲得的是a[1][1],a[1][2],因此結果就是2,3, 5, 6。

      第八題

 1 int *f1(void)
 2 {
 3     int x = 10;   return &x;
 4 }
 5 
 6 int *f2(void)
 7 {
 8     int *ptr;   *ptr = 10;   return ptr;
 9 }
10 
11 int *f3(void)
12 {
13     int *ptr;   ptr = malloc(sizeof *ptr);   return ptr;
14 }

      這裏考的是返回一個指針的問題,通常來講返回指針的函數,裏面必定有malloc之類的內存申請操做,傳入指針類型,則是對指針指向的內容作修改。若是想修改指針自己,那就要傳入指針的指針。

      第九題

1 int i = 3;   int j;
2 j = sizeof(++i + ++i);
3 printf("i=%d j=%dn", i, j);

      這道題考的內容其實就是sizeof,若是計算表達式,那麼表達式是不會作計算的,也就是無論加加減減,sizeof就是針對i計算大小。在32位機器上,這個j應該爲4。

      我將代碼擴展了一下,看看你們能不能想到結果:

 1 short m;    
 2 int n;     
 3 double dn;
 4 
 5 int j = sizeof ( m + n);
 6 int k = sizeof ( n + n);
 7 int l = sizeof ( m);
 8 int l2 = sizeof (m * m);
 9 int l3 = sizeof (m + dn);
10 int l4 = sizeof (m + m);

        第十題

 1 void f1(int*, int);
 2 void (*p[2])(int*, int);
 3 
 4 int main(void)
 5 {
 6     int a = 3;
 7     int b = 5;
 8     p[0] = f1;
 9     p[1] = f1;
10     p[0](&a, b);
11     printf("%d %d ", a, b);
12     p[1](&a, b);
13     printf("%d %dn", a, b);
14     return 0;
15 }
16 
17 void f1(int *p, int q)
18 {
19     int tmp = *p;   *p = q;   q = tmp;
20 }

      函數指針的數組p勉強算是一個知識點,另一個知識點就是第八題提到的,對於int q這樣的參數,是不會修改其內容的。而*p則可修改p指向的內容。

      第十一題

 1 void e(int);
 2 
 3 int main(void)
 4 {
 5     int a = 3;
 6     e(a);
 7     putchar('n');
 8     return 0;
 9 }
10 
11 void e(int n)
12 {
13     if (n > 0)
14     {
15         e(–n);
16         printf("%d ", n);
17         e(–n);
18     }
19 }

      這道題本身debug一下就徹底明白了,主要知識點就是遞歸調用,另外前置後置自減操做的返回值問題。

      第十二題

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

      也是常常出現的一類題,對複雜的指針定義作解析,實際上K&R裏面(5.12)也有介紹該如何解讀。不熟悉的朋友能夠試着練習練習標準庫中的bsearch,qsort以及signal函數。

      第十三題

1 char p;
2 char buf[10] = {1, 2, 3, 4, 5, 6, 9, 8};
3 p = (buf + 1)[5];
4 printf("%dn", p);

      也就是p實際指向*(buf + 1 + 5),寫的更詭異一些就是p=5[buf +1];也是一樣結果。

       第十四題

       相似十三題,也是把數組弄得有些詭異,(p += sizeof(int))[-1];至關於*(p + sizeof(int) + (-1))。

       第十五題

 1 int ripple(int n, …)
 2 {
 3     int i, j, k;
 4     va_list p;
 5     k = 0;
 6     j = 1;
 7     va_start(p, n);
 8     for (; j < n; ++j)
 9     {
10         i = va_arg(p, int);
11         for (; i; i &= i – 1)
12         ++k;
13     }
14     return k;
15 }
16 
17 int main(void)
18 {
19     printf("%dn", ripple(3, 5, 7));
20     return 0;
21 }

      這道題也是兩個知識點,一個是可變參數函數定義以及如何實現,va_arg會把5,7依次取出來。另外一個知識點是i &= i-1,其實是計算了i二進制形式中1的個數,每次計算都會消減掉最低有效位上的1。好比7二進制表示爲111。i &= i –1的計算結果依次爲110,100, 000 (也就是0)。在hacker's Delights這本書裏介紹了不少相似技巧。

      第十六題 

 1 int counter(int i)
 2 {
 3     static int count = 0;
 4     count = count + i;
 5     return count;
 6 }
 7 
 8 int main(void)
 9 {
10     int i, j;
11     for (i = 0; i <= 5; i++)  j = counter(i);
12     printf("%dn", j);
13     return 0;
14 }

      只要瞭解靜態局部變量的真正內涵,這道題就是小菜啦。

相關文章
相關標籤/搜索