我试图掌握C中的指针和数组。现在,我坚持尝试弄清楚C编译器如何为二维数组中的元素分配内存。这是我的示例代码:
#include <stdio.h>
int main(void)
{
int ar[2][2] = { {1, 2}, {3, 4} };
printf("sizeof(int) = %u\n-----\n", sizeof(int));
printf("ar = %p\n", ar);
printf("ar + 1 = %p\n", ar + 1);
printf("&ar = %p\n", &ar);
printf("&ar + 1 = %p\n\n", &ar + 1);
printf("sizeof(ar) = %u\n-----\n", sizeof(ar));
printf("ar[0] = %p\n", ar[0]);
printf("ar[0] + 1 = %p\n", ar[0] + 1);
printf("&ar[0] = %p\n", &ar[0]);
printf("&ar[0] + 1 = %p\n\n", &ar[0] + 1);
printf("sizeof(ar[0]) = %u\n-----\n", sizeof(ar[0]));
printf("ar[1] = %p\n", ar[1]);
printf("ar[1] + 1 = %p\n", ar[1] + 1);
printf("&ar[1] = %p\n", &ar[1]);
printf("&ar[1] + 1 = %p\n\n", &ar[1] + 1);
printf("sizeof(ar[1]) = %u\n-----\n", sizeof(ar[1]));
printf("&ar[0][0] = %p\n", &ar[0][0]);
printf("&ar[0][0] + 1 = %p\n", &ar[0][0] + 1);
printf("&ar[1][0] = %p\n", &ar[1][0]);
printf("&ar[1][0] + 1 = %p\n\n", &ar[1][0] + 1);
printf("sizeof(ar[0][0]) = %u\n-----\n", sizeof(ar[0][0]));
return 0;
}
我在系统上得到的输出是:
sizeof(int) = 4
-----
ar = 0061FF20
ar + 1 = 0061FF28
&ar = 0061FF20
&ar + 1 = 0061FF30
sizeof(ar) = 16
-----
ar[0] = 0061FF20
ar[0] + 1 = 0061FF24
&ar[0] = 0061FF20
&ar[0] + 1 = 0061FF28
sizeof(ar[0]) = 8
-----
ar[1] = 0061FF28
ar[1] + 1 = 0061FF2C
&ar[1] = 0061FF28
&ar[1] + 1 = 0061FF30
sizeof(ar[1]) = 8
-----
&ar[0][0] = 0061FF20
&ar[0][0] + 1 = 0061FF24
&ar[1][0] = 0061FF28
&ar[1][0] + 1 = 0061FF2C
sizeof(ar[0][0]) = 4
-----
我了解为什么ar
的大小为16个字节;它应该能够容纳4个int
,在我的系统上为4x4 = 16个字节。我猜这也是为什么&ar + 1
和&ar
之间的字节差为(hex)30-20 = 16的原因。
我不明白的是,为什么ar + 1
和ar
之间的区别只有8个字节。这意味着该数组只能容纳2个int
或4个字节。
我在理解ar[0]
和ar[1]
时遇到了同样的问题。
ar + 1
和&ar + 1
不会产生相同的结果吗?
答案 0 :(得分:2)
在您的情况下,ar
是一个数组。因此,首先要记住
ar
是int [2][2]
的类型,它是int
s的数组的数组&ar
的类型为int (*)[2][2]
,即指向2个int
s数组的指针。也就是说,在某些情况下,数组类型会衰减到指向数组第一个元素的指针。 注意
因此,如果表达式类似
ar + 1
与
相同(&(ar[0])) + 1;
,它基本上指向ar[1]
。
我不明白的是为什么
ar + 1
和ar
之间的差异只有8个字节
因此,这里的“差异” 由ar[0]
元素所占据的大小(即2 ints
)组成,在您的平台中, 8个字节。结果检出。
另一方面,对于类似
的表达式&ar + 1;
它对指针类型进行操作(如前所述),并指向数组中最后一个元素之后的位置。因此,差异是对于每个2个int
的2个数组,因此(2 * 2 * 4)= 16个字节。
注意:
引用C11
,第6.3.2.1章
除非它是
sizeof
运算符,_Alignof
运算符或 一元&
运算符,或者是用于初始化数组的字符串文字,该表达式具有 类型“类型数组” 转换为类型为“类型指针” 的表达式 数组对象的初始元素,不是左值。 [....]
答案 1 :(得分:2)
ar
,在表达式中使用时,“衰减”到第一个元素的指针。在这种情况下,arr + 1
对类型为int (*)[2]
的指针进行算术运算。指向大小为8个字节的int [2]
。
此“阵列衰减”规则在C17 6.3.2.1§3中指定:
除非它是sizeof运算符的操作数或一元&运算符,或者是 用于初始化数组的字符串文字,其类型为“数组类型”的表达式为 转换为类型为“要输入的指针”的表达式,该表达式指向的初始元素 数组对象,不是左值
因此,当您键入&ar
时,会从数组衰减规则中得到特殊的例外,不会发生衰减,但实际上会得到预期的int (*)[2][2]
。因此&ar + 1
给出16个字节。
答案 2 :(得分:0)
所以:
sizeof(int) == 4
以下内容:
int ar[2][2];
是2D数组。
我们知道a[b]
等于*(a + b)
。并且&*
转换为空。
所以:
&ar[1]
等于
(ar + 1)
此处ar
“衰减”或“应调整”(读作:神奇地转换)成指针。指向两个int元素的数组的指针,即。 int (*)[2]
。因此,它不是int *
也不是int[2][2]
指针,而是int (*)[2]
。我们知道
sizeof(ar) == sizeof(int[2][2]) == sizeof(int[2]) * 2 == sizeof(int) * 2 * 2
sizeof(*ar) == sizeof(*(int(*)[2]) == sizeof(int[2]) == sizeof(int) * 2
sizeof(**ar) == sizeof(**(*(int(*)[2])) == sizeof(*(int[2])) == sizeof(*(int*)) == sizeof(int)
所以
(ar + 1)
等于(等于值):
(uintptr_t)ar + sizeof(*ar) * 1 ==
(uintptr_t)ar + sizeof(*(int(*)[2])) * 1) ==
(uintptr_t)ar + sizeof(int[2]) * 1) ==
(uintptr_t)ar + sizeof(int) * 2 * 1)
即。会使ar
指针值增加2 * sizeof(int)
。
我不明白的是为什么ar + 1和ar之间的区别只有8个字节。
ar + 1
等于
(uintptr_t)ar + sizeof(*ar) + 1
由于ar
是int[2][2]
,所以*ar
是int[2]
,所以sizeof(*ar) = sizeof(int) * 2
。
因此ar + 1
等于
(uintptr_t)ar + sizeof(int) * 2 * 1
所以(ar + 1) - ar
等于
((uintptr_t)ar + sizeof(int[2]) * 1) - (uintrpt_t)ar ==
sizeof(int[2]) ==
sizeof(int) * 2
ar + 1和&ar + 1是否会产生相同的结果?
对于像int array[2];
这样的数组,array
指针值等于&array
指针值。这是C的怪癖,将address-of运算符应用于数组会导致指向同一内存的数组指针。到array
的类型为int[2][2]
,但是&array
的类型为int(*)[2][2]
,即。它是指向2d数组的指针。
由于类型更改,指针算法也会更改。 typeof(ar)
衰减到typeof(int(*)[2])
,所以ar + 1
等于
`(uintptr_t)ar + sizeof(int[2]) * 1`.
但是由于typeof(&ar) == typeof(int(*)[2][2])
等于&ar + 1
`(uintrpt_t)ar + sizeof(int[2][2]) * 1`.
由于sizeof(int[2][2])
等于sizeof(int) * 2 * 2
,因此增加指针时指针值的差异。
我认为您无法掌握2d数组的情况,“第一”级别是两个元素的1d数组,而第二个级别是int。因此typeof(ar[0])
是由两个int元素组成的数组。
您的代码包含UB,因为%p
修饰符应仅与void*
指针一起使用。最好记住(或至少知道应该这么做)printf("%p", (void*)&ar[1][0] + 1);
投射指针。