** Lecture4 指针、函数指针与数组应用**
指针
好处:
1.在函数参数传递时,不用把全部数据都传入,只需传入数据的首地址(数据是在内存中连续储存的),从而节约内存空间
2.使代码更加简洁
基本思想:
1.一种数据类型对应一种指针。
2.函数可以有指针,称为函数指针。
3.指针必须要定义指向的地址。
重要应用:
函数中的地址传递和值传递
1.值传递
传入内容:变量的拷贝值,拷贝值和原变量不共用同一个内存空间。
变量类型:和传入的变量类型相同,这里不妨认为是int,可以是int、float、char…,也可以是相应的指针int*、float*、char*…
实例:
值传递(以int为例子)
void add(int x) {
x = x + 1;
}
int main() {
int x;
add(x);
printf("%d\n", x);
}
解释:输出的是5,因为在传入x时,程序用另一片内存空间将拷贝的x值存了进去,在add函数里,拷贝的x变成了6,但是等到add运行完之后,拷贝的x作为局部变量就被销毁了,根本没有对main函数中的x造成影响。本质上是两块内存空间是独立的,所以输出的x的值就是5。
2.地址传递
传入内容:变量的地址,因为内存地址相同,函数中的变量和传入的变量共用同一个内存空间。
变量类型:地址,即原本的变量加个星 * ,指向int的地址的指针int*、float的float*、char的char*、指向int指针的地址的int**。
实例
void add(int *x) {
*x = *x + 1;
}
int main() {
int x;
add(&x);
printf("%d/n", x);
}
解释:注意到程序中有三个改变,我们从main函数到add函数依次分析:
第一个,main函数中传入的参数变成了 &x, ‘&’ 代表了地址运算符,这样使用后,传入的参数变成了x的地址,实现了共用内存空间。
第二个,传入参数类型改变之后,函数参数中的类型 也要改变,所以参数也加了个星 *, 变成了 int*。
第三个,由于函数参数类型变成了int*,所以函数中的运算也相应变成了加了 * ,这里用到了 (*+指针)等于指针指向的对象(这里是int类型的数值)的知识。
*2.特别的地址传递-指针的地址传递:&ptr
ps.我愿称之为俄罗斯套星星
传入内容:指针ptr所指向的地址
变量类型:int** h,注意这里的ptr的类型是 int * ,所以&ptr表示的是指向指针的指针,即 &ptr 代表了指针ptr的地址,所以要加两个 ** 。
同理,如果要对指针变量的指针来一次地址传递,那就再加一个 *, 变成 int***.
实例1:改变指针(结合int数组[],指针)
void add(int ** p) {
*p = *p + 1;
}
int main() {
int ar[3] = { 50,60,70 };
int* q = ar;
add(&q);
printf("%d\n", *q);
return 0;
}
这个例子特别好玩!
首先明确数组和指针十分得相似,而且数组ar表示指向数组第一个元素的指针。
先看main函数:
这里定义了大小为3的数组,并且定义指针q作为数组第一个元素的地址。
因为是地址传递,所以传入的参数是 &q,注意!!! q的类型是int*,所以&q的类型是 int**,表示指针的地址。
再看add函数:
函数参数的类型与 &q一致,为 int**。函数体中*q是 指向数组元素的指针,所以 *q = *q +1表示改变该指针,使其 转而指向数组的后一个元素”。在本例中就从本来指向的50变成了指向60.所以输出的就是 60.
实例2.进阶版-改变指针(结合int数组[],指针)
void add(int ** p) {
*p = *p + 1;
**p = **p + 1;
}
int main() {
int ar[3] = { 50,60,70 };
int* q = ar;
add(&q);
printf("%d\n", *q);
return 0;
}
感受到指针的魅力了嘛,继续看这个例子的进阶版,看懂了就完全理解了指针和参数传递了!
这里和之前的例子只有一个区别:在函数体中加了一行 **q = **q +1
怎么理解呢?
可以这么想,之前的 *q 表示数组第k个元素的地址,那么再加个星星 * 就相当于更进一步:**q 就表示第k个元素的(地址指向的)数值,所以 **q = **q +1 表示第k个元素的数值加1。
综合以上两个例子,我们可以知道,此时 *q 指向的是 第二个元素,而且第二个元素的数值加了1,所以输出是61!