c - 为什么我可以在二维数组的情况下从 int ** 转换为 int *,但在指针数组的情况下则不行


#include <stdio.h>
#include <string.h>
int main(void)
{
int arr2d[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
int* arr = (int*)arr2d;
int i = 0;
for (i = 0; i < 9; i++)
{
printf("%d ", arr[i]);
}
printf("nn");
int arr1[] = { 11,12 };
int arr2[] = {13, 14, 15, 16};
int arr3[] = { 17, 18, 19 };
int * parr[3] = { arr1,arr2,arr3 };
int *parr1 = (int*)parr;
for (i = 0; i < 9; i++)
{
printf("%d ", parr1[i]);
}
return 0;

一开始我从二维数组转换并打印出来,但之后我用指针数组做了,它不起作用,有人可以解释一下吗?

您可能希望了解这些对象在内存中的布局方式:

int array[][3] = { {1,2,3}, {4,5,6}, {7,8,9} };
int *p_array = (int *) array;

这会在内存(在本例中为堆栈)中创建类型为int[][3]类型的对象array,该对象以假设的(16 位字大小)体系结构物理布局为:

array 0x8000: 0x0001
0x8002: 0x0002
0x8004: 0x0003
0x8006: 0x0004
0x8008: 0x0005
0x800A: 0x0006
0x800C: 0x0007
0x800E: 0x0008
0x8010: 0x0009
p_array 0x8012: 0x8000

其中0x8000表示array的假想堆栈地址。

但是,在第二个示例中:

int array1[] = { 11, 12 };
int array2[] = { 13, 14, 15, 16 };
int array3[] = { 17, 18, 19 };
int *array_pointers[] = { array1, array2, array3 };
int *array_pointers2 = (int*) array_pointers;

这里array_pointers是类型int *[],这意味着它是指向ints 数组的指针。内存布局如下所示:

array1 0x8000: 0x000B
0x8002: 0x000C
array2 0x8004: 0x000D
0x8006: 0x000E
0x8008: 0x000F
0x800A: 0x0010
array3 0x800C: 0x0011
0x800E: 0x0012
0x8010: 0x0013

array_pointers:  0x8012: 0x8000 address of array1
0x8014: 0x8004 address of array2
0x8016: 0x800C address of array3
array_pointers2: 0x8018: 0x8012 address of array_pointers

因此,当您尝试打印array_pointers2的内容时,现在包含指向int数组的指针,您将打印地址值而不是它们指向的数字。

下面是一个输出这些对象的内存地址的示例:

https://ideone.com/T52Ow0

哪些打印:

array      0x7ffeda682090: 0x00000000000001
0x7ffeda682094: 0x00000000000002
0x7ffeda682098: 0x00000000000003
0x7ffeda68209c: 0x00000000000004
0x7ffeda6820a0: 0x00000000000005
0x7ffeda6820a4: 0x00000000000006
0x7ffeda6820a8: 0x00000000000007
0x7ffeda6820ac: 0x00000000000008
0x7ffeda6820b0: 0x00000000000009
p_array    0x7ffeda682038: 0x007ffeda682090
array1     0x7ffeda68204c: 0x0000000000000b
0x7ffeda682050: 0x0000000000000c
array2     0x7ffeda682060: 0x0000000000000d
0x7ffeda682064: 0x0000000000000e
0x7ffeda682068: 0x0000000000000f
0x7ffeda68206c: 0x00000000000010
array3     0x7ffeda682054: 0x00000000000011
0x7ffeda682058: 0x00000000000012
0x7ffeda68205c: 0x00000000000013
array_poin 0x7ffeda682070: 0x007ffeda68204c
0x7ffeda682078: 0x007ffeda682060
0x7ffeda682080: 0x007ffeda682054
array_poi2 0x7ffeda682040: 0x007ffeda682070

在我的电脑上。

外部链接过期时的源代码:

#include <stdio.h>
#include <string.h>
int main(void)
{
int array[][3] = { {1,2,3}, {4,5,6}, {7,8,9} };
int *p_array = (int*) array;
for (int i = 0; i < 9; i++) {
printf("%-10s %08p: %016pn", i == 0 ? "array" : "", &p_array[i], p_array[i]);
}
printf("%-10s %08p: %016pn", "p_array", &p_array, p_array);
int array1[] = { 11, 12 };
int array2[] = { 13, 14, 15, 16 };
int array3[] = { 17, 18, 19 };
int *array_pointers[] = { array1, array2, array3 };
int *array_pointers2 = (int*) array_pointers;
putchar('n');
for (int i = 0; i < sizeof(array1) / sizeof(array1[0]); i++) {
printf("%-10s %08p: %016pn", i == 0 ? "array1" : "", &array1[i], array1[i]);
}
for (int i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
printf("%-10s %08p: %016pn", i == 0 ? "array2" : "", &array2[i], array2[i]);
}
for (int i = 0; i < sizeof(array3) / sizeof(array3[0]); i++) {
printf("%-10s %08p: %016pn", i == 0 ? "array3" : "", &array3[i], array3[i]);
}
for (int i = 0; i < sizeof(array_pointers) / sizeof(array_pointers[0]); i++) {
printf("%-10s %08p: %016pn", i == 0 ? "array_poin" : "", &array_pointers[i], array_pointers[i]);
}
printf("%-10s %08p: %016pn", "array_poi2", &array_pointers2, array_pointers2);
return 0;
}

您不能简单地将int *与从 0 到 N 的单个循环一起使用,就像处理连续的整数数组一样,例如arr2d因为:

  1. 指针在内存中可能相距很远
  2. 你需要取消引用这些指针中的每一个,你不能用int *这样做,因为int *A[i]解释为int,而不是指针。

要了解如何修复它,您可能需要了解事物在内存中的布局方式。 让我们从一个简单的开始:您的arr2d声明。 假设一个 2 字节的int,下面是它的外观:

arr2d @ 0x8000
=======================
|  arr2d[0] @ 0x8000    |
|   -----------------   |
|  | 1      2      3 |  |
|   -----------------   |
|  @8000  @8002  @8004  |
|                       |
|  arr2d[1] @ 0x8006    |
|   -----------------   |
|  | 4      5      6 |  |
|   -----------------   |
|  @8006  @8008  @800A  |
|                       |
|  arr2d[0] @ 0x800C    |
|   -----------------   |
|  | 7      8      9 |  |
|   -----------------   |
|  @800C  @800E  @8010  |
=======================

注意到从一个项目到下一个项目的内存地址是如何相隔 2 个字节的吗? 这就是为什么您可以执行int* arr = (int*)arr2d并打印arr[i]以获取所需值的原因之一。 另一个原因是您正在存储整数。

现在让我们看看parrarr1arr2arr3

arr1 @ 0xA000
----------
| 11    12 |
----------
@A000  @A002
arr2 @ 0xB000
-------------------------
| 13     14     15     16 |
-------------------------
@B000  @B002  @B004  @B006
arr3 @ 0xC000
------------------
| 17     18     19 |
------------------
@C000  @C002  @C004
parr @ 0xE000
------------------------
| @A000   @B000   @C000  |
| (arr1)  (arr2)  (arr3) |
------------------------
@E000   @E004   @E008

还记得我说过的arr2d存储整数吗? 好吧,parr存储指针,所以事情的工作方式有点不同。 首先,您需要使用*运算符取消引用指针,但您使用的是int *parr1,因此parr1[i]会导致指针被错误地解释为整数。 另请注意,每个数组中整数的地址增加 2,而指针的地址 (@E000..@E008)增加4。 换句话说,您将 4 字节指针解释为 2 字节整数,如果您的系统对intint *使用不同的大小,这可能会使您的问题变得更糟。 如果您声明int **parr1 = parr并使用*parr1[i]进行打印,情况会好得多。

但是,这将仅打印每个数组中的第一个值。 这是因为您存储了指向每个数组中第一项的指针(因为数组衰减arr1变成&arr1[0]arr2变成&arr2[0]arr3变成&arr3[0])。 指针没有元素计数,因为它们不是数组,并且由于您存储的是指向单个int的指针而不是指向数组的指针,因此无论如何您都会有效地删除元素计数。 您需要自己跟踪每个数组的元素计数,也许在另一个数组中。 下面是一些修改后的代码,可以解决您的问题:

// Only works for real arrays, not pointers returned by malloc/calloc.
#define ARRAY_COUNT(a) (sizeof (a) / sizeof (a)[0])
int arr1[] = { 11,12 };
int arr2[] = {13, 14, 15, 16};
int arr3[] = { 17, 18, 19 };
int * parr[3] = { arr1,arr2,arr3 };
int **parr1 = (int*)parr;
int counts[] = { ARRAY_COUNT(arr1), ARRAY_COUNT(arr2), ARRAY_COUNT(arr3) };
for (i = 0; i < ARRAY_COUNT(counts); i++)
{
for (j = 0; j < counts[i]; j++)
{
printf("%d ", parr1[i][j]);
}
}

请注意,我使用了两个循环:1 用于计数,1 用于每个数组的内容。如果你真的想要,你可以合并两个循环,但遵循逻辑有点困难:

i = 0;
j = 0;
while (i < ARRAY_COUNT(counts))
{
if (j < counts[i]) {
printf("%d ", parr1[i][j]);
j++;
} else {
i++;
j = 0;
}
}

相关内容

最新更新