我刚刚看到这个代码片段Q4 here,想知道我是否理解正确。
#include <stdio.h>
int main(void)
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int*)(&a + 1);
printf("%d %d\n", *(a + 1), *(ptr - 1));
return 0;
}
我的解释是:
int a[5] = { 1, 2, 3, 4, 5 };
=>a
指向数组的第一个元素。换句话说:a
包含数组第一个元素的地址。int *ptr = (int*)(&a + 1);
=>这里的&a
将是一个双指针,指向整个数组。我把它想象成这样:int b[1][5] = {1, 2, 3, 4, 5};
,这里b
指向一行2d数组。&a + 1
应该指向内存中的下一个整数数组(不存在)[类型类似,b + 1
指向具有1行的2D数组的第二行(不存在)行。我们将其转换为int *
,因此这可能指向内存中下一个数组(不存在)的第一个元素。这个很简单。它只指向数组的第二个元素。
*(a + 1)
=>这个很棘手,我的解释可能有缺陷。由于*(ptr - 1)
是一个ptr
,它应该指向int *
所指的int之前的int。ptr
指向内存中不存在的第二个数组。因此,ptr-1可能应该指向第一个数组的最后一个元素(ptr
)。 最佳答案
你的陈述基本上是正确的,而且你可能比大多数专业人士更理解它。但既然你在寻求批评,这里有一个很长的答案。C语言中的数组和指针是不同的类型,这是C语言中最微妙的细节之一。我记得我最喜欢的一位教授曾经说过,让C语言成为后者的人后悔让这一点如此微妙,而且常常令人困惑。
在许多情况下,类型的数组和指向类型的指针可以用相同的方式处理。它们的值都等于它们的地址,但它们确实是不同的类型。
获取数组地址时,有一个指向数组的指针。当你说&a
时,你有一个指向int的指针,当你说(a + 1)
时,你有一个数组(不是指针)。a
与键入a[1]
完全相同,实际上您可以键入*(a + 1)
,它将与前两个完全相同。将数组传递给函数时,实际上并不是传递数组,而是传递指针1[a]
和void Fn(int b[])
都是完全相同的函数签名,如果在函数中取void Fn(int *b)
,在这两种情况下都会得到指针的大小。
指针算法很复杂,它总是以它指向的对象的大小(字节)为单位进行偏移。每当使用address of运算符时,都会得到一个指向应用它的类型的指针。
所以对于上面例子中发生的事情:sizeof b
是指向数组的指针,因此当您向它添加一个指针时,它将被该数组的大小所偏移。
当强制转换为&a
时,强制转换将保留指针的值,但现在它的类型是pointer to int,然后将其存储在(5 * sizeof(int))
中,后者是pointer to int类型的变量。int*
是数组,而不是指针。所以当你说ptr
时,你把加法运算符应用到一个数组中,而不是一个指针;这会产生一个指向数组中存储的类型的第一个元素a
后面的指针。用a + 1
取消对它的引用将提供指向的int。int
是指向int的指针,它指向数组末尾后面的一个。(将一个指针指向数组的末尾是合法的,取消对该指针的引用是不合法的)当从中减去1时,将得到指向数组中最后一个可以取消引用的int的指针。(你对视觉化的解释是我以前从未听过的,虽然我不能诚实地说这在技术上是否正确,但我会说这就是它的工作原理,我认为这是一种很好的思考方式;从现在开始,我可能会在脑海中这样做。)
在C中,类型将变得非常棘手,并且在C++中也会变得非常棘手。最好的还在后头。