C/C++编译器如何区分常规二维数组和数组指针数组?

Maz*_*azi 5 c c++ pointer-arithmetic multidimensional-array implicit-conversion

常规静态分配数组如下所示,可以使用以下公式进行访问:

const int N = 3;
const int M = 3;

int a1[N][M] = { {0,1,2}, {3,4,5}, {6,7,8} };

int x = a1[1][2]; // x = 5 
int y = *(a1+2+N*1); // y = 5, this is what [] operator is doing in the background
Run Code Online (Sandbox Code Playgroud)

数组是连续的内存区域。在动态数组分配的情况下看起来有所不同,而是有指向数组的指针数组:

int** a2 = new int*[N];
for (int i = 0; i < N; i++) 
   a2[i] = new int[M];

//Assignment of values as in previous example

int x = a2[1][2];
int y = *(*(a2+1))+2); // This is what [] operator is doing in the background, it needs to dereference pointers twice

Run Code Online (Sandbox Code Playgroud)

我们可以看到,在典型的连续数组和动态分配数组的情况下,[]运算符所做的操作是完全不同的。我的问题现在如下:

  1. 我对 [] 操作的理解是否正确?
  2. C/C++编译器如何区分它应该执行哪个[]操作,以及它在哪里实现?我可以想象通过重载 [] 运算符在 C++ 中自己实现它,但是 C/C++ 如何处理这个呢?
  3. 在C语言中使用malloc而不是new能正常工作吗?我看不出有什么理由不这样做。

Vla*_*cow 7

对于这个数组的声明

int a1[N][M] = { {0,1,2}, {3,4,5}, {6,7,8} };
Run Code Online (Sandbox Code Playgroud)

这些记录

int x = a1[1][2];
int y = *(a1+2+N*1); 
Run Code Online (Sandbox Code Playgroud)

不等价。

第二个是不正确的。该表达式*(a1+2+N*1)的类型隐式转换为用作初始值设定int[3]项的类型的对象。int *所以整型变量y是由指针初始化的。

运算符 a1[1] 的计算方式类似于*( a1 + 1 )。结果是 类型的一维数组int[3]

因此,应用第二个下标运算符,您将得到*( *( a1 + 1 ) + 2 )

使用二维数组和动态分配数组时的表达式之间的区别在于,该表达式中二维数组的指示符(a1 + 1) 隐式转换为指向其类型的第一个元素的指针int ( * )[3],而指向动态分配数组的指针指针数组仍然具有相同的类型int **

在第一种情况下,取消引用表达式,*(a1 + 1 )您将获得类型的左值int[3],该类型的左值又在表达式中使用,*( a1 + 1) + 2并再次隐式转换为类型的指针int *

在第二种情况下,表达式*(a1 + 1)生成 类型的对象int *

在这两种情况下都使用了指针算术。不同之处在于,当您在下标运算符中使用数组时,它们会隐式转换为指向其第一个元素的指针。

当您动态分配数组时,您已经处理了指向其第一个元素的指针。

例如,代替这些分配

int** a2 = new int*[N];
for (int i = 0; i < N; i++) 
   a2[i] = new int[M];
Run Code Online (Sandbox Code Playgroud)

你可以写

int ( *a2 )[M] = new int[N][M];
Run Code Online (Sandbox Code Playgroud)