C++ 数组退化为指针是怎么回事?

文章导读
Previous Quiz Next Array decay 指的是数组类型和维度的丢失。在某些情况下,例如将数组通过指针或值传递给函数时,数组会被视为指向第一个元素的指针,而不是整个数组。因此,原始数组的大小和类型信息会丢失。
📋 目录
  1. C++ 中数组退化何时发生?
  2. C++ 中数组退化为指针引起的问题
  3. 数组退化问题的解决方案
  4. 结论
A A

C++ 中的 Array Decay



Previous
Quiz
Next

Array decay 指的是数组类型和维度的丢失。在某些情况下,例如将数组通过指针或值传递给函数时,数组会被视为指向第一个元素的指针,而不是整个数组。因此,原始数组的大小和类型信息会丢失。

由于这种 decay,sizeof 操作符不再返回数组的完整大小,而是返回指针的大小。这会导致依赖于知道元素数量的函数出现不正确行为。

Array decay to first element

C++ 中数组退化何时发生?

数组退化会在以下场景中发生 −

  • 将数组传递给函数
  • 将数组赋值给指针
  • 在指针运算中使用数组

让我们详细了解这三种场景以及它们如何触发数组退化。

将数组传递给函数

在将array传递给function时,数组会自动退化pointer。退化后,函数无法接收数组的大小,只接收一个指向数组first element的指针。

以下example演示了将数组arr传递给printSize()函数时如何退化为指向第一个数组元素的指针。

#include <iostream>
using namespace std;

void printSize(int *arr){
   cout << "\nSize of array inside function: " 
      << sizeof(arr) << " bytes" 
      << endl;
}

int main(){
   int arr[5] = {1, 2, 3, 4, 5};

   cout << "Original Size of the array " << sizeof(arr) 
      << " bytes" << endl;
   cout << "Number of elements: " 
      << sizeof(arr) / sizeof(arr[0]) << endl;

   // Array decay occurs here
   printSize(arr);

   return 0;
}

上述代码的output如下。您可以观察到将数组传递给函数beforeafter的大小 −

Original Size of the array 20 bytes
Number of elements: 5

Size of array inside function: 8 bytes

将数组赋值给指针

在 C++ 中,您可以将数组赋值给指针。当您将数组赋值给指针时,数组会自动退化为指针。

以下是一个example,演示了在将数组arr赋值给指针ptr时发生的数组退化。

#include <iostream>
using namespace std;

int main() {
   int arr[5] = {10, 20, 30, 40, 50};

    
   int* ptr = arr;  // Array decay occurs here

   cout << "Accessing elements using pointer:" << endl;
   for (int i = 0; i < 5; i++) {
      cout << "*(ptr + " << i << ") = " << *(ptr + i) << endl;
   }

   cout << "Address of arr[0]: " << &arr[0] << endl;
   cout << "Value of ptr: " << ptr << endl;

   return 0;
}

上述代码的output如下 −

Accessing elements using pointer:
*(ptr + 0) = 10
*(ptr + 1) = 20
*(ptr + 2) = 30
*(ptr + 3) = 40
*(ptr + 4) = 50
Address of arr[0]: 0x7ffd7a353890
Value of ptr: 0x7ffd7a353890

在指针运算中使用数组

您可以使用pointer arithmetic通过加法和减法等arithmetic operations来访问数组元素。这会导致数组退化,因为数组loses its size information

在这个example中,我们使用了*(arr + 2)来访问数组的第三个元素 −

#include <iostream>
using namespace std;

int main() {
   int arr[5] = {10, 20, 30, 40, 50};

   cout << "Array elements: ";
   for (int i = 0; i < 5; i++){
      cout << arr[i] << " ";
   }
   cout << endl;

   // Size of array before decay
   cout << "Array size before decay: " << sizeof(arr) 
      << " bytes" << endl;

   // Array decay happens here
   cout << "\n3rd element using pointer arithmetic: *(arr + 2) = " 
      << *(arr + 2) << endl;

   // Size after decay
   cout << "Size of (arr + 2): " << sizeof(arr + 2) 
      << " bytes " << endl;

   return 0;
}

上述代码的output如下。您可以观察到指针beforeafter的元素大小 −

Array elements: 10 20 30 40 50 
Array size before decay: 20 bytes

3rd element using pointer arithmetic: *(arr + 2) = 30
Size of (arr + 2): 8 bytes

C++ 中数组退化为指针引起的问题

在 C++ 编程中,array decay 可能会导致几个问题。以下列出了一些问题

无法在函数内部确定数组大小

当数组作为function parameter传递时,arraydecayedpointer。该指针仅stores the address,且no information关于数组中的number of elements,因此 sizeof() 函数返回的是size of a pointer,无法确定数组大小。

在下面的示例中,我们将数组arr作为参数传递给函数func。我们在 main 函数和func()函数中计算了数组大小。func()函数返回数组大小。

#include <iostream>
using namespace std;

void func(int arr[]) {

   // 这里发生 array decay,因为 arr[] 是作为参数传递的
   int size = sizeof(arr) / sizeof(arr[0]); 

   cout << "Inside function:" << endl;
   cout << "sizeof(arr) = " << sizeof(arr) << " bytes (pointer)" << endl;
   cout << "sizeof(arr[0]) = " << sizeof(arr[0]) << " bytes" << endl;
   cout << "Calculated size = " << size << " (WRONG!)" << endl;
   cout << endl;
}

int main() {
   int arr[5] = {1, 2, 3, 4, 5};

   cout << "In main function:" << endl;
   cout << "sizeof(arr) = " << sizeof(arr) << " bytes" << endl;
   cout << "sizeof(arr[0]) = " << sizeof(arr[0]) << " bytes" << endl;
   int size = sizeof(arr) / sizeof(arr[0]);
   cout << "Calculated size = " << size << " (CORRECT!)" << endl;
   cout << endl;

   func(arr);

   return 0;
}

上述代码的output如下 −

In main function:
sizeof(arr) = 20 bytes
sizeof(arr[0]) = 4 bytes
Calculated size = 5 (CORRECT!)

Inside function:
sizeof(arr) = 8 bytes (pointer)
sizeof(arr[0]) = 4 bytes
Calculated size = 2 (WRONG!)

Warnings/Errors:
main.cpp: In function 'void func(int*)':
main.cpp:7:23: warning: 'sizeof' on array function parameter 
'arr' will return size of 'int*' [-Wsizeof-array-argument]
    7 |     int size = sizeof(arr) / sizeof(arr[0]);
      |                      ~^~~~

无法使用 = 操作符复制数组

由于数组退化为指针,因此无法使用"="操作符将一个数组复制到另一个数组。'=' 操作符copies the address,但我们不能重新分配数组内存,因为它已经被固定。因此,会显示error

以下是一个使用 "=" 操作符将一个数组复制到另一个数组的example

#include <iostream>
using namespace std;

int main() {
   int a[3] = {1, 2, 3};
   int b[3];

   // 这里发生 Array decay
   // 'a' 退化为指向第一个元素的 pointer
   b = a; 
           
   cout << "b elements: ";
   for (int i = 0; i < 3; i++)
      cout << b[i] << " ";

   return 0;
}

上述代码的output如下 −

Warnings/Errors:
main.cpp: In function 'int main()':
main.cpp:11:7: error: invalid array assignment
   11 |     b = a;
      |     ~~^~~

无法直接比较数组

由于array names在使用时会decay to pointers,因此无法使用comparison operators(==, !=)直接比较数组。即使数组具有相同的元素,它也会return false,因为memory address不同,它会比较指针的内存地址。

以下是比较两个数组arr1arr2example

#include <iostream>
using namespace std;

int main() {
   int arr1[5] = {1, 2, 3, 4, 5};
   int arr2[5] = {1, 2, 3, 4, 5};

   cout << "Array 1: ";
   for (int i = 0; i < 5; i++)
      cout << arr1[i] << " ";
   cout << endl;

   cout << "Array 2: ";
   for (int i = 0; i < 5; i++)
      cout << arr2[i] << " ";
   cout << endl;

   cout << "Are arr1 and arr2 same?:  ";
   if (arr1 == arr2)
      cout << "TRUE" << endl;
   else
      cout << "\nFALSE" << endl;

   cout << "\nAddress of arr1: " << arr1 << endl;
   cout << "Address of arr2: " << arr2 << endl;

   return 0;
}

上述代码的output如下。可以看出两个数组具有相同的元素,但由于地址不同,它返回 false −

Array 1: 1 2 3 4 5 
Array 2: 1 2 3 4 5 
Are arr1 and arr2 same?:  
FALSE

Address of arr1: 0x7ffd8d5843b0
Address of arr2: 0x7ffd8d584390

数组退化问题的解决方案

为了避免数组退化问题,我们可以采用以下方法 −

将数组大小作为单独参数传递

为了避免数组退化,你可以将数组大小作为参数传递,与数组一起传递,因为数组的大小不会丢失。

在这个示例中,我们将数组及其大小作为函数参数传递。这里会发生数组退化,但由于有数组大小,不会引起任何问题。

#include <iostream>
using namespace std;

void printArray(int arr[], int size) {
   cout << "Given array: ";
   for (int i = 0; i < size; i++){
      cout << arr[i] << " ";
   }
   cout << endl;
}

int main() {
   int arr[5] = {10, 20, 30, 40, 50};
   int size = sizeof(arr) / sizeof(arr[0]);

   // Passing both array and size
   printArray(arr, size);

   return 0;
}

上述代码的输出如下 −

Given array: 10 20 30 40 50 

使用 std::array 和 std::vector

你可以使用 std::array 和 std::vector 来避免数组退化,因为这些是 C++ 中的对象,而不是数组,因此不会发生数组退化。

下面是使用 std::array 和 std::vector 避免数组退化的示例

Using std::array Using std::vector
#include <iostream>
#include <array>
using namespace std;

void printArray(array<int, 5> arr) {
   cout << "Array size: " << arr.size() << endl;
   cout << "Elements: ";
   for (int num : arr){
      cout << num << " ";
   }
   cout << endl;
}

int main() {
   array<int, 5> stdArr = {1, 2, 3, 4, 5};
   printArray(stdArr);

   return 0;
}

上述代码的输出如下 −

Array size: 5
Elements: 1 2 3 4 5 
#include <iostream>
#include <vector>
using namespace std;

void printVector(vector<int> vec) {
   cout << "Vector size: " << vec.size() << endl;
   cout << "Elements: ";
   for(int num : vec) {
      cout << num << " ";
   }
   cout << endl;
}

int main() {
   vector<int> vec = {1, 2, 3, 4, 5};
   printVector(vec);       
    
   return 0;
}

上述代码的输出如下 −

Vector size: 5
Elements: 1 2 3 4 5 

结论

C++ 中的数组退化指的是由于各种原因(如将数组传递给函数将数组赋值给指针在指针运算中使用数组)而导致数组丢失维度和类型的信息。这可能会引起我们在本章中强调的各种问题,并提供了相应的解决方案。