事情是这样的。我可以完全理解多维数组的概念(让我们考虑一下 2D(,由指针到数组到指针等等......
我们做这样的事情:
// we can use dynamically defined size n and m
int n = 3, m = 5 ;
int **T = new int *[n];
for (int i = 0 ; i < n ; ++i)
T[i] = new int[m];
我们得到的是:(检查我是否在这里(
- 3 个 5 个整数的内存块,放置在内存中的某个位置
- 一个与整数块数(行数(大小相同的额外内存块。这个块是一个指向这些 int 行的指针数组(通常为 4 个字节,如 int 的指针(。
- 我们最感兴趣的是 - 类型为 (**T( 的 T - 指向指针的指针。这恰恰是指向指针数组的指针,因为C++数组实际上是指向内存块的指针,因此 t[] 或 t[0] 表示 *t,t[x] 表示 *(t+x(。
现在的问题是,当我们这样做时:
int n = 3, m = 5 ;
int T[n][m] ;
我们所拥有的不是我之前展示的东西。我们变得很奇怪。什么是T?当打印 T 时,我们得到与 T[0] 相同的值。看起来我们保留了一个大小为 n*m 的整数块,没有额外的指向行的指针数组。
我的问题是:编译器是否记住数组的维度以及行数和列数?当请求 T[i][j] 时,它实际上要求 *(T+i*n+j(,所以这个 n 存储在某个地方?问题是当我们试图将这个东西(T(传递给一个函数时。我不知道为什么,但如果 n 和 m 是常量,则可以将 T 作为指向该数组的指针传递,以像在这个程序中一样工作:
#include <stdio.h>
const int n = 3, m = 4 ; // this is constant!
void add_5(int K[][m])
{
for (int i = 0 ; i < n ; ++i)
for (int j = 0 ; j < m ; j++)
K[i][j] += 5 ;
}
int main()
{
// n x m array the most straight forward method
int T[n][m] ;
for (int i = 0 ; i < n ; ++i)
for (int j = 0 ; j < m ; ++j)
T[i][j] = i*m + j ;
for (int i = 0 ; i < n ; ++i)
{
for (int j = 0 ; j < m ; j++)
printf("%d ",T[i][j]) ;
printf("n") ;
}
printf("n") ;
// adding 5 to all cells
add_5(T) ;
printf("it worked!!n") ;
for (int i = 0 ; i < n ; ++i)
{
for (int j = 0 ; j < m ; j++)
printf("%d ",T[i][j]) ;
printf("n") ;
}
int ss ;
scanf("%d",&ss) ;
}
但是,如果n和m不是常数,我们就不能。所以我需要的是将动态创建的多维数组的指针传递给函数,而无需为此手动分配内存。怎么做?
在C++数组实际上是一个指向内存块的指针
绝对不行。数组与指针完全分开。您可能会感到困惑的原因是,使用了一种称为数组到指针转换的标准转换。考虑:
int arr[10];
变量 arr
表示数组。它根本不是指针。碰巧的是,在许多情况下,数组的名称将被转换为指向其第一个元素的指针。也就是说,转换将其变成int*
。
int T[n][m] ;
在这种情况下,T
是"m
int
的数组n
数组"。您提到打印T
和T[0]
给出相同的结果,这是由于数组到指针的转换。
表达式
T
可以转换为指向第一个元素的指针,即int (*)[m]
,因为T
的第一个元素本身就是一个包含m
元素的数组。表达式
T[0]
可以转换为指向第一个子数组的第一个元素的指针。因此,您将获得指向类型为int*
的元素T[0][0]
的指针。
由于数组在内存中的布局方式,这些指针保持相同的地址。数组开始的地址与该数组的第一个元素的地址相同。但是,指针的行为方式不同。如果递增由 T
生成的指针,则移动到下一个子数组。如果递增由 T[0]
生成的指针,则移动到下一个int
。
它可能会帮助您查看与动态分配的"2D 数组"相比,2D 数组在内存中的布局方式。3x3 2D 数组如下所示:
0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ int │ int │ int │ int │ int │ int │ int │ int │ int │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
而如果您动态分配 3×3 的"2D 数组":
┌─────┐
│ │ // The int**
└──╂──┘
┃
▼
┌─────┬─────┬─────┐
│ │ │ ┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // An array of int*
└──╂──┴──╂──┴─────┘ ┃
┃ ┗━━━━━━━━━━━━━━━┓ ┃
▼ ▼ ▼
┌─────┬─────┬─────┐ ┌─────┬─────┬─────┐ ┌─────┬─────┬─────┐
│ int │ int │ int │ │ int │ int │ int │ │ int │ int │ int │ // Arrays of ints
└─────┴─────┴─────┘ └─────┴─────┴─────┘ └─────┴─────┴─────┘
0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2
是编译器是否记住数组的维度以及行数和列数?
的,如果您有一个数组类型的变量,则数组的大小是该类型的一部分。编译器始终知道变量的类型。
当请求 T[i][j] 时,它实际上要求 *(T+i*n+j(,所以这个 n 存储在某个地方?
表达式 T[i][j]
等效于 *(*(T + i) + j)
。让我们了解一下这是做什么的。首先,数组到指针的转换由T
进行,给出一个int (*)[m]
。然后,我们在此添加i
以指向第 i
个子数组。然后取消引用它以获取子数组。接下来,这个子数组也经历了数组到指针的转换,给出了一个int*
。然后,将j
添加到此对象以获取指向该子数组中第 j
个int
对象的指针。这被取消引用以给出该int
.
问题是当我们试图将这个东西(T(传递给一个函数时。我不知道为什么,但如果 n 和 m 是常量,则可以将 T 作为指向该数组的指针传递给函数
这实际上是一个谎言。您不会将 2D 数组传递给函数。事实上,没有数组类型参数这样的东西。你的论点int K[][m]
实际上等同于int (*K)[m]
.也就是说,所有数组类型参数都转换为指针。
所以当你用add_5(T)
调用这个函数时,你不会传递用T
表示的数组。 T
实际上正在进行数组到指针的转换,以为您提供int (*)[m]
并且此指针正在传递到函数中。
下面是一个 2D 动态数组的示例:
const int Mapsize = n
int** GameField2 = 0;
GameField2 = new int*[Mapsize]; // memory alocation
for(int i = 0; i < Mapsize; i++)
GameField2[i] = new int[Mapsize];
for(int j = 0; j < Mapsize; j++)
for(int i = 0; i < Mapsize; i++)
GameField2[i][j] = 0;
如果您希望在任何函数上对此数组进行操作,只需像这样传递指针:
int Whatver(int** GameField)
{
GameField[a][b]=x;
}