指针的传递与偏移
指针的传递
为了理解指针的传递,我们来看看下面这个程序
#include<stdio.h>
void change(int j) {
j = 5;//j是形参
}
int main() {
int i = 123;
printf("before:%d\n", i);
change(i);//i是实参
printf("after%d\n",i);
return 0;
}
注意:这里的i是主函数的局部变量。
change的目的就是改变实参的值。把i=123变成5,那上面这段程序可以实现吗?
从结果我们得知,change函数并没有实现它的功能。那为什么会这样呢?
有人就会问:“哎呀!你在上一篇文章中数组传递中也有过类似的操作,为什么数组就可以,上面程序这样就不行啊!?
别急,兄弟姐妹们听我细嗦
首先先来揭晓一下为什么会这样,其实函数是存在栈空间的。在我们主方法中定义了一个int i(局部变量),它就会生成一个四个字节大小的空间给i 同理,在change函数中也会生成一个四个字节大小的空间给j(局部变量)。这两个变量都有各自的地址,在主函数中的i只是传过去了值123,在change函数中执行j=5相当于给把j的值从123变成了5而已。
我们debug验证一下,可以看到传值过去的时候j的值就是123
点击下一步执行change(i),可以看到
0x0096FBFC 7b 00 00 00(i的地址与内存的值)
0x0096FB28 05 00 00 00(j的地址与内存的值)
只有j发生了改变i表示内心毫无波澜甚至有点想笑
所以输出的结果都是主函数中i原来的值是正确的。
那么解答第二个问题,我们上一篇文章中数组传递中也有过类似的操作,为什么数组就可以呢?
如果有认真阅读过文章的人肯定记得当时传参到方法中的数组我们sizeof运算符运算了一下大小是4个字节(因为定义的是int类型的数组),之后得出的结论就是传过了初始地址过去
那传地址这种事情不是只有指针才有的嘛,而且数组的变量本质就是指针变量啊。
欸!那如果我们这样试试会不会不一样
我们用&取i的地址,再用*取出地址所对应的值,再重新赋值。就好像这样
#include<stdio.h>
void change(int *j) {
*j = 5;//*j是形参
}
int main() {
int i = 123;
printf("before:%d\n", i);
change(&i);//&i是实参
printf("after%d\n",i);
return 0;
}
可以看到我们就这样成功了,因为我们从头到尾都是围绕i的地址和值在做事情。
这样的操作也叫做指针的传递。
指针的偏移
概念(复制粘贴的嘻嘻嘻嘻)
指针即地址,就像我们找到了一栋楼,这栋楼的楼号是B,那么往前就是A,往后就是C,所以应用指针的另一个场景就是对其进行加减,但对指针进行乘除是没有意义的,就像家庭地址乘以5没有意义那样。在工作中,我们把对指针的加减称为指针的偏移,加就是向后偏移,减就是向前偏移。
接下来我们用一个程序来理解指针偏移
比如有一个数组a,我们定义一个int指针变量p,通过指针偏移用p输出数组a各个元素的值
程序如下:
#include<stdio.h>
#define N 5
int main() {
int a[N] = { 1,2,3,4,5 };
int* p = a;//对一个指针进行取值,得到的类型是其基类型(比如这里取值就得到是int类型)
//上面的语句等价于
// int *p;
//p=a;
for (int i = 0; i < N; i++) {
printf("%d\n", *(p + i));
}
return 0;
}
讲解:
我们用定义指针变量后用 p=a,使数组a的初始地址赋给p,然后用指针偏移循环输出数组a的元素,*p+0等于a[0] (因为初始地址本来就是a[0]的值,所以p+0就算a[0]的地址),*p+1等于a[1](向下偏移一个单位),这样依次输出全部元素。
从监视我们可以看到p的值就是a的地址,✳p+0就是1
注意:指针加减叫做偏移,是和我们平时的运算不太一样的。上面的p+i是指针偏移,不是我们所理解简单的加减法。
比如i=1时,p+1不是地址向下移动一个字节,而是移动(p+(1*p的基类所占字节大小)
因为我们定义的是int ✳p;int类型是4个字节。即取p地址的向下4个字节的值