數組和指針

     C/C++ 程序中,指針和數組在不少地方可以相互替換着用。任何能由數組下標完成的操作也都可用指針來實現,但程序中使用指針可使代碼更緊湊、更靈活。
     數組要麼在靜態存儲區被創建(如全局數組),要麼在棧上被創建。數組名對應着(而不是指向)一塊內存,其地址與容量在生命期內保持不變,只有數組的內容可以改變。

     指針可以隨時指向任意類型的內存塊,它的特徵是“可變”,所以我們常用指針來操作動態內存。指針遠比數組靈活,但也更危險。

  • 指向數組元素的指針

     定義一個整型數組和一個指向整型的指針變量

     int a[10],*p; 

     整型指針p指向數組中的元素       

     p=&a[0];

     此時,p指向數組中的第0號元素,即a[0],指針變量p中包含了數組元素a[0]的地址,由於數組元素在內存中是連續存放的,因此,我們就可以通過指針變量p及其有關運算間接訪問數組中的任何一個元素。數組名就是數組第0號元素的地址因此下面兩個語句是等價的
     p=&a[0];
     p=a;

     根據地址運算規則,a+1爲a[1]的地址,a+i就爲a[i]的地址。
     下面我們用指針給出數組元素的地址和內容的幾種表示形式:
     (1). p+i和a+i均表示a[i]的地址, 或者講,它們均指向數組第i號元素, 即指向a[i]。
     (2). *(p+i)和*(a+i)都表示p+i和a+i所指對象的內容,即爲a[i]。
     (3). 指向數組元素的指針, 也可以表示成數組的形式,也就是說,它允許指針變量帶下標, 如p[i]與*(p+i)等價。假若: p=a+5;則p[2]就相當於*(p+2), 由於p指向a[5], 所以p[2]就相當於a[7]。而p[-3]就相當於*(p-3), 它表示a[2]。
     (5).&a + 1: 取數組 a 的首地址,該地址的值加上 sizeof(a) 的值,即 &a + 10*sizeof(int),也就是下一個數組的首地址,顯然當前指針已經越過了數組的界限。
          a,&a的值是一樣的,但意思不一樣,a 是數組首元素的首地址,也就是a[0]的首地址,&a是數組的首地址,a+1是數組下一元素的首地址,即a[1]的首地址,&a+1是下一個數組的首地址。

  • 指向二維數組的指針

      定義以下二維數組:
      int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};

      從二維數組的角度來看,a代表二維數組的首地址,當然也可看成是二維數組第0行的首地址。a+1就代表第1行的首地址,a+2就代表第2行的首地址。如果此二維數組的首地址爲1000,由於第0行有4個整型元素,所以a+1爲1008,a+2也就爲1016。
      既然我們把a[0],a[1],a[2]看成是一維數組名,可以認爲它們分別代表它們所對應的數組的首地址,也就是講,a[0]代表第 0 行中第 0 列元素的地址,即&a[0][0], a[1]是第1行中第0列元素的地址,即&a[1][0],根據地址運算規則,a[0]+1即代表第0行第1列元素的地址,即&a[0] [1],一般而言,a[i]+j即代表第i行第j列元素的地址, 即&a[i][j]。
      另外,在二維數組中,我們還可用指針的形式來表示各元素的地址。如前所述,a[0]與*(a+0)等價,a[1]與*(a+1)等價,因此a[i]+j就與*(a+i)+j等價,它表示數組元素a[i][j]的地址。
      因此,二維數組元素a[i][j]可表示成*(a[i]+j)或*(*(a+i)+j),它們都與a[i][j]等價,或者還可寫成(*(a+i))[j]。

      另外, 要補充說明一下, 果你編寫一個程序輸出打印a和*a,你可發現它們的值是相同的,這是爲什麼呢? 我們可這樣來理解:
首先,爲了說明問題,我們把二維數組人爲地看成由三個數組元素a[0],a[1],a[2]組成,將a[0],a[1],a[2]看成是數組名它們又分別是由4個元素組成的一維數組。因此,a表示數組第0行的地址, 而*a即爲a[0], 它是數組名, 當然還是地址,它就是數組第0 行第0 列元素的地址。

  •  指向一個由n個元素所組成的數組指針

      int (*p)[3];
      指針p爲指向一個由3個元素所組成的整型數組指針。在定義中,圓括號是不能少的, 否則它是指針數組。這種數組的指針不同於前面介紹的整型指針,當整型指針指向一個整型數組的元素時,進行指針(地址)加1運算,表示指向數組的下一個元素, 此時地址值增加了2(因爲放大因子爲2),而如上所定義的指向一個由3個元素組成的數組指針,進行地址加1運算時,其地址值增加了6(放大因子爲 2x3=6)。

      int a[3][4], (*p)[4];
      p=a;
      開始時p指向二維數組第0行,當進行p+1運算時,根據地址運算規則,此時放大因子爲4x2=8,所以此時正好指向二維數組的第1行。和二維數組元素地址計算的規則一樣,*p+1指向a[0][1],*(p+i)+j則指向數組元素a[i][j]。

例:

程序運行結果如下:
9 13 17 21
3 11 19

  •  字符指針

      在程序中如出現字符串常量,C編譯程序就給字符串常量按排一存貯區域,這個區域是靜態的,在整個程序運行的過程中始終佔用, 平時所講的字符串常量的長度是指該字符串的字符個數, 但在按排存貯區域時, C 編譯程序還自動給該字符串序列的末尾加上一個空字符'/0',用來標誌字符串的結束,因此一個字符串常量所佔的存貯區域的字節數總比它的字符個數多一個字節。

      char s[]="a string";
      數組s共有9個元素所組成,其中s[8]中的內容是'/0'。實際上,在字符數組定義的過程中,編譯程序直接把字符串複寫到數組中,即對數組s初始化。

      char *cp = "a string";

      或

      char *cp;
      cp="a string";
      用字符指針指向字符串,然後通過字符指針來訪問字符串存貯區域。
      可通過cp來訪問這一存貯區域, 如*cp(指針形式)或cp[0](下標形式)就是字符a,而cp[i]或*(cp+i)就相當於字符串的第i號字符,但企圖通過指針來修改字符串常量的行爲是沒有意義的(可能導致運行錯誤,"a string"位於靜態存儲區,內容爲 a string/0)。

  •  指針數組

      數組中的每個元素都是指針變量,根據數組的定義,指針數組中每個元素都爲指向同一數據類型的指針。

      int *a[10];
      定義了一個指針數組,數組中的每個元素都是指向整型量的指針,該數組由10個元素組成,即a[0],a[1],a[2], ..., a[9],它們均爲指針變量。a爲該指針數組名,和數組一樣,a是常量,不能對它進行增量運算。a爲指針數組元素a[0]的地址,a+i爲a[i]的地址,*a就是a[0],*(a+i)就是a[i]。

      指針數組對處理字符串提供了更大的方便和靈活。

      例子,打印1月至12月的月名:

     

  •  修改內容

      下面例子中,字符數組 a 的容量是 6 個字符,其內容爲 hello/0。a 的內容可以改變,如 a[0]= ‘X’。指針 p 指向常量字符串“world” (位於靜態存儲區,內容爲 world/0),常量字符串的內容是不可以被修改的。從語法上看,編譯器並不覺得語句 p[0]= ‘X’有什麼不妥,但是該語句企圖修改常量字符串的內容而導致運行錯誤。
      char a[] = “hello”;
      a[0] = ‘X’;
      cout << a << endl;
      char *p = “world”; // 注意 p 指向常量字符串 
      p[0] = ‘X’; // 編譯器不能發現該錯誤 
      cout << p << endl;

  •  內容複製與比較

      不能對數組名進行直接複製與比較。下面例子中,若想把數組 a 的內容複製給數組 b,不能用語句 b = a ,否則將產生編譯錯誤。應該用標準庫函數 strcpy 進行復制。同理, 比較 b 和 a 的內容是否相同, 不能用 if(b==a) 來判斷, 應該用標準庫函數 strcmp進行比較。 語句 p = a 並不能把 a 的內容複製指針 p,而是把 a 的地址賦給了 p。要想複製 a的內容,可以先用庫函數 malloc 爲 p 申請一塊容量爲 strlen(a)+1 個字符的內存,再用 strcpy 進行字符串複製。同理,語句 if(p==a) 比較的不是內容而是地址,應該用庫函數 strcmp 來比較。      

     數組…
     char a[] = "hello";
     char b[10];
     strcpy(b, a);   // 不能用  b = a; 
     if(strcmp(b, a) == 0)  // 不能用  if (b == a) 
     指針...
     int len = strlen(a);
     char *p = (char *)malloc(sizeof(char)*(len+1));
     strcpy(p,a);   // 不要用 p = a;
     if(strcmp(p, a) == 0)  // 不要用 if (p == a)

  •  計算內存容量

      用運算符 sizeof 可以計算出數組的容量(字節數) 。下面例子中,sizeof(a)的值是 12(注意別忘了’/ 0’) 。指針 p 指向 a,但是 sizeof(p)的值卻是 4。這是因爲sizeof(p)得到的是一個指針變量的字節數,相當於 sizeof(char*),而不是 p 所指的內存容量。C/C++ 語言沒有辦法知道指針所指的內存容量,除非在申請內存時記住它。

      char a[] = "hello world";
      char *p  = a;
      cout<< sizeof(a) << endl; // 12 字節
      cout<< sizeof(p) << endl; // 4 字節 
       注意當數組作爲函數的參數進行傳遞時,該數組自動退化爲同類型的指針。下面例子中,不論數組 a 的容量是多少,sizeof(a)始終等於 sizeof(char *)。 
      void Func(char a[100])
      {
           cout<< sizeof(a) << endl; // 4 字節而不是 100 字節
      }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章