数组 + 1 的各不相同(或许会有你的盲点)

在讨论这个问题前, 我们先看一段代码。这段代码,大家都会很容易看懂, 但是真正能够明白的我不知道有几。

/*   代码 1-1   */

#include <stdio.h>

int main() 
{
	int a[5]={1,2,3,4,5};
	int *ptr=(int *)(&a+1);
	printf("%d,%d",*(a+1),*(ptr-1));
	return 0;
}


输出结果为: 2, 5


讨论:

当我第一眼看到这个代码的时候, 我的第一直觉答案是2, 1. 但是当运行结果出来以后, 我蒙了。为什么会是2, 5 呢。后来我就测试了输出他的地址(原来最终问题,通过查看内存才是王道),我就发现了问题的所在。主要的原因就在于 &a + 1 这里。

这里给出我查看内存运行情况的代码:

/*     代码1-2     */

#include <stdio.h>

int main() 
{
	int c[6] = {1, 2, 3, 4, 5, 6, };
	
	printf("c = %p, c+1 = %p, &c+1 = %p\n", c, c+1, &c+1);	
	/* 数组名c 的地址, 数组名c + 1 的地址, 数组名地址 + 1 的地址*/
	return 0;
}

运行结果: c = 0022FF18, c+1 = 0022FF1C, &c+1 = 0022FF30


通过这个输出结果, 我们不难看出,c+1 的地址是在数组地址(c 的地址)的基础上加了1 个int。但是,&c + 1 的地址是在数组地址(c 的地址)的基础上加了6 个int 。而这6 个int 就是整个数组的长度。这不是偶然,这是必然。就是说,在数组地址的基础上加 1 就相当于再加了一个数组(跳过一个数组的长度)。


这样我们就不难解释上面代码 1-1,为什么结果是2, 5 。

int *ptr=(int *)(&a+1);

这里的ptr 地址实际上就是跳到整个数组的最末端,然后新开始的一个数组(即元素5 后面的一个位置)。所以,当输出*(ptr-1) 就是5 。


讨论了一维数组的情况, 下面我们接着看看二维数组。

按理说,把一维数组弄明白了二维数组就很简单,这里因为笔者在后面写二维数组的时候也遇到了点麻烦,所以就多多啰嗦一下。


下面我们看一段测试代码:

/*       代码 2 - 1    */

#include <stdio.h>


int main() 
{
	int a[][3] = {{1, 2, 3, }, {4, 5, 6, }};
	
	// 数组名的不同 +1
        printf("结果1\n");
	printf("** a        = %d\n", **a);
	printf("a          = %p\n", a);
	printf("&a         = %p\n", &a);	
	printf("a+1        = %p\n", a+1);
	printf("&a+1       = %p\n", &a+1);
	printf("\n");
	
	// 数组起始单元的不同+1
        printf("结果2\n");
	printf("* a[0]      = %d\n", *a[0]);
	printf("&a[0]      = %p\n", &a);	
	printf("a[0]+1     = %p\n", a[0]+1);
	printf("&a[0]+1    = %p\n", a[0]+1);
	printf("\n");
	
	// 数组中间单元不同+1
        printf("结果3\n");
	printf("a[0][1]    = %d\n", a[0][1]);
	printf("&a[0][1]   = %p\n", &a[0][1]);
	printf("&a[0][1]+1 = %p\n", &a[0][1]+1);
	
	return 0;
}

输出结果为:

结果1
** a        = 1
a          = 0022FF18                   // 数组名
&a         = 0022FF18                  // 数组名地址
a+1        = 0022FF24                 // 数组名加1 , 结果为跳转到下一行(即第二行,这里跳过3 个int)开头。
&a+1       = 0022FF30                // 数组名地址加1, 按照上文介绍,跳转到下一个数组的位置(即跳过整个数                                          组,如果是加2 , 那么就跳过2 个数组空间的位置。),这里就跨过了6 个int 的空间。


结果2
* a[0]      = 1                            
&a[0]      = 0022FF18             // 数组中第一行行名地址
a[0]+1     = 0022FF1C            // 数组中第一行行名加1, 结果为跳转到同行的下一个元素位置(跳过1 个int)
&a[0]+1    = 0022FF24           // 数组中第一行行名地址加1, 结果为跳转一行的地址,到下一行的行头位置                                           (即跳转了3 个int 位置)。


结果3
a[0][1]    = 2
&a[0][1]   = 0022FF1C           // 数组中位置为[0][1]  的地址
&a[0][1]+1 = 0022FF20         // 数组中位置为[0][1]  的地址加1 ,结果跳转了1 个int 的位置。跳转到同行的下一                                      个元素位置。是本行的行末位置,那么就跳转到下一行的开头位置。


这段代码有点多, 都是printf(); 这个不重要,重要的是输出的内容。分析就在答案的后面注释。


从这里的结果, 我们可以看出 结果3 中的 &a[0][1]+1 = 0022FF20 与 结果1, 结果2中的 &a+1       = 0022FF30 , &a[0]+1    = 0022FF24  运算方式是不同的。这是因为,结果1和结果2 中是对数组名地址加1, 而结果3 不是 对数组名地址加1, 他只是数组中间的一个元素地址。如果要进一步深究,我们可以再把指针带进来考虑就可以更加清晰明白。


指针的操作,就相当于上面 结果3 的操作结果一样。他的操作都是按部就班,不会存在因为加1 而跳转一个数组的长度的情况。下面我们看看测试代码:

/*    代码 3 -1     */

#include <stdio.h>

int main()
{
	int a[][3] = {{1, 2, 3, }, {4, 5, 6, }};
	int *p1 = *a;            // 取a 的地址
	int *p2 = a[0];          // 取a[0] 的地址
	int *p3 = &a[0][1];      // 去a[0][1] 的地址
	
	printf("p1   = %p \n", p1);
	printf("p1+1 = %p \n", p1+1);
	printf("p2   = %p \n", p2);
	printf("p2+1 = %p \n", p2+1);	
	printf("p3   = %p \n", p3);
	printf("p3+1 = %p \n", p3+1);
	
	return 0;
}

输出结果:

p1   = 0022FF0C
p1+1 = 0022FF10
p2   = 0022FF0C
p2+1 = 0022FF10
p3   = 0022FF10
p3+1 = 0022FF14


从这个结果可以看出指针的跳转是按位置跳转的。一次只能跳转偏移个数个位置。

比如: int *p = a;            p + 3;  就表示跳转从当前p 的位置, 跳转到之后的第三个位置。



总结:

综上所诉,笔者最想表达的是,在数组名取地址然后增加偏移量时, 他是按照整个数组再进行跳转。即跳过整个数组的长度的偏移量倍。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值