0

事情就是这样。我可以完全理解一个多维数组的概念(让我们暂时考虑一下二维),由指向数组的指针等组成......

我们做这样的事情:

// 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 行的指针数组(对于 int 类的指针通常为 4 个字节)。
  • 我们最感兴趣的——即类型为 (**T) 的 T——一个指向指针的指针。这正是指向指针数组的指针,因为在 C++ 中,数组实际上是指向内存块的指针,因此 t[] 或 t[0] 表示 *t,而 t[x] 表示 *(t+ X)。

现在问题是当我们这样做时:

int n = 3, m = 5 ;
int T[n][m] ;

我们所拥有的不是 w 可以做我之前展示的事情。我们有点奇怪。什么是T?当 printfing 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 不是常数,我们不能。所以我需要将动态创建的多维数组的指针传递给一个函数,而无需为此手动分配内存。这该怎么做?

4

2 回答 2

5

在 C++ 中,数组实际上是指向内存块的指针

绝对不。数组与指针完全分开。您可能会产生这种混淆的原因是一种称为数组到指针转换的标准转换。考虑:

int arr[10];

变量arr表示一个数组。它根本不是指针。碰巧在许多情况下,数组的名称将被转换为指向其第一个元素的指针。也就是说,转换将其转换为int*.

int T[n][m] ;

在这种情况下,T是一个“s 数组的n数组m int”。您提到打印两者TT[0]给出相同的结果,这是由于数组到指针的转换。

  1. 表达式T可以转换为指向第一个元素的指针;即 an int (*)[m],因为 的第一个元素T本身就是一个包含m元素的数组。

  2. 该表达式T[0]可以转换为指向第一个子数组的第一个元素的指针。所以你得到了一个指向T[0][0]type 元素的指针int*

由于数组在内存中的布局方式,这些指针拥有相同的地址。数组开始的地址与该数组的第一个元素的地址相同。但是,指针的行为方式不同。如果增加由 产生的指针T,则移动到下一个子数组。如果您增加由 产生的指针T[0],您将移动到下一个int

与动态分配的“二维数组”相比,它可能会帮助您查看二维数组在内存中的布局图。一个 3×3 二维数组如下所示:

  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“二维数组”:

┌─────┐
│     │ // 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第 th 个子数组。然后取消引用以获取子数组。接下来,这个子数组也进行数组到指针的转换,给出一个int*. 然后添加j到 this 以获取指向该子数组中j第 th 个int对象的指针。this 被取消引用以给出int.

问题是当我们试图将这个东西(T)传递给一个函数时。我不知道为什么,但是如果 n 和 m 是常量,则可以将 T 作为指向该数组的指针传递给函数

这实际上是一个谎言。您没有将二维数组传递给函数。事实上,没有数组类型参数这样的东西。您的论点int K[][m]实际上等同于int (*K)[m]. 也就是说,所有数组类型参数都转换为指针。

因此,当您使用 调用此函数时add_5(T),您不会传递由 表示的数组TT实际上正在进行数组到指针的转换以给您一个int (*)[m]并且这个指针正在被传递到函数中。

于 2013-05-05T23:04:12.707 回答
0

这是二维动态数组的示例:

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;
}
于 2013-05-05T23:05:32.010 回答