繁体   English   中英

2D数组到指针到指针的转换

[英]conversion of 2D array to pointer-to-pointer

Activity solution[a][b];

...

Activity **mother = solution;

我想将2D对象数组转换为指针到指针。 我怎样才能做到这一点;

我在Google上搜索了它。 但是我只找到一个维数组的例子。

单纯的转换对您无济于事。 2D数组类型和指针到指针类型之间没有任何兼容性。 这样的转换是没有意义的。

如果确实需要这样做,则必须引入一个额外的中间“行索引”数组,该数组将弥合2D数组语义和指针对指针语义之间的鸿沟

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]

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

这是 一切皆有可能! 但这是因此需要一定程度的理解。

为此,让我们从2个一维数组的简单示例开始: 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) 这对于char是正确的,但对于占用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 << siArray << ' ' << &(siArray[1])将输出:

0x76543218 0x7654321A

*(siArray + 1)仍将与siArray[1]索引相同的siArray元素。 这是因为在执行指针算术时, 考虑到正在操作的地址的类型,因此增加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个1维数组指针,但包含2个数组的内容。 (偶然地,在不存储指针的32位计算机上,可以节省8字节的内存,这对于8字节的2维数组而言相当可观。)因此,将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;
}

现场例子

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

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

因此,您可以看到从数组转换为指针会造成大量信息丢失。 您可能会想保留元素类型,从而简化指针算法。 值得注意的是, reinterpret_cast仅将转换为char*视为定义的行为: http : //en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing

我想将2D对象数组转换为指针到指针。 我怎样才能做到这一点?

为什么? 是否因为接口需要指向指针的指针?

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

Activity solution[a][b];

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

Activity** mother = solutionPtrs;

为什么你不能只投中一个二维数组TT** 好吧,因为他们彼此无关!

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

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

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

作为记录,如果有人发现它有用:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM