29 指標和陣列分析(下)

2021-10-02 08:53:46 字數 4109 閱讀 5284

注:部落格中內容主要來自《狄泰軟體學院》,部落格僅當私人筆記使用。

測試環境: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 ...