【问题标题】:Working of Pointers and Arrays in CC中指针和数组的工作
【发布时间】:2015-01-28 04:18:45
【问题描述】:

我正在使用 Visual Studio 2013 for C。

ma​​in.c

int main()                          
{
    int arr[2][3] = { { 10, 20, 30 }, { 15, 25, 35 } };
    int **pArr = arr;

    printf("\n\n----SIZE OF----");
    printf("\nSizeOf(arr) = %d", sizeof(arr));
    printf("\nSizeOf(pArr) = %d", sizeof(pArr));

    printf("\n\n----DEREFERENCING THE ADDRESS----");
    printf("\narr = %d   *arr = %d   **arr = %d", arr, *arr, **arr);
    printf("\npArr = %d   *pArr = %d   **pArr = %d", pArr, *pArr, **pArr);    //EXCEPTION thrown here

    return 0;
}

查看此图像以了解引发的异常:

在使用断点时,会显示指针 pArr 和数组 arr 中的值:

但是当我从代码的倒数第二行中删除**pArr 部分时,程序输出如下:

----SIZE OF----
SizeOf(arr) = 24
SizeOf(pArr) = 4

----DEREFERENCING THE ADDRESS----
arr = 5569760   *arr = 5569760   **arr = 10
pArr = 5569760   *pArr = 10

指针pArr 是“指向int 指针的指针”类型,因此是arr 的地址。那为什么我使用int **pArr = arr;后无法访问**pArr

【问题讨论】:

  • Visual Studio 是一个 C++ 编译器,顺便说一下。对于语言的这个有限子集,这无关紧要,但无论如何你都应该意识到这一点。
  • int **pArr = arr 是如何编译的?二维数组不会衰减为指针。
  • @paxdiablo 感谢您指出这一点:)
  • @jamesdlin 我也期待一个错误,但坦率地说,它确实编译了。而且我不知道它为什么会这样

标签: c arrays pointers visual-studio-2013


【解决方案1】:

如果您使用一维数组,arr 它是指向数组 arr 的基值的指针。那就是和&arr[0]一样好。

当您使用二维数组时,arr 表示指向基值的指针,即arr[0],即(*arr)[3]

所以你应该使用,

int (*pArr)[3] = arr;

代替

int **ptr = arr;

【讨论】:

  • 使用int **pArr = arr;,其中arr是一个二维数组,是不是不允许?
  • 不,因为你指的是哪里?指向数组地址的指针。这意味着 &arr?不,我们想指向 arr[0]。
  • 我必须使用int **pArr = &arr; 而不是int **pArr = arr;,这是您的建议吗?
  • @beginner 不,我不是在暗示。首先什么是arr,在指针场景中,它是指向一维数组的指针。所以int(*ptr)[NUMBER],这里可以指向NUMBER元素。
  • @beginner 是的。但是什么时候int **ptr 是可能的?当你持有 int 指针的地址时。
【解决方案2】:

您收到错误消息,因为 *pArr 产生二维数组的第一个元素,即 10。
然后,通过在最后一个 printf 中写入 **pArr,您尝试取消引用十六进制 0xa 中的 10,从而显示错误消息。

【讨论】:

  • @Santosh 我知道这一点。但是,我想知道我什么时候做了int **pArr = arr;,而不是pArr 的值应该等于arr 的地址(大小和类型都是pointer to pointer to int)。 pArr 的值与 arr 相同,但类型为 pointer to int。为什么?
  • @beginner 这就是你要找的东西吗:int (*pArr)[3] = arr; ?
  • 这是Pointer to Arrays。我不这么认为。
  • @beginner 那么这个怎么样:int (*pArr)[2][3] = &arr;?如果不是这样,我不知道你想要什么......
  • @beginner 因为它是一个二维数组
【解决方案3】:

您的数组arrpArr 包含地址0x60f914。这里的内存包含6个整数是你初始化的。

当您执行*pArr 时,它会给您包含地址0x60f91410。当您执行**pArr 时,它会给您包含地址10

所以结论是二维数组不完全排列为_pointer-to-pointer-to-int`。


更新:

这类似于类型转换。如果您有一些内存地址(例如void *),您可以将其转换为任何内容(int *my_struct *)并访问它的包含。但这并不能保证该内存的结构和包含对于您尝试进行的类型转换是有效的。

【讨论】:

  • 那么,你的意思是当我使用int **pArr = arr; 时,pArr 的地址与arr 相同,但类型为pointer to int 而不是pointer to pointer to int?是真的,但是为什么会这样,这就是我想知道的!
  • 好的,我也尝试过类型转换。我做了int **pArr = (int **)arr;,但输出和以前一样。如果我们使用int **pArr = (int **)arr;,那么根据您的pArr 中的值的类型是什么
【解决方案4】:

改变

int **pArr = arr;

int *Arr = arr;
int **pArr = &Arr;

由于pArr 是指向int 类型指针的指针,因此您试图将其仅指向一个指针。 所以当试图取消引用一个未知的内存位置时会导致Segmentation fault

编辑:
考虑int arr[]
这里&arrarr 是内存位置。 (arr+2) 来获取 arr[2] 的地址,而不是 ((&arr)+2) 来做同样的事情。同样,pArr 指向arr。当你写*pArr时,它与**arr相同

更多信息请参考Please explain the difference

【讨论】:

  • 请看第二个答案的评论!
  • 你能看出*arr*pArr打印出来的值有什么不同吗?前者将打印地址,而后者将取消引用指针并打印值。
  • 当然,我可以看出区别。但我要问的是,在做int **pArr = arr; 时,pArr 实际上是持有*arr 而不是arr。为什么?
【解决方案5】:

多维数组是一种编译器构造。它是一个连续的内存区域,其位置被编译器抽象出来。计算机的物理内存布局是纯一维的(一个巨大的数组)。

所以,下面的代码:

int **pArr = arr;

它在以下程序集中翻译:

   6:hhh1.c         ****     int **pArr = arr;
  41                    .loc 1 6 0
  42 0032 488D45E0      leaq    -32(%rbp), %rax
  43 0036 488945D8      movq    %rax, -40(%rbp)

以及以下(正确)代码:

 int (*pArr)[3] = arr;

在以下程序集中翻译:

   6:hhh2.c         ****     int (*pArr)[3] = arr;
  41                    .loc 1 6 0
  42 0032 488D45E0      leaq    -32(%rbp), %rax
  43 0036 488945D8      movq    %rax, -40(%rbp)

惊讶于它的翻译完全一样!?这是因为第二个代码改变了编译器稍后在代码中获取内存的方式。

让我们看看最后一个 printf 或你的程序集,第一个是错误代码,第二个是正确代码:

  14:hhh1.c         ****     printf("\npArr = %d   *pArr = %d **pArr = %d", pArr, *pArr, **pArr);    //EXCEPTION thrown here
  71                    .loc 1 14 0
  72 00a0 488B45D8      movq    -40(%rbp), %rax
  73 00a4 488B00        movq    (%rax), %rax
  74 00a7 8B08          movl    (%rax), %ecx
  75 00a9 488B45D8      movq    -40(%rbp), %rax
  76 00ad 488B10        movq    (%rax), %rdx
  77 00b0 488B45D8      movq    -40(%rbp), %rax
  78 00b4 4889C6        movq    %rax, %rsi
  79 00b7 BF000000      movl    $.LC5, %edi
  79      00
  80 00bc B8000000      movl    $0, %eax
  80      00
  81 00c1 E8000000      call    printf

现在,使用第二种样式生成:

  14:hhh2.c         ****     printf("\npArr = %d   *pArr = %d **pArr = %d", pArr, *pArr, **pArr);    //EXCEPTION thrown here
  71                    .loc 1 14 0
  72 00a0 488B45D8      movq    -40(%rbp), %rax
  73 00a4 8B08          movl    (%rax), %ecx
  74 00a6 488B55D8      movq    -40(%rbp), %rdx
  75 00aa 488B45D8      movq    -40(%rbp), %rax
  76 00ae 4889C6        movq    %rax, %rsi
  77 00b1 BF000000      movl    $.LC5, %edi
  77      00
  78 00b6 B8000000      movl    $0, %eax
  78      00
  79 00bb E8000000      call    printf

哇!现在看到了吗?不一样。

【讨论】:

  • 现在这是一个非常令人印象深刻的演示文稿,但坦率地说我不明白assembly。至少我知道single dimensional 数组被编译器处理与multidimensional 数组完全不同
  • 还有一件事,如果有办法,我应该如何将二维数组的地址分配给int **somePointer;。当然Pointer to Arrays 是一种选择,但还有其他选择吗?
  • 没有。如果你真的想使用int **pArr = arr;,你唯一能做的就是改变printf,提示编译器指针的内容是内存地址,就像printf("\npArr = %d *pArr = %d **pArr = %d", pArr, *pArr, &**pArr);
  • 所以当我们执行int **pArr = arr;int **pArr = (int **)arr; 时,arr 可以是任何multidimensional array,而不是pArr 将始终持有&arr[][]&arr[][][],具体取决于尺寸和*pArr 将显示arr[][]arr[][][],同样取决于数组的尺寸。对吗?
  • 只是修复了我上面提出的答案:我将多维数组与不相关的东西混淆了。多维数组不是指针数组,它连续存储在内存中,位置被编译器抽象出来。指向指针的指针不起作用的原因是,顾名思义,它是必须指向另一个内存地址的指针(内存地址)。使用指向数组指针的指针,您可以获得第一个元素的内存地址和元素本身,而不是另一个内存地址。取消引用它没有任何意义。
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 2013-03-10
  • 1970-01-01
  • 2019-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多