如何将一个二维数组转换为指向指针的指针?

29
Activity solution[a][b];

...

Activity **mother = solution;

我想将一个二维对象数组转换为指向指针的指针。我该如何做到这一点?

我在谷歌上搜索了一下,但只找到了一维数组的例子。


这个回答解决了你的问题吗?将多维数组转换为指针在C++中 - undefined
6个回答

28

仅仅进行转换是没有用的。二维数组类型和指向指针的类型之间没有任何兼容性。这样的转换没有意义。

如果你确实需要这么做,你必须引入一个额外的中间"行索引"数组,它将连接二维数组语义和指向指针语义之间的差距。

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]


24
你可以对一维数组进行此操作,而不能对二维数组进行此操作,这与数组元素在内存中存储方式有关。对于一维数组,所有元素都按顺序存储,因此表达式“array[i]”等同于表达式“*(array + i)”。可以看到,执行数组索引操作不需要知道数组大小。但是,对于二维数组,元素按“行优先”顺序存储,这意味着零行中的所有元素首先存储,其次是第一行中的元素,其次是第二行中的元素,依此类推。因此,表达式“array[i][j]”等同于表达式“*(array + (i * ROW_SIZE) + j)”,其中“ROW_SIZE”是每行中元素的数量。因此,执行数组索引操作需要知道数组的行大小,将数组变量转换为指针会丢失该信息。

15
这是!一切皆有可能!但是由于这是,因此需要一定的理解水平。
为此,让我们从一个简单的例子开始,使用两个一维数组: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 << firstName << ' ' << lastName,您将得到:

0x76543210 0x76543214

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

在我们继续之前,有一些重要的事情需要注意,char正好占用1个字节,因此数组中每个后续char的地址将简单地是下一个地址。这被下标运算符利用,例如: firstName[1]等同于*(firstName + 1)。对于占用超过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
+------------+--------+
< p >即使< code>cout << siArray << ' ' << &(siArray [1])将输出:

< blockquote> < p>0x76543218 0x7654321A < p>< code>*(siArray + 1)仍将索引< code>siArray的相同元素,如< code>siArray [1]。这是因为在执行指针算术时,会考虑所操作地址的类型,因此增加< code>short *实际上会将地址增加< code>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个一维数组指针,而是包含了这两个数组的内容。(顺便说一下,在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;
}

实时示例

请注意,在此示例中,即使我通过psi2DPointer引用si2DArray,我仍然使用来自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

非常感谢Jonathan提供的解决方案。你知道这种转换所带来的开销吗?你所说的“转换很大”是什么意思? - khateeb

6
我希望把对象的二维数组转换成指向指针的指针,如何实现?
为什么要这么做?是因为接口期望得到指向指针的指针吗?
如果是,你需要创建一个新数组来存储这些指针。
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*,因为你得到的是指向该数组第一个元素的指针。
对于二维数组也是一样的,但是如果你有一个 T[a][b],那么它会衰减为 (T[b])*,因为二维数组不是指针数组,而是数组的数组。

2
这行代码 solutionPtrs[a] = solution[a]; 应该改为 solutionPtrs[i] = solution[i]; - jcchuks

1

不确定您是否正在寻找类似于此的内容。您应该提供更多关于您想要实现的细节。它们是根本不同的类型。一种解决方案如下。

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

// 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]);
    }
}

-1

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


1
你能详细解释一下吗? - Bahri Gökcan
如果您提供更多有关您想要使用“mother”做什么的详细信息,那么我可以在我的答案中给出建议。 - Oliver Charlesworth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接