C語言面試那些事兒──一道指針與數組問題


首先看如下代碼:

1 int main(int argc, char** argv)
2 {
3     int a[5] = {1,2,3,4,5};
4     int* ptr = (int*)(&a + 1);
5     printf("%d,%d\n", *(a+1), *(ptr-1));
6     return 0;
7 }

這道題在很多所謂經典C語言面試題里是常見的不能再常見,你知道輸出結果嗎?

答案是:2,5

但是仍有許多人不能答對,也包括當初的我。這道題簡簡單單,但是考察了不少於如下內容:數組指針、數組首地址概念、數組指針和數組首地址和數組首元素地址之間的關系,指針運算規則,指針類型,int型長度,指針長度,類型轉換…這些概念如果有一個及以上不是那么太清楚的話,很容易答錯。

為方便討論,先開始理解如下關系:

以下是某次在Ubuntu 10.10-desktop-i386 + gcc 4.4.5的運行結果:

size of a: 20 (bytes) (Why? 因為機器是32位的, size of int 為 4 bytes,a有5個int)

size of ptr: 4 (bytes) (ptr是指針,永遠不要忘記,32位字長的機器,指針長度是4)

ptr的地址:bfb5c52c (取決於具體情況,這里只是為了方便理解和討論)

a的地址(&a):bfb5c518

a[0]的地址:bfb5c518

a[1]的地址:bfb5c51c

&a + 1:bfb5c52c (&a[0]:bfb5c518 -- &a[1]:bfb5c51c -- &a[2]:bfb5c520 -- &a[3]:bfb5c524 -- &a[4]:bfb5c528 -- &a[5]:bfb5c52c)

注意&a[5]只是這里的書面表示,其實已經在數組范圍之外了,當然利用數組地址a取a[5]的地址本身是合法的。

下面具體解釋:

  • a是數組地址,根據C語言語義,即數組首元素的地址,首元素是int類型,故首元素地址應為int*類型,故a的類型為int*;
  • &a是數組指針,其值與a相同,但含義卻不同,即類型不同,各位能准確給出&a的類型嗎?是 int (*)[5],難理解嗎?還記得我那篇《C堆上申請二維數組》里面提到的a的類型嗎?這正是C語言指針較晦澀和難懂的地方。

理解了這兩點的話,上述問題則十分簡單:

a + 1:現在把a看作一個指針,指針+1操作,根據C語言語義,實際增加偏移量的是指針指向類型的長度,即32位機器下的int型,即4字節,故a+1就是&a[1],那么*(a+1)就是a[1],即2;

&a + 1:現在把&a看作一個指針(不知道怎么“看作”? int (*b)[5] = &a),那么&a + 1實際增加的偏移量是其指向類型(int (*)[5])的長度,即20字節,故&a + 1其實是a+5,即&a[5](雖然a[5]是越界訪問,但&a[5]沒啥問題,這就是C語言)。

好了,現在已知&a+1的值是a+5,但其類仍然是int (*)[5],現在ptr指向&a+1且通過強制類型轉換變成了int*,那么ptr-1是什么呢?因為ptr已經是int*類型了,ptr-1即向前移動4字節,即a+4,即&a[4],那么*(ptr-1)就是a[4],即5。

是否覺得十分困難?慢慢來吧。這也正是C語言難和強大的冰山一角。

再補充一句:以下關系的值(地址)是一樣的:

a = &a = &a[0],不信可以試試喔!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM