C語言數組和指針的理解_在取地址運算上的操作_指針加減操作_a 和&a 的區別


 

1.一個實例+理論分析

 

在了解數組和指針的訪問方式前提下,下面再看這個例子:

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

打印出來的值為多少呢? 這里主要是考查關於指針加減操作的理解


  對指針進行加1操作,得到的是下一個元素的地址,而不是原有地址值直接加1。所 以,一個類型為T的指針的移動,以sizeof(T) 為移動單位。

  因此,對上題來說,a是一個一維數組,數組中有5個元素,所以a的類型是數組指針;ptr是一個int 型的指針,ptr的類型是整型指針。 

&a + 1:取數組a 的首地址,該地址的值加上sizeof(a) 的值,即&a + 5*sizeof(int),也就是下一個數組的首地址。  

  顯然當前指針已經越過了數組的界限。


   (int *)(&a+1): 則是把上一步計算出來的地址,強制轉換為int * 類型,賦值給ptr。

  

  *(a+1): a,&a的值是一樣的,但意思不一樣a是數組首元素的首地址,也就是a[0]的首地址,&a是數組的首地址,a+1是數組下一元素的首地址,即a[1]的首地址,&a+1是下一個數組的首地址。

  所以輸出2*(ptr-1):因為ptr是指向a[5],並且ptr是int * 類型,所以*(ptr-1)是指向a[4],輸出5。

 

2.Visual C++6.0上的真實調試結果


  這些分析我相信大家都能理解,但是在授課時,學生向我(陳正沖老師)提出了如下問題:


在Visual C++6.0的Watch窗口中&a+1的值怎么會是(x0012ff6d(0x0012ff6c+1)呢?

 

上圖是在Visual C++6.0調試本函數時的截圖。

a在這里代表是的數組首元素的地址即a[0]的首地址,其值為0x0012ff6c。
&a代表的是數組的首地址,其值為0x0012ff6c。
a+1的值是0x0012ff6c+1*sizeof(int),等於0x0012ff70。 


問題就是&a+1的值怎么會是(x0012ff6d(0x0012ff6c+1)呢?


  按照我們上面的分析應該為0x0012ff6c+5*sizeof(int)。其實很好理解。當你把&a+1放到Watch窗口中觀察其值時,表達式&a+1已經脫離其上下文環境,編譯器就很簡單的把它解析為&a的值然后加上1byte。而a+1的解析就正確,我(陳正沖老師)認為這是Visual C++6.0的一個bug。既然如此,我們怎么證明證明&a+1的值確實為0x0012ff6c+5*sizeof(int)呢?

  很好辦,用printf函數打印出來。這就是我在本書前言里所說的,有的時候我們確實需要printf函數才能解決問題。你可以試試用printf("%x",&a+1);打印其值,看是否為0x0012ff6c+5*sizeof(int)。注意如果你用的是printf("%d",&a+1);打印,那你必須在十進制和十六進制之間換算一下,不要冤枉了編譯器。
  

  另外我(陳正沖老師)要強調一點:不到非不得已,盡量別使用printf函數,它會使你養成只看結果不問為什么的習慣。比如這個列子,*(a+1)和*(ptr-1)的值完全可以通過Watch窗口來查看。平時初學者很喜歡用“printf("%d,%d",*(a+1),*(ptr-1));”這類的表達式來直接打印出值,如果發現值是正確的就歡天喜地。這個時候往往認為自己的代碼沒有問題,根本就不去查看其變量的值,更別說是內存和寄存器的值了。(嗯,這個壞習慣,我是有的。)

 

 

3. 最好不要利用printf函數進行調試

 

陳正沖老師的經驗與教誨:

 

  更有甚者,printf函數打印出來的值不正確,就措手無策,舉手問“老師,我這里為什么不對啊?”。長此以往就養成了很不好的習慣,只看結果,不重調試。這就是為什么同樣的幾年經驗,有的人水平很高,而有的人水平卻很低。其根本原因就在於此,往往被一些表面現象所迷惑。printf函數打印出來的值是對的就能說明你的代碼一定沒問題嗎?我看未必。曾經一個學生,我讓其實現直接插入排序算法。很快他把函數寫完了,把值用printf函數打印出來給我看。我看其代碼卻發現他使用的算法本質上其實是冒泡排序,只是寫得像直接插入排序罷了。等等這種情況數都數不過來,往往犯了錯誤還以為自己是對的。所以我平時上課之前往往會強調,不到非不得已,不允許使用printf函數,而要自己去查看變量和內存的值。學生的這種不好的習慣也與目前市面上的教材、參考書有關,這些書甚至花大篇幅來介紹scanf和printf這類的函數,卻幾乎不講解調試技術。甚至有的書還在講TruboC 2.0之類的調試器!如此教材教出來的學生質量可想而知。

 

參考:陳正沖老師的《c語言深度剖析》。


免責聲明!

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



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