原文連接: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 }
只要瞭解靜態局部變量的真正內涵,這道題就是小菜啦。