一、前言
指针是C语言的精华,就像封装、继承、多态这三大特性之于Java一样重要。如果不会指针、不会结构体,那只能算编程爱好者,不能算专业程序员。利用指针很方便的处理内存地址,很方便使用数组和字符串,并能像低级语言一样处理内存地址,从而写出精炼而高效的程序。
二、学习指针之前要明白内存单元
2.1 什么是内存单元
我们知道在C语言程序中,定义一个变量,比如
int a = 2020;
编译的时候就会给这个int类型的a分配相应的内存单元,每种数据类型所占的内存单元数是不等的,这对C语言数据基本类型比较了解的同学肯定能明白,int占4个内存单元,char占一个内存单元。
比较难理解的概念叫地址:
刚刚说了,要给a分配内存单元,
每一个内存单元可以看做是一平方米土地,
分配内存单元可以看成要给他安排一个座位,因为它是int类型的,所以他的座位要占4个一平方米土地,即四个内存单元
至于a,a是这个变量的名字,就像我叫Kelvin一样,
2020是这个变量的实质内容,就是a这个变量名所对应的实实在在的实体,就像Kelvin所对应的我的身体一样。
画个图就比较容易理解
上面的横向表格可以看成我们的计算机内存地址,下面的20,23,24。。。这种数字可以看成是地址编号,就像门牌号一样,一格一格地过去,每个int类型要占四个内存地址,所以28、29、30、31这四个格子一起存储的是2020这个整数。
2.2 查看内存单元
如下,我写了一个初学者最熟悉的程序,其中&是取地址符号,&a便是将a这个int类型的变量地址给取出来。
#include <iostream>
int main() {
int a = 2020;
printf("%d\n",a);
printf("%d",&a);
return 0;
}
运行结果如下图,可以看到,a的地址为7405132
那么问题来了,7405132只表示了一个内存单元,而int类型的a是要占4个内存单元的,那么这个7405132是表示哪一个内存单元呢?
这里要注意,**变量的内存地址是指存储这个变量的内存单元开始的地址。**也就是说,7405132、7405133、7405134、7405135这四个内存单元一起存储了2020这个整数。
也许作为初学者你会迷惑,1也是int型,2020也是int型,为啥他俩要用同样的存储空间,1就不能用一个内存空间吗?
这是因为在计算机内,数是二进制表示的,你所看到的整数1在计算机看来其实是一样的长度,只不过某些位置上的1和0不同而已,可以回顾一下,int类型的取值范围是不是 -2^31 – 2^31-1,说明需要32位来表示int型数据,每8位为一个字节,正好是4个字节。故而int型数据需要4个内存单元。
三、指针是什么
上节详细讲解了变量在C语言中的存储方式,那么你会发现好像在此之前你从未和存储单元、存储地址这些东西打过交道,写程序也没有用到他们。所以我必须先给你说明一下C语言为什么要引入指针这个概念。
3.1 为什么要引入指针
试想一个场景,医生要去酒店给一批刚刚从国外回来的人做核酸检测,这群人并不住在一起,而是分散在酒店的各个房间(也许相邻,也许同层,也许隔得很远)如果医生只有这一群人的名单,就像我们刚刚写的程序,假如有a,b,c,d,e这五个变量,我们只知道名字,也是可以操作的,只不过比较麻烦。如果医生不仅有名单,名单的后面还附带着每个人的住址,比如a住在A310,b住在B531诸如此类,那医生操作起来就会简单的多,直接去找这批隔离者就好了。也许到这里你能理解一点,却还是没有深入灵魂的那种感触,那就等学到链表的时候再回想这个例子吧。
要想理解指针,首先我们要先学会定义指针,和以往定义数据一样
int a;//定义int类型变量a
int* p;//定义一个指向int类型的指针变量p
"*"表示这是一个指针变量,和int在一起,表示p是指向int类型的一个指针变量。其实也可以这样写
int *p;
不过这样写很容易把定义指针变量的语法和取出地址元素的语法弄混淆,如果你能充分理解,那就不用担心了
那么我们来写一个简单的例子
#include <iostream>
int main() {
int a;
int *p;
a = 2020;
p = &a;
printf("%d\n",&a);
printf("%d",p);
return 0;
}
应该很容易理解,p是一个指针变量,指针变量存储的是地址,这里的p存储的是一个int类型元素的地址,就是a的地址。那么运行一下
果然,两个地址一样,说明p确实是a的地址。
回顾你写输入语句的时候,是不是经常有&a这样的语法,那么既然这里需要取出a的地址然后输入,那么直接用p是不是也可以获得一样的效果。写个程序试一下
#include <iostream>
int main() {
int a;
int *p;
p = &a;
scanf("%d",p);
printf("%d",a);
return 0;
}
这和你以往写的任何输入输出程序都没有区别,唯独把&a换成了p,运行一下
和以往写的程序没有任何区别。
回到开头医生做核酸检测的那个例子,&a就相当于一份没有房间号的一份名单,医生可能要逐一打电话去询问名单,但是p就相当于印着地址的一份名单,医生拿起来就可以直接用。
3.2 指针怎么用
也许作为初学者的你没有听说过什么叫“值传递”和“引用传递”,先写一个最为熟悉的交换两个数的函数吧。
#include <iostream>
void swap(int m,int n){
int t;
t = m;
m = n;
n = t;
}
int main() {
int a = 1;
int b = 2;
swap(a,b);
printf("a = %d\n",a);
printf("b = %d",b);
return 0;
}
也许你会对这段代码很熟悉,会很自然的说出输出结果,a = 2,b = 1;不过看到结果,你可能会有一点点失望。
两者并没有发生改变,其实在swap内部确实是发生了改变,但是这种改变不能传递到a,b两者本身。
就像医生在名单上让a,b换一个房间住,但是没有去他们所在的房间真正让他们换,所以最终的a,b还是没有一点点改变。
那么在实际的开发中确实是需要进行改变的,怎么实现呢,刚刚说到就是没有去真正的房间让ab交换,所以现在我们要使用地址来交换。
#include <iostream>
void swap(int* m,int* n){
int t;
t = *m;//*m是取出地址m所对应内存单元中的数据
*m = *n;
*n = t;
}
int main() {
int a = 1;
int b = 2;
int* pa = &a;
int* pb = &b;
swap(pa,pb);
printf("a = %d\n",a);
printf("b = %d",b);
return 0;
}
运行一下
ab确实做了交换,其中*m的意思是取出m所对应地址的内存空间中的数据。
这里的swap就相当于接收了两个房间号,将这两个房间号里面的人进行交换,这样就真正的实现了交换房间。
总结
指针是C语言的精华,如果不懂指针那就不算学会了C语言,这篇文章只是说明了指针是什么以及基础用法,指针的更广泛用途还没有细说,后续的blog会详细讲解指针数组,数组指针等比较高阶的用法。
原创不易,打赏一下我们或许能做个朋友~~