【问题标题】:C++ multidimensional array and pointers to table of pointersC++ 多维数组和指向指针表的指针
【发布时间】:2013-04-29 16:32:43
【问题描述】:

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

我们这样做:

// 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 不是常数,我们不能。 因此,我需要将动态创建的多维数组的指针传递给函数,而无需为此手动分配内存。 如何做到这一点?

【问题讨论】:

  • 如果你“完全理解”了这个主题,为什么还要我们“检查我是否正确”?
  • "因为在 C++ 中,数组实际上是一个指针" 这就是错误所在。数组不是指针。
  • 我的意思是当我们创建一个数组时,我们得到的最直接的方法是一个指针(C++ 分配内存在一边,所以没有什么“真正”将该指针连接到这个数据块,除了它是value 是第一个单元格的地址。我们也可以更改指针的值。
  • 通过说“我完全理解..”(阅读其余部分)然后写“检查我是否正确”,我想告诉你我是对的。就像当你说“嘿,我很强壮”然后举起重物来证明某人时......
  • @MichałKownacki “当我们创建一个数组时,我们得到的最直接的方法就是一个指针”——这就是你错的地方。你根本没有得到指针。你得到一个数组。数组和指针不一样。

标签: c++ function pointers multidimensional-array dynamically-generated


【解决方案1】:

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

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

int arr[10];

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

int T[n][m] ;

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

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

  2. 表达式T[0] 可以转换为指向第一个子数组的第一个元素的指针。所以你会得到一个指向 T[0][0] 类型为 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 添加到此以继续指向ith 子数组。然后取消引用以获取子数组。接下来,这个子数组还要进行数组到指针的转换,得到一个int*。然后将j 添加到此以获取指向该子数组中jth int 对象的指针。这被取消引用以给出int

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

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

因此,当您使用add_5(T) 调用此函数时,您不会传递由T 表示的数组。 T 实际上是在进行数组到指针的转换,为您提供int (*)[m],并且 this 指针正在传递给函数。

【讨论】:

  • 好的,所以数组不是 C++ 中的指针,但我们有一个从数组“对象”到指针的转换。我懂了。谢谢你。那么为什么如果在显示的程序中我必须使 n 和 m const 呢?以及如何避免这种情况,同时声明数组如下: int T[n][m] ; ?并将这样的二维数组传递给函数(C++ 应该记住维度还是我们应该将其作为参数传递?)?
  • @MichalKownacki nm 必须是 const 的原因是另一个问题。在 C++ 中,类型在编译时是完全已知的(它是一种静态类型语言)。由于数组的大小是其类型的一部分,因此在编译时也必须知道它。给出数组大小的表达式必须是所谓的常量表达式(可以在编译时计算)。 const int 可以用作常量表达式。非const int 不能,因为它可以在运行时更改。
  • @MichałKownacki 如果需要动态大小的数组,则需要动态分配。但是,您最好使用为您封装分配的标准容器,例如std::vector
  • 我明白了。您写道“数组的大小是其类型的一部分”。那么请告诉我,在创建 int T[n][m] 时,在预编译时程序中究竟存储了什么?我注意到只有 m 需要保持不变。所以当创建 int T[n][m] T 的类型是 int (*)[m] ?
  • 请回答我最后的评论 :)
【解决方案2】:

这是一个二维动态数组的例子:

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;
}

【讨论】:

  • 好的,我知道我们可以做这样的事情。但是如果数组是这样声明的,C++ 是否允许将数组传递给函数: int T[n][m] ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-11
  • 2023-03-11
  • 2013-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多