int main()
{
char arr[5][7][6];
char (*p)[5][7][6] = &arr;
printf("%d\n", (&arr + 1) - &arr);
printf("%d\n", (char *)(&arr + 1) - (char *)&arr);
printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);
printf("%d\n", (unsigned)(p + 1) - (unsigned)p);
return 0;
}
当我运行上面的代码时,我得到以下输出:
1
210
42
210
为什么每种情况下输出都不是1
?
答案 0 :(得分:6)
好吧,如果我想拆分头发:首先,代码在整个printf()
语句中调用整个地方的未定义行为。两个指针的区别在于ptrdiff_t
,为此,正确的转化说明符为%td
,而不是%d
。
其余的只是猜测。假设您的系统是合理的,并且数字指针值&arr
始终是相同的,无论它转换为何种类型。
现在,根据指针算法的规则,(&arr + 1) - &arr
是1。 (两个指针之间的实际差异是210 * sizeof(int)
字节,但这不是学校数学而是指针算术,这就是为什么结果以大小sizeof(T)
为单位给出,其中T
是指针的基本类型。)
然后(char *)(&arr + 1) - (char *)&arr
将指针投射到char *
,由于char
的大小为1,这将打印差异以字节为单位; 你是在这里有效地欺骗/滥用指针算术。
此外:printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr)
正在减去两个类型为int (*)[7][6]
的指针。这就是arr
衰变的原因。当然,7 * 6 = 42,因此arr + 1
和arr
之间的大小差异为42个元素。
p
不是指向数组第一个元素的指针,而是指向数组本身的指针。其类型正确表示为int (*)[5][6][7]
。现在,如果你使用那个类型来打印差异,但是你不让编译器通过欺骗它来进行划分,指针只是unsigned
,那么你将获得{{1这是210。
答案 1 :(得分:5)
在(&arr + 1) - &arr
:
&arr
是包含6个char
的7个数组的5个数组的数组的地址。如果我们有一个这些对象的数组而不是一个对象的数组,那么添加一个会产生5个7 char
数组的5个数组的 next 数组的地址。减去原始地址&arr
会产生两个地址之间的差异。根据C标准,此差异表示为两个地址之间的元素数,其中元素类型是指向的对象的类型。由于该类型是由7个6 char
数组组成的5个数组的数组,因此两个地址之间的距离是一个元素。换句话说,从&arr
到(&arr + 1)
的距离为一个数组,包含5个7个6 char
数组的数组。
在(char *)(&arr + 1) - (char *)&arr
:
&arr + 1
再次指向7个6 char
数组的5个数组的下一个数组的指针。当它转换为char *
时,结果是指向下一个数组的第一个字节的指针。类似地,(char *)&arr
是指向第一个数组的第一个字节的指针。然后减去两个指针会产生元素之间的差异。由于这些指针是指向char
的指针,因此差异产生为char
的数量。所以区别在于7个阵列的阵列中的字节数,其中7个阵列为6 char
,即5•7•6 char
或210 char
。
在(unsigned)(arr + 1) - (unsigned)arr
:
由于arr
未与&
(或sizeof
或其他例外情况)一起使用,因此会自动转换为包含7个6个{{1}数组的5个数组的数组指向第一个元素的指针。因此,它是一个指向7个6 char
数组的数组的指针。然后char
是指向7个6 arr + 1
数组的下一个数组的指针。当此地址转换为char
时,您正在使用的C实现中的结果实际上是对象的内存地址。 (这很常见,但C标准并不保证,当地址为64位但unsigned
为32位时,肯定会中断。)同样,unsigned
是第一个对象的地址。减去地址后,结果就是它们之间的距离(以字节为单位)。因此,差异是7个6 (unsigned)arr
数组的数组中的字节数,即7•6字节或42字节。注意这种情况下的关键区别:char
是一个指向由7个6 &arr
数组组成的5个数组的数组的指针,但是char
是一个指向7个数组6的数组的指针arr
。
在char
:
(unsigned)(p + 1) - (unsigned)p
是一个指向由7个6 p
数组组成的5个数组的数组的指针。然后char
是指向下一个数组的指针。转换为p+1
的行为如上所述。减去产生差异的字节,所以它是由7个6 unsigned
数组组成的5个数组的数组的大小,所以它又是210个字节。
暂且不说:
char
的类型为(&arr + 1) - &arr
,应使用ptrdiff_t
打印。
%td
的类型为(char *)(&arr + 1) - (char *)&arr
,应使用ptrdiff_t
打印。
%td
的类型为(unsigned)(arr + 1) - (unsigned)arr
,应使用unsigned int
打印。
%u
的类型为(unsigned)(p + 1) - (unsigned)p
,应使用unsigned int
打印。
答案 2 :(得分:5)
注意&arr
是完整的三维char数组的地址,而arr
指向第一个元素,即二维char数组。如下图所示:
0xbf8ce2c6
+------------------+ ◄-- arr = 0xbf8ce2c6
| 0xbf8ce2f0 |
| +------------------+ ◄-- arr + 1 = 0xbf8ce2f0
| | 0xbf8ce31a | |
| | +------------------+ ◄-- arr + 2 = 0xbf8ce31a
| | 0xbf8ce344 | | |
| | | +------------------+ ◄-- arr + 3 = 0xbf8ce344
| | 0xbf8ce36e | | | |
| | | | +------------------+ ◄-- arr + 4 = 0xbf8ce36e
| | | | | | | | | |
+---|---|---|--|---+ | | | | Each are 7*6, 2-Dimensional
| | | | | | | | Consists Of 42 bytes
+---|---|--|-------+ | | |
| | | | | |
+---|--|-----------+ | |
| | | |
+--|---------------+ |
| |
+------------------+
The diagram show:
1. How a 3-dimensional can be interpreted as series of 2-dimensional arrays
2. Here (arr + i) points to a 2-D array
3. Notice difference between: (arr + i + 1) - (arr + i) = 0x2a = 42, where i = [0, 4]
&arr
的类型是char(*)[5][7][6]
,它是维度[5][7][6]
的字符3D数组的地址。
&arr
和&arr + 1
之间的价值差异为5 * 7 * 6 * sizeof(char)
= 210
。
因为char[5][7][6]
的尺寸为5 * 7 * 6 * sizeof(char)
在您的代码&arr
中指向3-D数组和&arry + 1
下一个3-D数组(我们的代码中不存在)。
在codepade处查看此工作代码:
int main()
{
char arr[5][7][6];
printf(" &arr : %p", &arr);
printf(" &arr+1: %p", &arr + 1);
return 0;
}
输出:
&arr : 0xbf5dd7de
&arr+1: 0xbf5dd8b0
(&arr + 1) - (&arr)
= 0xbf5dd8b0 - 0xbf5dd7de
= 0xd2
= 210
之间的差异。
在你的第二张照片中:
printf("%d\n", (char *)(&arr + 1) - (char *)&arr);
您将char(*)[5][7][6]
类型的地址强制转换为普通(char*)
,并且因为sizeof char[5][7][6]
为210
,所以两个地址都是210远。 (记住sizeof(char) == 1
)。这就是输出的原因:210
正如我在第一句话中所说,arr
是第一个元素的地址,它是二维chars数组。 arr
的类型为char(*)[7][6]
。现在有一个元素(大小为二维的数组6 * 7 * sizeof(char) = 42
)
(注意:您可以将三维数组视为一维数组,其中每个元素都是一个二维数组)。
在你的第三张照片中:
printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);
您将类型转换为无符号值(但不转换为地址/指针类型)。 arr + 1
和arr
之间的差异为42 * sizeof(char)
= 42
(等于char[7][6]
的大小)。因此printf语句输出:42
。
注意:您应该阅读sizeof (int) == sizeof (void*)?,因为您要将地址转换为值。并且此转换尚未完全定义。 (我的解释是你的输出和我给出的输出)。
有关进一步说明,请查看codepade:
下方的工作代码int main()
{
char arr[5][7][6];
printf(" arr : %p\n", arr);
printf(" arr+1: %p", arr + 1);
return 0;
}
输出是:
arr : 0xbf48367e
arr+1: 0xbf4836a8
区分(arr + 1) - (arr)
= 0xbf4836a8
- 0xbf48367e
= 0x2a
= 42
。
上次打印:
printf("%d\n", (unsigned)(p + 1) - (unsigned)p);
只需区分&arr+1
和&arr
= 210
(类似于第二个printf),因为p
是指向3-D char数组的指针(= {{1} })。并且你将它类型化为值类型(不是指针类型)。
此外,(只是为了理解目的添加,我猜读者会发现它有用),
让我们使用sizeof运算符学习&arr
和arr
之间的另一个区别,这将有助于您更深入地理解概念。首先阅读:sizeof
Operator
将
&arr
运算符应用于数组标识符时,结果是整个数组的大小而不是 由数组标识符表示的指针大小。
在codepade处查看此工作代码:
sizeof
其输出:
int main()
{
char arr[5][7][6];
printf(" Sizeof(&arr) : %lu and value &arr: %p\n", sizeof(&arr), &arr);
printf(" Sizeof(arr) : %lu and value arr : %p\n", sizeof(arr), arr);
printf(" Sizeof(arr[0]): %lu and value a[0]: %p\n",sizeof(arr[0]), arr[0]);
return 0;
}
这里Sizeof(&arr) : 4 and value &arr: 0xbf4d9eda
Sizeof(arr) : 210 and value arr : 0xbf4d9eda
Sizeof(arr[0]): 42 and value a[0]: 0xbf4d9eda
只是一个地址,系统地址是四字节,这是完整的三维字符数组的地址。
&arr
是三维数组的名称,arr
运算符给出的数组总大小为sizeof
。
正如我在图中所示210 = 5 * 7 * 6 * sizeof(char)
指向的第一个元素是二维数组。因为arr
= arr
。现在在(arr + 0)
使用*
取消引用运算符会在地址(arr + 0)
处给出值。
*(arr + 0) = arr[0]
给出sizeof(arr[0])
= 42
。并且这在概念上证明了三维阵列注意到了二维阵列的阵列。 因为在我的回答中,我多次写道:“7 * 6 * sizeof(char)
的大小是char[5][7][6]
。”所以我在@ {{3下面添加了一个有趣的代码}}:
5 * 7 * 6 * sizeof(char)
输出:
int main(){
printf(" Char : %lu \n", sizeof(char));
printf(" Char[5] : %lu \n", sizeof(char[6]));
printf(" Char[5][7] : %lu \n", sizeof(char[7][6]));
printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6]));
return 1;
}