在 C 中使用指针和数组

Working of Pointers and Arrays in C

我正在为 C 使用 Visual Studio 2013。

main.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 的类型为 "pointer to pointer to int",因此是 arr 处的地址。那为什么我用了int **pArr = arr;后不能访问**pArr

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

改变

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了解更多信息

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

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

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


更新:

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

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

一样好

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

所以你应该使用,

int (*pArr)[3] = arr;

而不是

int **ptr = arr;

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

因此,以下代码:

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    [=14=], %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    [=15=], %eax
  78      00
  79 00bb E8000000      call    printf

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