1. 程式人生 > >《C和指標》讀書筆記(7)

《C和指標》讀書筆記(7)

宣告:該讀書筆記摘抄自《C和指標》——Kenneth A.Reek (著)    徐波(譯)。為了克服自己走馬觀花,提高閱讀和學習效率,決定將自己在讀書過程中遇到的一些知識點加以摘抄和總結備忘,在此感謝原書作者和翻譯。

一、陣列名

1、一個誤區:陣列名並不表示整個陣列,它大多時候只是一個指標常量,指向陣列的第一個元素,它的型別取決於陣列元素型別。這一點解釋了為什麼C語言的陣列不能整體拷貝,只能迴圈拷貝賦值。如果你將陣列名賦值給另一個數組名,例如,int  a[10] ;  intb[10];  a = b; ,就已經錯的一塌糊塗:

    1)陣列名是一個常量指標,它不可以被賦值;

    2)就是上面提到的陣列不能整體拷貝,因為你得到的僅僅是一個指向陣列第一個元素的指標常量而已。

注:個人認為這兩點其實本質上是一個意思。即,假如陣列名不是一個指標常量而是表示整個陣列,那麼它可以整體賦值。

2、陣列名大多時候是一個常量指標,但是有且只有兩種情況,陣列名不用指標常量來表示:

    1)作為sizeof的表示式的時候,陣列名錶示整個陣列,sizeof會返回整個陣列佔用的位元組數;

    2)當陣列名作為操作符&的運算元時,會產生一個指向該陣列的指標。

二、陣列的下標引用

1、當用下標來訪問陣列時,下標值可以為任意整數值,可以為負,但是程式設計師自己必須保證訪問的有效性,即不能跳出陣列的前邊界;下標值也可以大於陣列實際長度,這通常用來實現不對稱邊界,但是要避免對超出陣列有效長度的地址進行訪問(只要地址,不要值)。

2、C語言並不會對陣列下標有效性進行安全檢查,C語言標準也並沒有規定編譯器必須對陣列下標進行有效性檢查,對陣列下標的有效性檢查會大大增加程式執行時間和空間的開銷。也就是說保證陣列訪問不越界的工作應該交由程式設計師來做。

3、a[2] 和2[a] 都是合法的,並且具有相同的效果,這緣於C語言實現下標的方式,因為在C語言中對下標的訪問都可以轉換為對等的間接訪問表示式。a[2] 、2[a]都會被編譯器轉換為*(2 + a),這兩種下標書寫形式對編譯器來說並無差異。當然,如果你不是在參加混亂程式碼大賽,在程式碼可讀性上沒有人會喜歡2[a]這種書寫方式。

三、指標與下標

1、加入下標和指標都可以實現某種需要,下標一定不會比指標更有效率,並且有時候指標比下標更有效率。

2、指標比下標更有效率的場合:當在陣列中迴圈變數一次以固定步長移動時,固定數字與固定數字的相乘是在編譯期間完成的,不會佔用執行時間,更有效率一些。

3、一個高效率的陣列的拷貝, 將陣列y的元素賦給陣列x

#define      SIZE             10

int    x[size], y[size] ;

void array_to_array(void)

{

register   int     *p1, *p2;

for (p1=x, p2=y; p1<&x[SIZE]; ) {

*p1++ =  *p2++;

}

}

四、陣列和指標

1、陣列和指標都具有指標值,都可以進行間接訪問和下標引用操作,但是需要注意的是他們並不等價。宣告一個數組時,編譯器首先根據陣列元素個數為該陣列分配記憶體,然後再建立陣列名,它是一個指標常量,指向陣列的第一個元素。宣告一個指標變數時,編譯器只為該變數分配4個位元組的記憶體。

2、當一個數組名作為引數傳遞給一個函式時,該陣列名退化為指標,此時,在函式內部對其sizeof時,得到的結果是4(32位機器),

3、編譯器允許陣列不完整初始化,但只允許順序省略後面的初始值,被省略的初始值全部為0;例如,int   a[10] = {1,2};   a[0] = 1, a[1] = 2, a[2]~a[9]全部為0。另外,對於全域性陣列,預設初始值都是0, 但是如果對於一個區域性陣列,並且所有元素都沒有初始化時,其所有元素初始值都是不確定的。

4、在陣列宣告時,允許編譯器自動計算陣列長度,例如,int   a[ ] = {1, 2, 3, 4, 5};

5、字元陣列的初始化,

  方法1、char    a[ ] = {'h', 'e', 'l', 'l', o'', '\0'};        只適用於較短的字元陣列初始化

  方法2、char    a[ ] = "hello";   注意這種初始化方法與字串常量的區別:當它用於初始化一個字元陣列時,它就是一個字元陣列的初始化列表,其他的任何情況都是字串常量。

五、多維陣列

1、int     a[3][4]; 可以將a看作一個具有三個元素的一維陣列,只不過每個元素又是一個具有4個元素的一位陣列;

同樣,int     a[2][3][4];將a看作一個具有兩個元素的一維陣列,其中每個元素又是一個具有三個元素的一維陣列,而這三個元素的每一個又是一個具有4個元素的一維陣列。可見,多維陣列可以看作是很多一維陣列的巢狀集合。

2、多維陣列在記憶體中的儲存順序

在C語言中,多維陣列元素的儲存順序按照最右邊的下標率先變化,這稱為行主序。

3、指向陣列的指標

例如:int  a[3][4], *(p)[4] = a; 此時p指向該二維陣列的第一行,p+1指向第二行,每一行是一個具有4個元素的陣列,此時p就是一個指向陣列的指標。

特別注意:若p初始化為int  *p = a; 則是錯的,因為p是一個指向整型的指標,而二維陣列名a是一個指向二維陣列第一行的一維陣列的指標常量,也就是說a是一個指向陣列的指標,相當於二級指標,用一個二級指標初始化一個一級指標,顯然不合適。此時應該使用行指標變數,即int   (*p)[4]才正確,要注意,除了第一維宣告為指標p外,其他各維必須明確用下標指定。 若將行指標變數定義為int   (*p)[ ]; 此時p仍然是一個指向一個一維陣列的行指標變數,但是此時一維陣列的長度卻沒有指定,於是當p指標參與運算時,它指向的陣列將會被視為空陣列,即陣列長度為0,計算指標p的步長時將與0相乘,這種情況可能不是你想要的,應該避免。

4、多維陣列作為函式引數

例如有一個二維陣列:  int  a[3][4];    

a作為引數傳遞給函式:      func(a);

      函式func的宣告形式為: void func(int  p[ ][4])   或者void func(int   (*p)[4])

  注:要區別void  func(int   **p) ,這種宣告形式是錯誤的,p是一個真正的二級指標,即p被宣告為一個指向整型的指標的指標。

5、多維陣列的初始化

原則:按照最右邊的下表率來變化。

6、多維陣列的長度自動計算

注意,在多維陣列中只有第一維的長度才能自動計算,其餘各維必須顯示指定。

七、指標陣列

下表優先順序高於間接訪問符優先順序

             以上為第八章“陣列和指標”摘抄總結,未完待續。。。