我刚看到这个代码片段Q4,并想知道我是否理解正确.
#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;
}
Run Code Online (Sandbox Code Playgroud)
这是我的解释:
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 ptr.ptr指向内存中不存在的第二个数组.所以,ptr - 1应该指向第一个数组的最后一个元素(a[4]).
Here &a will be a double pointer.
Run Code Online (Sandbox Code Playgroud)
不.它是指向数组的指针.在这个例子中int (*)[5].将C指针指向歧义消除的指针数组/数组
因此,当您将指针递增到数组时,它将穿过数组并指向不存在的位置.
在此示例中,它被分配给整数指针.所以当int指针递减时,它将指向先前的sizeof(int)字节.这样打印出5.
您的陈述基本上是正确的,您可能比大多数专业人士更理解它。但既然你正在寻求批评,这里是长答案。C 中的数组和指针是不同的类型,这是 C 中最微妙的细节之一。我记得我最喜欢的一位教授曾经说过,后来创造该语言的人后悔将其做得如此微妙且经常令人困惑。
在许多情况下,类型的数组确实如此,并且可以以相同的方式处理指向类型的指针。它们的值都等于它们的地址,但它们是真正不同的类型。
当您获取数组的地址时&a,您就有了一个指向数组的指针。当你说(a + 1)你有一个指向 int 的指针时,当你只是说a你有一个数组(而不是指针)时。a[1]与打字完全相同*(a + 1),事实上你可以打字1[a],它会与前两个完全相同。当您将数组传递给函数时,您实际上并没有传递数组,而是传递了一个指针,void Fn(int b[])并且void Fn(int *b)两者都是完全相同的函数签名,如果您sizeof b在函数内进行获取,则在这两种情况下您都会获得指针的大小。
指针算术很棘手,它总是按其指向的对象的大小(以字节为单位)进行偏移。每当您使用运算符的地址时,您都会获得一个指向您应用它的类型的指针。
因此,对于上面的示例中发生的情况:
&a是一个指向数组的指针,因此当您向其添加 1 时,它会偏移该数组的大小(5 * sizeof(int))。int*,转换保留了指针的值,但现在它的类型是指向 int 的指针,然后您将其存储在一个指向 intptr类型的指针变量中。a是一个数组,而不是一个指针。因此,当您说a + 1将加法运算符应用于数组而不是指针时;这会产生一个指向数组中存储的类型的第一个元素的指针int。取消引用它*会得到指向的int 。ptr是一个指向 int 的指针,它指向数组末尾之后的一个。(顺便说一句,将 1 指向数组末尾是合法的,只是取消引用该指针是不合法的)当您从中减去 1 时,您最终会得到一个指向数组中最后一个 int 的指针,您可以取消引用。(你对可视化的解释int b[1][5] = {1, 2, 3, 4, 5};是我以前从未听过的,虽然我不能诚实地说这在技术上是否正确,但我会说这就是它的工作原理,我认为这是一个很好的思考方式;我从现在开始,我可能会在脑海中这样做。)类型在 C 中会变得非常棘手,在 C++ 中也是如此。最好的还在后头。