在以下两行中,
char a[5]={1,2,3,4,5};
char *ptr=(char *)(&a+1);
printf("%d",*(ptr-1));
这在屏幕上打印5个。当使用a代替& a时,
char a[5]={1,2,3,4,5};
char *ptr=(char *)(a+1);
printf("%d",*(ptr-1));
这打印1
a和& a都是数组的起始地址。所以为什么会出现这种差异?
另外 char * ptr =& a + 1;
显示警告。
答案 0 :(得分:12)
数组不是指针!请阅读section 6 of the comp.lang.c FAQ以获取更多信息。
让我们先来看看你的第二个案例,因为它更“正常”,而且是惯用的。逐行:
a
元素的数组char
。a
)衰变为指向此上下文中第一个元素的指针。您向其添加1
并将结果分配给ptr
。 ptr
指向2
。不需要演员,但你有一个。1
中减去ptr
,然后取消引用并打印 - 因此您获得了1
。现在,让我们再次逐行解决第一种情况:
a
元素的数组char
。a
的地址,产生char (*)[5]
类型指针。然后将1
添加到此指针 - 由于指针算术,这个新指针会在内存中5
后面的字节处过去。然后进行类型转换(必需,此时)并将此值分配给ptr
。1
中减去ptr
,然后重新引用并打印。 ptr
是一个char *
,因此这个减法只是将指针从“a
末尾的一个”移回一,指向a
的最后一个元素。因此,您获得了5
。最后,char *ptr=&a+1;
给出警告的原因是因为C要求指针类型之间的转换具有显式强制转换。如上所述,&a
的类型为char (*)[5]
,不是 char *
,因此要将该值分配给char *
变量,您需要显性演员。
答案 1 :(得分:10)
由于你似乎对它很陌生,让我用简单的语言向你解释,而不是去做严格的解释。
你知道,对于你上面的程序,a
和&a
将具有相同的数值,我相信这就是你的整个混乱所在。你可能想知道如果它们相同,则在两种情况下,a
后面的下一个地址将通过指针算法进行:
(&a+1) and (a+1)
但事实并非如此!! 数组的基地(此处a
)和数组的地址不一样! a
和&a
可能在数字上相同,但它们不是相同的类型。 a
的类型为char*
,而&a
的类型为char (*)[5]
,即&a
是指向(地址)和大小为5的数组的指针。但是a
如您所知,是数组第一个元素的地址。在数字上,它们与使用下面的 ^ 从插图中看到的相同。 / p>
但是当你递增这两个指针/地址时,即(a+1)
和(&a+1)
,算术完全不同。而在第一种情况下,它会“跳转”到下一个元素的地址。数组,在后一种情况下,跳过5个元素,就像5个元素的数组的大小一样!。现在呢?
1 2 3 4 5
^ // ^ stands at &a
1 2 3 4 5
^ // ^ stands at (&a+1)
1 2 3 4 5
^ //^ stands at a
1 2 3 4 5
^ // ^ stands at (a+1)
以下将给出关于未指定数组绑定的错误,因为未明确指定大小如下所示意味着当遇到类似(& a + 1)之类的程序时,程序将不知道要“跳转”到多少元素。
char a[]={1,2,3,4,5};
char *ptr=(char *)(&a+1); //(&a+1) gives error as array size not specified.
现在到了将指针/地址递减为(ptr-1)
的部分。在第一种情况下,在你来到减量部分之前,你应该知道它上面的语句中会发生什么char*
:
char *ptr=(char *)(&a+1);
这里发生的是,您“剥离”type
(&a+1)
char (*)[5]
类型char*
,现在将其转换为类型为a
的{{1}} printf()
的那个,即数组的基地址。(再次注意数组的基地址和数组的地址之间的差异。)之后上面语句中的强制转换和赋值,后跟ptr
中的递减,5
现在给出了数组最后一个元素后面的内存位置,即 1 2 3 4 5
^ // ^ stands at location of 5, so *ptr gives 5
。
ptr
因此,当您将指针*(ptr-1)
递减为5
后取消引用时,它会按预期打印1
的值。
现在最后,将其与第二种情况进行对比,其中a
被打印出来。看看我使用符号 ^ 给出的插图。当您将a+1
增加为2
时,它指向数组的第二个元素,即ptr
,并且您已将此地址分配给ptr
。因此,当您减少(ptr-1)
时{1}}它为1
,它跳回一个元素,现在指向数组的第一个元素,即ptr
。因此,在第二种情况下取消引用1
会给 1 2 3 4 5
^ // ^ stands at address of 1, so *ptr gives 1
}。
{{1}}
希望这一切都清楚。
答案 2 :(得分:2)
不同之处在于您获得的指针类型:
a
本身表示指向数组初始元素的指针。当以这种方式解释时,例如,在表达式a+1
中,指针被视为指向单个字符。&a
时,指针指向一个包含五个字符的数组。向指针添加整数时,指针移动的字节数由指针指向的对象指针的类型决定。如果指针指向char
,则添加N
会使指针前进N
个字节。如果指针指向一个包含五个char
的数组,则添加N
会使指针前进5*N
个字节。
这正是你得到的差异:你的第一个例子将指针推进到一个超过数组末尾的元素(这是合法的),然后将其移回最后一个元素。另一方面,您的第二个示例将指针前进到第二个元素,然后将其移回指向数组的初始元素。
答案 3 :(得分:0)
数组“衰减”成指向第一个元素的指针。因此,获取a的地址会给出一个指向5个字符数组的指针,就像声明char[][5]
一样。并且递增此指针前进到char[][5]
数组的下一个元素 - 一次是5个字符。这与递增从char[5]
数组衰减的指针(即一次只有一个字符)不同。
答案 4 :(得分:0)
你遇到的是指针运算的微妙之处。
编译器将“a”视为指向char的指针 - 一个大小为1字节的实体。向此处添加1会产生一个指针,该指针会增加实体的大小(即1)。
编译器将“& a”视为指向chars数组的指针 - 一个大小为5个字节的实体。向此处添加1会产生一个指针,该指针会增加实体的大小(即5)。
这是指针算法的工作原理。将一个指针添加到指针会使其增加一个指针类型的大小。
当然,有趣的是,当评估“a”或“& a”的价值时,在解除引用时,它们都会评估到同一地址。这就是你看到你所做的价值的原因。