注:部落格中內容主要來自《狄泰軟體學院》,部落格僅當私人筆記使用。
測試環境:ubuntu 10.10
gcc版本:4.4.5
一、問題:
陣列名可以當做指標常量使用,那麼指標是否也可以當做陣列名來使用呢?
二、陣列的訪問方式
1) 以下標的形式訪問陣列中的元素
int main()
;
a[1] = 3;
a[2] = 5;
return 0;
}
2) 以指標的形式訪問陣列中的元素
int main()
;
*(a + 1) = 3;
*(a + 2) = 5;
return 0;
}
三、下標形式vs指標形式
1) 指標以固定增量在陣列中移動時,效率高於下標形式
2) 指標增量為1且硬體具有硬體增量模型時,效率更高
3) 下標形式與指標形式的轉換
a[n]*(a+n)*(n+a)n[a]
注意:現代編譯器的生成**優化率已大大提高,在固定增
量時,下標形式的效率已經和指標形式相當;但從可
讀性和**維護角度來看,下標形式更優。(基本上都用下標形式)
例項分析
陣列的訪問方式
29-1.c
#include int main()
; int* p = a;
int i = 0;
for(i=0; i<5; i++)
for(i=0; i<5; i++)
printf("\n");
for(i=0; i<5; i++)
for(i=0; i<5; i++)
return 0;
}
操作:
1) gcc 29-1.c -o 29-1.out編譯正確,執行結果:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
a[0] = 10
a[1] = 11
a[2] = 12
a[3] = 13
a[4] = 14
程式設計實驗
陣列和指標不同
29-2.c
#include int main()
ext.c
int a = ; //編譯階段就分配好記憶體位址,在上個函式中呼叫改變了位址
操作:
1) gcc 29-2.c ext.c -o 29-2.out編譯正確,執行出現段錯誤:
&a = 0x804a014
a = 0x1 //a被宣告為int* 指標後,元素都變為指標型別,因此將元素中數值變為位址
段錯誤 //去0x1記憶體位址取資料,但是作業系統不允許去這個記憶體位址中訪問資料
2) 修改**:extern int a
#include int main()
gcc 29-2.c ext.c -o 29-2.out編譯正確,列印結果:
&a = 0x804a020
a = 0x804a020
*a = 1
四、a和&a的區別
1) a為陣列首元素的位址
2) &a為整個陣列的位址
3) a和&a的區別在於指標運算
a+1 ==> (unsigned int)a + sizeof(*a) //陣列元素位址+1,元素下標移動+1
&a+1 ==> (unsigned int)(&a) + sizeof(*&a)
相當於 ==> (unsigned int)(&a) + sizeof(a) //增加整個陣列長度
注:二者區別在指標運算時增加步長不一樣
例項分析(筆試題)
指標運算經典問題
29-3.c
#include int main()
; int* p1 = (int*)(&a + 1); //指向陣列末尾邊界,擦邊球
//p[-1] ==>*(p1 - 1)==>a[4],指標回退1
int* p2 = (int*)((int)a + 1); //數**算,p2=陣列元素首位址+1
int* p3 = (int*)(a + 1); //p3指向a[1]位址
printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
return 0;
}幾種可能性
// a. 陣列下標不能是負數,程式無法執行
// b. p1[-1]將輸出隨機數,p2[0]輸出2, p3[1]輸出3
// c. p1[-1]將輸出亂碼, p2[0]和p3[1]輸出2
操作:
1)gcc 29-3.c -o 29-3.out編譯正確,列印結果:
5, 33554432, 3
分析:
p1[-1] = 5: p1指向了陣列末尾邊界(擦邊球)。p1[-1]相當於指標回退乙個元素,即a[4],因此列印5。
p2[0] = 33554432: (int*)((int)a + 1)==>(int)a是陣列位址轉換成int型數值==>(int)a + 1==>
陣列位址轉換成int型數值後+1==>(int*)((int)a + 1)表示將相加後數值轉換成int*,
此時指標指向的位置相當於陣列名位址+1,即從首位址移動1個位元組。
陣列首位址相當於整體向後移動1個位元組,因為linux是小端系統,轉換成int*型別後,每個元素占用4位元組,
4位元組儲存的資料為0x02000000=33554432
p3[1] = 3: (int*)(a + 1)==>a+1表示陣列移動乙個元素,即a[1]==>p3指向a[1]==>p3[1]等於a[2],即p3[1]=a[2]=3
五、陣列引數
1) 陣列作為函式引數時,編譯器將其編譯成對應的指標
void f(int a); void f(int* a);
void f(int a[5]);void f(int* a);
結論:一般情況下,當定義的函式中有陣列引數時,需要定
義另乙個引數來標示陣列的大小,因為陣列變為指標,引數無效。
例項分析
虛幻的陣列引數
29-4.c
#include void func1(char a[5]) //陣列變為指標
void func2(char b)
int main()
;
func1(array);
printf("array[0] = %c\n", array[0]);
func2(array);
printf("array[0] = %c\n", array[0]);
return 0;
}
操作:
1) gcc 29-4.c -o 29-4.out編譯正確,列印結果:
in func1: sizeof(a) = 4
array[0] = a
in func2: sizeof(b) = 4
array[0] = b
分析:
char a陣列作為引數時,退化為指標!如果想完整獲取陣列資訊,最好給用乙個變數傳遞陣列長度。 小結
1) 陣列名和指標近使用方式相同
- 陣列名的本質不是指標
- 指標的本質不是陣列
2) 陣列名並不是陣列的位址,而是陣列首元素的位址
3) 函式的陣列引數退化為指標(重點)
C語言高階剖析 29 指標和陣列分析(下)
2 a 和 a 的區別 3 陣列引數 4 小結 在開始之前,先思考乙個問題 陣列名可以當作常量指標使用,那麼指標是否也可以當作陣列名來使用呢?訪問陣列中的元素有兩種訪問方式,通過下標訪問和通過指標訪問陣列 下標形式和指標形式基本是等價的,但是效率略有區別 注意 現代編譯器的生成 優化率已大大提高,在...
26 指標和陣列分析(上)
1 陣列的本質 提出問題 假設a是乙個陣列,a 1 假設a是乙個指標,a 1 指標運算的意義是什麼?結果又是什麼?指標 n 的本質就是指標所指向的位址 指向資料的型別佔的記憶體大小 n include int main int p null printf a 0x x n unsigned int ...
第29課 指標和陣列分析(下)
1 訪問陣列元素有兩種方式 以下標的形式訪問陣列中的元素 和以指標的形式訪問陣列中的元素。2 下標形式vs指標形式 注意 現代編譯器的生成 優化率已大大提高,在固定增量時,下標形式的效率已經和指標形式相當,但從可讀性和 維護的角度來看,下標形式更優。陣列的訪問方式 1 include 2 3int ...