1. 程式人生 > >指標與陣列知識點總結

指標與陣列知識點總結

本文基於flying_music部落格,加入自己的一些理解,請周知

1.印子——指標為什麼需要返回型別

  我們用C語言寫了這樣的語句

  1. int a;  
  2. a = 3;  
編譯器為了完成這兩句程式碼,首先在編譯過程中要建立一個符號表,樣子大概如下圖:

   然後在執行過程中,編譯器發現a=3這句程式碼時,會在符號表裡找a對應的地址,然後把3放入對應的地址,即這裡的0x1000。(A找B蓋章,直接蓋到了)

   如果是*p=3會怎麼做呢?首先,符號表變成了這個樣子

    

   在執行過程中,編譯器遇到*p=3時,首先要從符號表中找到p的地址0x1004,然後取出0x1004中的內容,這裡假設為0x2000,最後把3放到0x2000記憶體地址中,即*(0x1004)=3。

   相比於利用普通變數,利用指標存取資料的過程中多了一部取地址的的過程。這也就是指標變數與普通變數最大的不同。

   再來看一下指標加偏移量的引用方式,還以上面的指標p為例,讓我們來看一下*(p+2)=3的實現過程。

   首先,編譯器從符號表中找到p然後,取出裡面的內容0x2000,再根據其型別(int*),做一個運算,      

   0x2000+2×sizeof(int)=0x2008。所以編譯器會把3放入0x2008這個記憶體地址。

   整個過程可表示為*(*(0x1004)+2)=3。(A找B蓋章,B講你去C部門,找那誰蓋章(偏移))

   從這裡也可以看出為什麼指標必須有型別,因為在引用過程中要用到指標所指型別的長度。

來看一下,陣列元素的引用是如何實現的,假設我們定義了一個數組,並對其元素進行了引用

  1. int b[10];  
  2. b[4] = 3;  
對應的符號表變成了這個樣子


    那b[4]=3如何完成呢?首先,找到符號b,然後發現其型別為int[](假想表達方式,C語言中不支援這樣寫),所以計算式變成了0x1008+4×sizeof(int)=0x1018,然後把3放入0x1018就可以了。用一個式子表達就是*(0x1008+4)=3。(A找B蓋章,B講你去我們部門,找那誰蓋章(偏移))

    從上面的定址式子可以看出,普通變數、指標、陣列三者對於編譯器的區別。具體到陣列,它即具有普通變數的直接性,即不用取兩次地址裡的內容而是取一次,同時又具有和指標相同的偏移量引用方式,即下標的實現實際是由指標加偏移量實現的。

    為了表明上述事實(或者是為了提高C語言入門門檻),C語言對指標與陣列的引用方式做了可以“交叉”使用的語法規定。就上面的例子來說,如果p指標指向陣列b時,b[i]、*(b+i)、p[i]、*(p+i)都是對陣列第i個元素的正確引用方式,這也給很多C語言學習者製造了“指標和陣列一樣”的錯覺。

    

2.在函式形參中的表現

在向函式傳遞引數時,如果實參是一個一維陣列,那用於接受的形參為對應的指標。也就是傳遞過去是陣列的首地址而不是整個陣列。這麼做的原因主要是效率,這是無可爭議的,但為了使用上的簡便與通用,C語言接受兩種形參的寫法,即下面兩種寫法是相同的

  1. int foo(int *a, int n);  
  2. int foo(int a[], int n);  
甚至是
  1. int foo(int a[20], int n);  
    在底層都是完全一樣的形式。這簡化了C語言的使用,但同時也增加了對其進一步理解的難度。

    至於哪種寫法好的爭論,只要理解了,用哪種寫法倒是不必強求。

    指標形式表明了傳參過程的實質,而陣列形式表明了這個指標對應一個數組實參,甚至後面的數字可以提供陣列長度的參考。

3.如何區分二者

陣列和指標的關係如此微妙,那如何區分呢?

    首先說為什麼要區分,主要由兩個方面的原因

    一是C語言的主要戰場還是偏向於底層的,使用者對這塊兒一定要搞清楚,比如編譯器,硬體系統等相關知識。       二是在特定的情況下,二者的表現的確不同,最常見的就是sizeof關鍵字的作用結果,另外還有取址操作符&的結果等情況。

   至於如何區分,個人認為凡是要區分兩個相關的概念,一定要將二者的作用區分清楚,即為什麼要有這個東西。

   陣列和指標也是一樣,指標是一類特殊的變數,主要用途是函式間的傳址,用這種方式來改變實參內容。

   而陣列是用來實現線性表的結構,用於把同類物件集中在一起放置。

   所以,如果下次有人問你陣列和指標的區別,你首先第一句要說的就是二者根本沒有聯絡,這麼說並這麼理解,對於弄清這兩個傢伙的關係是很有裨益的。