【问题标题】:why i can cast from int** to int* with two-dimensional array, but not with pointer array?为什么我可以使用二维数组从 int** 转换为 int*,但不能使用指针数组?
【发布时间】:2021-07-25 07:57:42
【问题描述】:

为什么我可以用二维数组从 int** 转换为 int*,但不能用指针数组?

例如为什么我这样做:

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

它可以工作并打印:1 2 3 4 5 6 7 8 9

但是当我这样做时:

    int arr1[] = { 11, 12 };
    int arr2[] = { 13, 14, 15, 16 };
    int arr3[] = { 17, 18, 19 };
    int* pArr[3] = { arr1, arr2, arr3 };
    int* pArr1d = (int*)pArr;
    for (i = 0; i < 9; i++)
    {
        printf("%d ", pArr1d[i]);
    }

它不起作用并打印:4192000 4191976 4191956 -858993460 -858993460 17 18 19 -858993460

【问题讨论】:

  • 答案是“未定义的行为”
  • 答案是“因为你确实有一个从地址 &arr2d. &arr2d[0] 和 &arr2d[0][0]) 开始的整数序列,它们都是相同。但您强调没有从地址 &pArr 或 &parr[0] 开始,它们又是相同的”:在那里,您有一系列 地址 .整数序列从 pArr 的元素中 包含 的地址开始:在 pArr[0] 中包含的地址处有 个整数序列。
  • @4386427 虽然正确,但换句话说只是“因为它是错误的”,OP 之前一定已经清楚了。他们的问题是为什么。 (啊,我看到你正在尝试一个答案,这很好。)
  • @Peter-ReinstateMonica 为什么...!出于几个原因...阅读我的答案
  • 您可以在 C 中转换某些东西并不意味着它是一件好事。

标签: c pointers memory multidimensional-array


【解决方案1】:

pArr 是一个由 3 个指向 int 的指针组成的数组。

当在表达式中使用pArr 时,将转换为指向 int 的指针并指向数组的初始元素(即指向 int 的指针)。

所以当你这样做时:

int* pArr1d = (int*)pArr;

您将“指向 int 的指针”转换为“指向 int 的指针”。 类型不兼容。一旦取消引用强制转换的指针,它就是未定义的行为。

当你打印它时,你会犯两个违规行为,这也是未定义的行为。

  1. 您打印指针就像它们是整数一样。

  2. 您越界访问数组。

【讨论】:

  • 强制转换不是未定义的,而是使用强制转换的结果来尝试访问int对象是
  • @M.M 更新措辞
【解决方案2】:

第一种情况不是int **

(int*)pArr 只是暴力转换,而不是某种构造函数。

第一个版本警告int *arr = arr2d;(无演员)

warning: 
initialization of 'int *' from incompatible pointer type 'int (*)[3]'

可能不兼容,但只是 arr2d 有 3 个整数包,但背靠背。类似的,这里不需要内括号(没有间隙):

int arr2d[][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

第一个维度也可以省略。 int [][3] 对应于int (*)[3]


指针版本使用int* pArr1d = pArr; 给出此警告

warning: 
initialization of 'int *' from incompatible pointer type 'int **'

取消引用 (pArr1d[i]) 尝试访问 pArr1d 处的第一个整数 (9) i --- 但那里只有 3 个指针。所以你不能像上面那样简单地把它弄平。 (但现在三行可以有不同的大小)


这行得通!三个数组一个接一个(在栈上):

int arr1[] = { 11, 12 };
int arr2[] = { 13, 14, 15, 16 };
int arr3[] = { 17, 18, 19 };

for (int i = 0; i < 9; i++)
{
    printf("%d ", arr1[i]);
}

调试器说明:

(gdb) p pArr
$13 = {0x7fffffffe87c, 0x7fffffffe890, 0x7fffffffe884}

(gdb) x/12 arr1
0x7fffffffe87c: 11      12      17      18
0x7fffffffe88c: 19      13      14      15
0x7fffffffe89c: 16      -6020   32767   -6000
(gdb) x/12 arr2
0x7fffffffe890: 13      14      15      16
0x7fffffffe8a0: -6020   32767   -6000   32767
0x7fffffffe8b0: -6012   32767   -1022695680     287912987
(gdb) x/12 arr3
0x7fffffffe884: 17      18      19      13
0x7fffffffe894: 14      15      16      -6020
0x7fffffffe8a4: 32767   -6000   32767   -6012

pArr 包含三个地址 - 不是整数。

【讨论】:

  • 您的最后一个示例通过读取数组的末尾导致未定义的行为
  • 是的,感叹号和注释一样短,而且顺序错误。 pArr 做对了:e87c、e890、e884(arr2 和 arr3 交换了“位置”)。好吧,我想用 gdb 进行说明,然后我看到了布局并尝试了……在家里。别。 Arr1 可能是最后一个,首先。而且 arr1-3 “通常”是分开的 - 所以在 pArr 中收集它们的地址是必要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-18
  • 2014-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多