在 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[]
;
这里 &arr
和 arr
是内存位置。 (arr+2) 来获取 arr[2] 的地址,但不是 ((&arr)+2) 来做同样的事情。同样,pArr
指向 arr
。当您写 *pArr
时,它与 **arr
相同
参考Please explain the difference了解更多信息
您的数组 arr
和 pArr
包含地址 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
哇!现在看到了吗?不一样。
我正在为 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[]
;
这里 &arr
和 arr
是内存位置。 (arr+2) 来获取 arr[2] 的地址,但不是 ((&arr)+2) 来做同样的事情。同样,pArr
指向 arr
。当您写 *pArr
时,它与 **arr
参考Please explain the difference了解更多信息
您的数组 arr
和 pArr
包含地址 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
哇!现在看到了吗?不一样。