簡體   English   中英

指向數組的指針和指向數組第一個元素的指針之間的區別

[英]difference between pointer to an array and pointer to the first element of an array

int (*arr)[5]表示arr是一個指向 5 個整數的數組的指針。 現在這個指針到底是什么?

如果我聲明int arr[5]是一樣的,其中arr是指向第一個元素的指針嗎?

來自兩個示例的arr是否相同? 如果不是,那么指向數組的指針究竟是什么?

理論

首先是一些理論(你可以跳到“答案”部分,但我建議你也閱讀這個):

int arr[5]

這是一個數組,“arr”不是指向數組第一個元素的指針。 在特定情況下(即,將它們作為左值傳遞給函數),它們會衰減為指針:您將失去對它們調用sizeof的能力。

通常情況下,數組是數組,指針是指針,它們是兩個完全不同的東西。

在處理衰減的指針和指向您編寫的數組的指針時,它們的行為完全相同,但有一個警告:T 類型的數組可以衰減為 T 類型的指針,但只能衰減一次(或一層深度)。 新創建的衰變類型不能進一步衰變成其他任何東西。

這意味着像這樣的二維數組

int array1[2][2] = {{0, 1}, {2, 3}};

不能傳遞給

void function1(int **a);

因為這意味着兩個級別的衰減,這是不允許的(您會丟失數組元素的布局方式)。 以下內容將起作用:

void function1(int a[][2]);
void function1(int a[2][2]);

在將一維數組作為左值傳遞給函數的情況下,您可以將其分解為一個簡單的指針,在這種情況下,您可以像使用任何其他指針一樣使用它


答案

回答您的問題:

int (*arr)[5]

這是一個指向數組的指針,您可以將“作為 5 個整數的數組”視為其類型,即您不能使用它來指向 3 個整數的數組。

int arr[5]

這是一個數組,除非將其作為左值傳遞,否則將始終表現為數組

int* ptrToArr = arr;

在這種情況下,數組會衰減(除了我上面引用的所有例外),您會得到一個指針,您可以根據需要使用它。

並且:不,它們不相等,否則將允許這樣的事情

int (*arr)[5]
int* ptrToArr = arr; // NOT ALLOWED

Error cannot convert ‘int (*)[5]’ to ‘int*’ in initialization

它們都是指針,但不同之處在於它們的類型。

在運行時,無論指向什么,指針都是“只是一個指針”,區別在於語義; 與指向元素的指針相比,指向數組的指針傳達了不同的含義(對編譯器而言)

處理指向數組的指針時,您指向的是指定大小的數組 - 編譯器將確保您只能指向該大小的數組。

即此代碼將編譯

int theArray[5];
int (*ptrToArray)[5];
ptrToArray = &theArray;    // OK

但這會中斷:

int anotherArray[10];
int (*ptrToArray)[5];
ptrToArray = &anotherArray;    // ERROR!

在處理指向元素的指針時,您可以指向內存中具有匹​​配類型的任何對象。 (它甚至不一定需要在數組中;編譯器不會做出任何假設或以任何方式限制您)

IE

int theArray[5];
int* ptrToElement = &theArray[0];  // OK - Pointer-to element 0

和..

int anotherArray[10];
int* ptrToElement = &anotherArray[0];   // Also OK!

總之,數據類型int*並不意味着對數組有任何了解,但是數據類型int (*)[5]意味着一個數組,它必須正好包含 5 個元素。

指向數組的指針是指向特定類型數組的指針。 類型包括元素的類型以及大小。 您不能為其分配不同類型的數組:

int (*arr)[5]; 
int a[5];
arr = &a; // OK
int b[42];
arr = &b; // ERROR: b is not of type int[5].

指向數組第一個元素的指針可以指向具有正確元素類型的任何數組的開頭(實際上,它可以指向數組中的任何元素):

int* arr; 
int a[5];
arr = &a[0]; // OK
int b[42];
arr = &b[0]; // OK
arr = &b[9]; // OK

請注意,在 C 和 C++ 中,數組在某些上下文中衰減為指向其元素類型的指針。 這就是為什么可以這樣做:

int* arr; 
int a[5];
arr = a; // OK, a decays to int*, points to &a[0]

這里, arr ( int* ) 的類型與a ( int[5] ) 的類型不同,但a衰減為指向其第一個元素的int* ,從而使賦值合法。

指向數組的指針和指向數組第一個元素的指針是不同的。 int (*arr)[5]arr是指向5 int內存塊的指針。 取消引用arr將給出整行。 int arr[5]情況下, arr衰減到指向第一個元素的指針。 取消引用arr將給出第一個元素。
在這兩種情況下,起始地址相同,但兩個指針的類型不同。

如果我聲明int arr[5]是一樣的,其中arr是指向第一個元素的指針嗎? 兩個例子中的arr是一樣的嗎? 如果不是,那么指向數組的指針究竟是什么?

不。要理解這一點,請參見函數1的圖表:

void f(void) {
    int matrix[4][2] = { {0,1}, {2,3}, {4,5}, {6,7} };
    char s[] = "abc";
    int i = 123;
    int *p1 = &matrix[0][0];
    int (*p2)[2] = &matrix[0];
    int (*p3)[4][2] = &matrix;
    /* code goes here */
}

在此處輸入圖片說明

所有三個指針當然都允許您在matrix[0][0]定位0 ,並且如果您將這些指針轉換為“字節地址”並在printf()使用%p指令將它們打印出來,那么這三個指針都很有可能產生相同的輸出(在典型的現代計算機上)。 但是int *指針p1僅指向單個int ,如黑色圓圈所示。 紅色指針p2 ,其類型為int (*)[2] ,指向兩個int ,而藍色指針——指向整個矩陣的指針——確實指向整個矩陣。 這些差異會影響指針算術和一元* (間接)運算符的結果。 由於p1指向單個int ,因此p1 + 1向前移動單個int 黑色圓圈1只和一個int一樣大,而*(p1 + 1)就是下一個 int,其值為 1。同樣, sizeof *p1只是sizeof(int) (可能是 4)。

但是,由於p2指向整個“int 數組 2”,因此p2 + 1將向前移動一個這樣的數組 結果將是一個指向圍繞{2,3}對的紅色圓圈的指針。 由於間接運算符的結果是一個對象, *(p2 + 1)是整個數組對象,這可能屬於規則。 如果它確實屬於 The Rule,則該對象將變為指向其第一個元素的指針,即當前持有2int 如果它不屬於規則——例如,在sizeof *(p2 + 1) ,它將對象放在對象上下文中——它將保留整個數組對象。 這意味着sizeof *(p2 + 1) (當然還有sizeof *p2 )是sizeof(int[2]) (可能是 8)。


1以上內容摘自More Words about Arrays and Pointers

整個數組的地址和第一個元素的地址被定義為相同的,因為 C++(和 C)中的數組除了組成對象的填充之外沒有內在的填充。

但是,這些指針的類型是不同的。 在您執行某種類型轉換之前,將int *int (*)[5]比較就像是蘋果與橘子。

如果您聲明arr[5] ,則arr不是指向第一個元素的指針。 它是數組對象。 您可以觀察到這一點,因為sizeof( arr )將等於5 * sizeof (int) 數組對象隱式轉換為指向其第一個元素的指針。

指向數組的指針不會隱式轉換為任何內容,這可能是您混淆的另一個原因。

如果您編寫int arr[5] ,則您正在堆棧上創建一個包含五個int的數組 這占用的大小等於五個整數的大小。

如果你寫int (*arr)[5] ,你正在創建一個指向堆棧上五個int數組的指針 這占用的大小等於指針的大小。

如果從上面還不清楚,指針與數組分開存儲,可以指向任何東西,但不能將數組名賦值為指向其他東西。

有關更多詳細信息,請在此處查看我的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM