C++多维数组和指向指针表的指针



事情是这样的。我可以完全理解多维数组的概念(让我们考虑一下 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数组"。您提到打印TT[0]给出相同的结果,这是由于数组到指针的转换。

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

  2. 表达式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添加到此对象以获取指向该子数组中第 jint对象的指针。这被取消引用以给出该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;
}

相关内容

  • 没有找到相关文章

最新更新