【问题标题】:conversion of 2D array to pointer-to-pointer将二维数组转换为指针到指针
【发布时间】:2011-11-20 18:35:48
【问题描述】:
Activity solution[a][b];

...

Activity **mother = solution;

我想将二维对象数组转换为指针到指针。我该怎么做;

我在谷歌上搜索过。但是我发现只有一维数组示例。

【问题讨论】:

标签: c++ pointers static


【解决方案1】:

在这里,单纯的转换对您没有帮助。二维数组类型和指针到指针类型之间没有任何类型的兼容性。这样的转换是没有意义的。

如果你真的需要这样做,你必须引入一个额外的中间“行索引”数组,它将弥合二维数组语义和指针到指针语义之间的差距

Activity solution[a][b];

Activity *solution_rows[a] = { solution[0], solution[1] /* and so on */ };

Activity **mother = solution_rows;

现在访问 mother[i][j] 将允许您访问 solution[i][j]

【讨论】:

    【解决方案2】:

    您可以为一维数组而不是二维数组执行此操作的原因与实际数组元素在内存中的存储方式有关。对于一维数组,所有元素都是连续存储的,所以表达式array[i] 等价于表达式*(array + i)。如您所见,执行数组索引操作不需要数组大小。但是,对于二维数组,元素以“行主要”顺序存储,这意味着首先存储第零行中的所有元素,然后是第一行中的元素,然后是第二行中的元素等。因此,表达式array[i][j] 等价于*(array + (i * ROW_SIZE) + j),其中ROW_SIZE 是每行中的元素数。因此,执行数组索引操作需要数组的行大小,而将数组变量转换为指针会丢失该信息。

    【讨论】:

      【解决方案3】:

      这是!一切皆有可能!但是这是,所以需要一定程度的理解。

      为此,让我们从 2 个 1 维数组的简单示例开始:char firstName[4] = { 'J', 'o', 'n', '\0' }char lastName[4] = { 'M', 'e', 'e', '\0' } 让我们在这里看看可能的内存布局:

      +------------+-------+
      |  Address   | Value |
      +------------+-------+
      | 0x76543210 | 0x4A  | <- firstName[0] - 'J'
      | 0x76543211 | 0x6F  | <- firstName[1] - 'o'
      | 0x76543212 | 0x6E  | <- firstName[2] - 'n'
      | 0x76543213 | 0x00  | <- firstName[3] - '\0'
      +------------+-------+
      | 0x76543214 | 0x4D  | <- lastName[0] - 'M'
      | 0x76543215 | 0x65  | <- lastName[1] - 'e'
      | 0x76543216 | 0x65  | <- lastName[2] - 'e'
      | 0x76543217 | 0x00  | <- lastName[3] - '\0'
      +------------+-------+
      

      鉴于此内存布局,如果您要执行 cout &lt;&lt; firstName &lt;&lt; ' ' &lt;&lt; lastName,您会得到:

      0x76543210 0x76543214

      这些数组实际上只是指向其第一个元素的指针!这说明了 Array to Pointer Decay,您可以在此处阅读更多信息:http://en.cppreference.com/w/cpp/language/array#Array-to-pointer_decay

      在我们继续之前,这里需要注意一些重要的事情,chars 恰好占用 1 个字节,因此数组中每个后续 char 的地址将只是下一个地址。 Subscript Operator 以这种方式利用了这一点:firstName[1] 等效于 *(firstName + 1)。这对于chars 是正确的,但对于占用超过 1 个字节的任何其他类型也是如此。让我们举个例子:short siArray = { 1, 2, 3, 4 }siArray 的可能内存布局如下所示:

      +------------+--------+
      |  Address   | Value  |
      +------------+--------+
      | 0x76543218 | 0x0001 | <- siArray[0] - 1
      | 0x7654321A | 0x0002 | <- siArray[1] - 2
      | 0x7654321C | 0x0003 | <- siArray[2] - 3
      | 0x7654321E | 0x0004 | <- siArray[3] - 4
      +------------+--------+
      

      即使cout &lt;&lt; siArray &lt;&lt; ' ' &lt;&lt; &amp;(siArray[1]) 会输出:

      0x76543218 0x7654321A

      *(siArray + 1) 仍将索引siArray 的相同元素作为siArray[1]。这是因为在进行指针运算时 会考虑正在操作的地址的类型,因此增加short* 实际上会将地址增加sizeof(short)。您可以在此处阅读有关指针算法的更多信息:http://en.cppreference.com/w/cpp/language/operator_arithmetic

      最后让我们看看 是如何存储二维数组的。给定:char name[2][4] = { { 'J', 'o', 'n', '\0' }, { 'M', 'e', 'e', '\0' } } 可能的内存布局是:

      +------------+-------+
      |  Address   | Value |
      +------------+-------+
      | 0x76543220 | 0x4A  | <- name[0][0] - 'J'
      | 0x76543221 | 0x6F  | <- name[0][1] - 'o'
      | 0x76543222 | 0x6E  | <- name[0][2] - 'n'
      | 0x76543223 | 0x00  | <- name[0][3] - '\0'
      | 0x76543224 | 0x4D  | <- name[1][0] - 'M'
      | 0x76543225 | 0x65  | <- name[1][1] - 'e'
      | 0x76543226 | 0x65  | <- name[1][2] - 'e'
      | 0x76543227 | 0x00  | <- name[1][3] - '\0'
      +------------+-------+
      

      既然我们知道一维数组值实际上只是一个指针,我们可以从这个内存布局中看出name[0] 不是一个指针,它只是第一个数组的第一个字符.因此name 包含 2 个一维数组指针,但包含 2 个数组的内容。 (顺便说一下,在不存储指针的 32 位机器上节省了 8 字节的内存,这对于 8 字节的二维数组来说是相当可观的。)因此尝试将 name 视为 char** 将尝试使用字符作为指针。


      了解了这一点后,我们真的只需要避免使用 的指针算法来查找解引用值。为此,我们需要使用char*,这样加 1 实际上只是加 1。例如:

      const short si2DArray[2][3] = { { 11, 12, 13 }, { 21, 22, 23 } };
      const auto psi2DPointer = reinterpret_cast<const char*>(si2DArray);
      
      for(auto i = 0U; i < size(si2DArray); ++i) {
          for(auto j = 0U; j < size(*si2DArray); ++j) {
              cout << *reinterpret_cast<const short*>(psi2DPointer + i * sizeof(*si2DArray) + j * sizeof(**si2DArray)) << '\t';
          }
          cout << endl;
      }
      

      Live Example

      请注意,在此示例中,即使我引用 si2DArray 认为 psi2DPointer 我仍在使用来自 si2DArray 的信息进行索引,即:

      1. 主维度中有多少个数组:size(si2DArray)
      2. 次要维度中有多少元素:size(*si2DArray)
      3. 次要维度在内存中的大小是多少:sizeof(*si2DArray)
      4. 数组的元素类型是什么:sizeof(**si2DArray)

      因此,您可以看到从数组转换为指针的信息丢失是巨大的。您可能很想保留元素类型,从而也简化了指针运算。值得注意的是,只有转换为char* 才被视为reinterpret_cast 定义的行为:http://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing

      【讨论】:

      • 非常感谢乔纳森的解决方案。你知道这种转换的开销吗?您所说的“转化率很高”是什么意思?
      【解决方案4】:

      我想将二维对象数组转换为指针到指针。我该怎么做?

      为什么?是因为接口需要一个指向指针的指针吗?

      如果是这样,您需要创建一个包含这些指针的新数组。

      Activity solution[a][b];
      
      Activity* solutionPtrs[a];
      for (int i = 0; i < a; ++i)
          solutionPtrs[a] = solution[a];
      
      Activity** mother = solutionPtrs;
      

      为什么不能将T 的二维数组转换为T**?好吧,因为它们彼此无关!

      您可以将 T[a] 强制转换为 T*,因为您获得了指向数组第一个元素的指针。

      您也可以使用 2D 数组执行此操作,但如果您有 T[a][b],那么它会衰减为 (T[b])*,因为 2D 数组不是指针数组,而是数组数组。

      【讨论】:

      • 这行solutionPtrs[a] = solution[a];不应该是solutionPtrs[i] = solution[ i];
      【解决方案5】:

      不确定您是否正在寻找类似的东西。您应该提供有关您想要实现的目标的更多详细信息。它们是根本不同的类型。一种解决方案如下。

      记录一下,如果有人觉得它有用:

      // define matrix
      double A[3][3] = {
          { 1, 2, 3},
          { 4, 5, 6},
          { 7, 8, 9}
      };
      
      // allocate memory
      double ** A_ptr = (double **) malloc(sizeof (double *) * 3);
      for (int i = 0; i < 3; i++)
          A_ptr[i] = (double *) malloc(sizeof (double) * 3);
      
      // copy matrix
      for (int i = 0; i < 3; i++) {
          for (int j = 0; j < 3; j++) {
              A_ptr[i][j] = A[i][j];
              printf(" %f ", A_ptr[i][j]);
          }
      }
      

      【讨论】:

        【解决方案6】:

        你不能。它们是根本不同的类型。

        【讨论】:

        • 能详细解释一下吗?
        • 如果你能详细说明你想用mother做什么,那么我可以在我的回答中给你一个建议。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多