1. 程式人生 > >C++中陣列和指標之間的關係梳理

C++中陣列和指標之間的關係梳理

C++中陣列和指標是兩個十分常用且關係密切的資料結構,“陣列即指標,指標即陣列”的言論一直層出不窮。從本質上講,陣列和指標是不同的,陣列是具有確定數量的元素,而指標只是一個標量值。但是,在某些情況下陣列和指標又能相互轉換。下面,將從多個角度分析陣列和指標。

1. 陣列和指標互相轉換

陣列能在指定情況下轉換為指標,當陣列在表示式中使用時,編譯器將陣列名轉換為一個指標常量,指向陣列第一個元素的地址。

int main()
{
  int arr[] = {2,4,1,5,6};
  int *p1 = arr;
  int *p2 = &arr[0];
  cout << "p1:" << p1 << ", value:" << *p1 << endl;           // p1:0xffffcbd0, value:2
  cout << "p2:" << p2 << ", value:" << *p2 << endl;           // p2:0xffffcbd0, value:2
  cout << "arr:" << arr << ", value:" << arr[0] << endl;      // arr:0xffffcbd0, value:2

  cout << "p1+2:"  << p1+2 << ", value:" << *(p1+2) << endl;  // p1+2:0xffffcbd8, value:1
  cout << "arr+2:" << &arr[2] << ", value:" << arr[2] << endl;// arr+2:0xffffcbd8, value:1
}

結論:如果令int * p = arr,則p = arr = &arr[0], p+2 = arr+2 = &arr[2];

但是,有兩種情況下,陣列名與指標不能混為一談。

第一種,陣列作為sizeof操作符的引數時:

   sizeof是一個操作符(operator),其作用是返回一個物件或型別所佔的記憶體位元組數。

   sizeof(陣列): 大小是陣列的元素個數*元素型別所佔位元組數,與陣列的型別資訊相關,與地址資訊無關;

   sizeof(指標): 大小固定,32位機器全是4個位元組,64位機都是8個位元組。

int main() {
    int arr[] = {2, 4, 1, 5, 6};
    int *p = arr;
    cout << sizeof(arr) << endl;      // 20 : 5*sizeof(int)
    cout << sizeof(*arr) << endl;     // 4  : 第一個元素值的大小
    cout << sizeof(&arr) << endl;     // 8  : 第一個元素地址的大小
    cout << sizeof(arr[0]) << endl;   // 4  : 第一個元素值的大小
    cout << sizeof(&arr[0]) << endl;  // 8  : 第一個元素地址的大小

    cout << sizeof(p) << endl;        // 8  : 指標的大小
    cout << sizeof(*p) << endl;       // 4  : 指標所指元素值的大小
    cout << sizeof(&p) << endl;       // 8  : 指標的地址的大小
    cout << sizeof(p[0]) << endl;     // 4  : 陣列第一個元素值的大小
    cout << sizeof(&p[0]) << endl;    // 8  : 陣列第一個元素值的地址的大小
}

第二種,陣列作為單目操作符&的運算元時:

  &陣列: 表示取陣列的地址,即陣列的指標;

  &指標: 表示取指標的地址,即指標的指標。

2. 二維陣列與指標

指標的概念其實很簡單,指標難就難在與其他結構之間的牽扯,比如指標與二維陣列。

第一點,定義指向二維陣列的指標:

   定義二維陣列 int aa[2][5];

   由於陣列名為陣列的第一個元素的地址,而二維陣列aa的第一個元素為長度為5的一維陣列;

   因此,如果定義一個指標指向二維陣列的話,該指標的長度也必須為5;

   即,int (*p)[5] = aa 或者 int (*p)[5] = &aa[0], 表示長度為5的指標陣列。

第二點,指標訪問二維陣列第一個元素中的值:

   首先,*p表示二維陣列中第一個元素對應的值,即長度為5的一維陣列,假設為a[5];

   其次,*p可看成一維陣列a[5]的名,即a[5]的第一個元素的地址;

   最後,如果想取aa[0][2],則可用*(*p+2) 表示。

int main() {
    int aa[2][5] = {{2, 4, 1, 5, 6},{1,2,3,4,5}};
    int (*p)[5] = aa;
    cout << *(*p+2)<< endl;       // 1
}

第三點,指標訪問二維陣列任意一個元素的值:

  (1)使用列指標:定義一個列指標p,讓它指向二維陣列的第1個元素。

int main() {
    int aa[2][5] = {{2, 4, 1, 5, 6},{1,2,3,4,5}};
    int *p = &aa[0][0];
    cout << *(p+1*5+2)<< endl;
}

  首先,aa[0] 相當於 int a[5], 則p=&aa[0][0]相當於p=a[0];

  其次,C語言中陣列是按行優先順序儲存,而aa[i][j]前面共有i*5+j個元素,所以該二維陣列的任意i行j列元素可表示為*(p+i*5+j)

 (2)使用行指標:定義一個行指標p,讓它指向二維陣列的第1行。

int main() {
    int aa[2][5] = {{2, 4, 1, 5, 6},{1,2,3,4,5}};
    int (*p)[5] = aa; //也可以為p=&aa[0];
    cout << *(*(p+1)+2)<< endl;
}

  其中* ( *(p+i)+j)表示任意一個i行j列的元素值, *(p+i)可理解為取二維陣列中第i個元素的值,即a=int[5],而*(a+j)表示一維陣列a的第j個元素的值。

3. 陣列作為函式引數

當陣列作為函式引數傳入時,作為實參的陣列將在函式呼叫前被轉換為一個指向該陣列首元素的指標。陣列永遠不會傳遞給函式處理,函式內部操作傳來的陣列,其實都是在操作一個指標。

void fun(int arr[])
{
    cout << sizeof(arr) << endl; // 8, 說明arr是指標
    cout << arr << endl;
}

int main()
{
    int arr[3] = {3,1,2};
    cout << sizeof(arr) << endl; // 12, 說明arr是陣列
    cout << arr << endl;
    fun(arr);
    return 0;
}

4. 陣列與指標巢狀使用

當陣列和指標共同定義一個物件時,有以下兩種情況:

第一種,int *arr[5]

 即,指標的指標。

int main()
{
    int *arr[3];
    int a = 2;
    int b = 4;
    int c = 3;
    arr[0] = &a;
    arr[1] = &b;
    arr[2] = &c;
    cout << *arr[0] << endl; //2
    cout << *arr[1] << endl; //4
    cout << *arr[2] << endl; //3
    return 0;
}

第二種,int (*arr)[5]

即,陣列的指標。

int main()
{
    int arr[2][5] = {{2,3,1,2,3},{3,2,5,6,7}};
    int (*p)[5] = arr;
    cout << "arr[1][2]:" << *(*(p+1)+2) << endl; // arr[1][2]:5
    return 0;
}

5. 字元型物件與指標

第一種,字元陣列與指標

int main()
{
    char s[] = "hello";
    char *p = s;
    cout << p << endl;    // p = hello
    cout << *p << endl;   // *p = h
    cout << s << endl;    // s = hello
    cout << &s[0] << endl;// &s[0] = hello
    cout << s[0] << endl; // s[0] = h
}

C++中cout為了省去迴圈輸出字元陣列的麻煩,cout<<p<<endl;被翻譯為輸出p指向的字串值,但是p指向的依然是陣列第一個元素的地址。

第二種,字串與指標

int main()
{
    string s = "hello";
    string *p = &s;
    cout << p << endl;    // p = 0xffffcbd8
    cout << *p << endl;   // *p = hello
    cout << s << endl;    // s = hello
    cout << &s[0] << endl;// &s[0] = hello
    cout << s[0] << endl; // s[0] = h
}

由於string不是陣列,因此指標p指向的是字串s,定義指標的時候,也需要加取地址符號&。