数组a和&a的起始地址
在以下两行中,
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;
显示警告。
既然你看起来很陌生,那么让我用简单的语言向你解释,而不是去做严谨的解释。
你看,对于你上面的程序, a
和&a
将具有相同的数值 ,我相信这就是你的整个混乱所在。你可能想知道如果它们是相同的,下面的内容应该在两种情况下给出下一个地址 ,通过指针算术:
(&a+1) and (a+1)
但事实并非如此! 数组的基址 (此处为a
)和数组的地址不相同! a
和&a
可能在数字上相同,但它们不是同一类型 。 a
的类型为char*
而&a
的类型为char (*)[5]
,即&a
是指向(地址)和大小为5的数组的指针。但是,如您所知,a是第一个元素的地址。数字 。在数量上,它们与你在下面使用^的插图中看到的相同。
但是当你递增这两个指针/地址时,即(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
为char (*)[5]
的原始type
(&a+1)
,然后将其转换为类型char*
,它与a的类型相同,即基数数组的地址。(再次注意数组的基地址和数组的地址之间的区别。在上面的语句中进行强制转换和赋值后,接着是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
.So当你将ptr
减为(ptr-1)
,它会跳回一个元素,现在指向数组的第一个元素,即1
在第二种情况下解除引用ptr
给出1
。
1 2 3 4 5 ^ // ^ stands at address of 1, so *ptr gives 1
希望这一切都清楚。
数组不是指针! 有关更多信息,请阅读comp.lang.c FAQ的第6部分 。
让我们先来看看你的第二个案例吧,因为它更“正常”而且惯用。 逐行:
- 声明
a
包含5个char
元素的数组。 - 数组的名称(
a
)在此上下文中衰减为指向其第一个元素的指针。 你添加1
并将结果分配给ptr
。ptr
指向2
。 虽然你有一个演员阵容,但是没有必要。 - 你从
ptr
减去1
然后取消引用并打印 – 因此你得到1
。
现在,让我们逐行解决第一个案例:
- 声明
a
包含5个char
元素的数组。 - 你取a的地址,产生一个
char (*)[5]
类型的指针。 然后你将1
添加到这个指针 – 由于指针算术,这个新指针会在内存中的5
之后传递到字节。 然后你进行类型转换(必需,这次)并将此值赋给ptr
。 - 你从
ptr
减去1
,然后是dreference并打印。ptr
是一个char *
,所以这个减法只是将指针从“一个过去的a
”移回一个指向一个的最后一个元素。 因此你得到了5
。
最后,原因char *ptr=&a+1;
发出警告是因为C要求指针类型之间的转换具有显式强制转换。 如上所述, &a
的类型为char (*)[5]
, 而不是 char *
,因此要将该值赋给char *
变量,您需要显式强制转换。
不同之处在于您获得的指针类型:
- 数组名称
a
本身表示指向数组初始元素的指针。 当以这种方式解释时,例如在表达式a+1
,指针被认为指向单个字符。 - 另一方面,当你拿
&a
,指针指向一个包含五个字符的数组。
向指针添加整数时,指针移动的字节数由指针指向的对象指针的类型决定。 如果指针指向char
,则添加N
会使指针前进N
个字节。 如果指针指向一个包含五个char
的数组,则添加N
会使指针前进5*N
字节。
这正是你得到的差异:你的第一个例子将指针推进到一个超过数组末尾的元素(这是合法的),然后将其移回最后一个元素。 另一方面,您的第二个示例将指针前进到第二个元素,然后将其移回指向数组的初始元素。
你遇到的是指针算术的微妙之处。
编译器将“a”视为指向char的指针 – 一个大小为1字节的实体。 向此处添加1会产生一个指针,该指针会增加实体的大小(即1)。
编译器将“&a”视为指向字符数组的指针 – 一个大小为5字节的实体。 向此处添加1会产生一个指针,该指针会增加实体的大小(即5)。
这就是指针算法的工作原理。 将一个指针添加到指针会使其增加指针所指定类型的大小。
当然,有趣的是,当评估“a”或“&a”的值时,在解除引用时,它们都会评估到相同的地址。 这就是你看到你所做的价值的原因。
数组“衰减”成指向第一个元素的指针。 因此,获取a的地址会给出一个指向5个字符数组的指针,就像声明char[][5]
。 并且递增此指针前进到char[][5]
数组的下一个元素 – 一次是5个字符。 这与递增从char[5]
数组衰减的指针(即一次只有一个字符)不同。