1 數組的通路方式
1.1 兩種數組通路方式
1.2 下标形式和指針形式對比
2 a 和 &a 的差別
3 數組參數
4 小結
在開始之前,先思考一個問題:數組名可以當作常量指針使用,那麼指針是否也可以當作數組名來使用呢?
通路數組中的元素有兩種通路方式,通過下标通路和通過指針通路數組
下标形式和指針形式基本是等價的,但是效率略有差別
指針以固定增量在數組中移動時,效率略高于下标形式
指針增量為 1 且硬體具有硬體增量模型時,效率更高
注意:現代編譯器的生成代碼優化率已大大提高,在固定增量時,下标形式的效率已經和指針形式相當;但從可讀性和代碼維護的角度來看,下标形式更優
下标形式與指針形式之間可以互相轉換,轉換如下:
這裡 a[n] 可是可以轉換為 n[a] 的哦
下面通過一個例子說明數組的指針通路和下标通路
編譯運作結果如下:
從結果我們可以看到,通過指針和數組都可以實作通路數組中的元素,可以把數組名當作常量指針使用,也可以把指針當作數組名來使用,a[i] 和 i[a] 是等效的
指針和數組的概念不是完全相等的,二着是有差別的,我們通過一個例子來說明指針數組和指針的不同
a 是數組首元素的位址,&a 是整個數組的位址,在數值上二者是相同的,*a 表示取元素第一個元素。
如果把 29-2.c 的第 6 行 extern int a[]; 改為 extern int *a; 代碼更改如下:
再次編譯運作,結果完全不一樣了
為什麼會出現這個運作結果呢?編譯器編譯 ext.c 後,數組 a 的位址就是 0x562f0c0ba010,等價于 a 的位址就是 0x562f0c0ba010。編譯 29-2.c 時,碰見 extern int* a; 就尋找其他位置定義的 a 的值,a 的位址為 0x562f0c0ba010,是以 &a 就是取 a 的位址,就是 0x562f0c0ba010,a 的值是該位址下對應的資料,為 0x200000001。*a 表示取位址值為 0x200000001 的資料,不合法,是不允許通路的,傳回段錯誤。
下面來說明一下為什麼位址 0x562f0c0ba010 對應的資料為 0x200000001,我們列印的是 %p,%p 代表的是:按十六進制輸出資料,長度為 8 個位元組,linux 是小端序系統,低位址存放低位,數組存儲方式如下,取 8 位元組資料為 0x200000001
a 為數組首元素的位址
&a 為整個數組的位址
a 和 &a 的差別在于指針運算
a + 1 表示指向下一個元素,a + 1是越過一個元素,&a + 1 表示指向這個數組最後一個元素後面的位置,&a + 1 是越過一個數組,可以從下圖清晰的看到二者的指向
下面看一個例子,更好的區分 a 和 &a
p1 指向整個數組最後一個元素後面的位置, p1[-1] ==> *(p1 -1),是以 p1[-1] 指向數組最後一個元素。
(int*)((long long)a + 1); 先把指針轉換為 long long 再相加,這已經不是指針運算了,就是基本資料類型之間的運算,再次解引用,等于從數組首元素的第二個位元組開始,取 4 個位元組的資料,結果如下,linux 系統是小端序,取到的資料為 0x02000000,等于十進制的 33554432。
p3 指向數組第二個元素
數組作為函數參數時,編譯器将其編譯成對應的指針
結論:将數組名作為參數傳遞給函數時,數組名退化為指針,一般情況下,當定義的函數中有數組參數時,需要定義另一個參數來表示數組的大小
下面通過例子來驗證我們的理論
在 func1() 函數中,,列印 sizeof(a),數組退化成指針,是以不管數組長度如何,列印的都是指針的長度,由于退化成指針,是以允許 修改指針 a = null; 我們是不能修改數組名的指向的。func1() 函數是一樣的,隻是沒有指定數組長度。
1、數組名和指針僅使用方式相同,數組名本質不是指針,指針也不是數組名
2、數組名并不是數組的位址,而是數組首元素的位址
3、函數的數組參數退化為指針