操作指针就是操作内存
1. 内存结构回顾
1.1 内存是由字节组成的
1.2 每一个字节都有一个地址
1.3 这个地址用来干嘛的
通过这个地址找到这个内存空间
2. 地址就是指针
地址的别称就是指针。地址就是指针,指针就是地址。
(1)为什么将地址称为指针
比如给张三取个外号叫胖子,为什么叫胖子,因为他的特点就是很胖啊,大家习惯以后就不喜欢叫他张三了,叫胖子感觉更加亲切。
那么地址到底是具有什么特征我们才称为指针的呢?
我也可以称门牌号为指针,因为门牌号指向了某个房间,我也可以称我家的地址叫指针,因为我家的地址指向了我家,根据门牌号、地址的指向,我就可以找到某个房间和我家,因为带有指向作用就叫指针。
同样的,我们将地址别称为指针,是因为我们能够通过这些地址(指针),找到你要操作的这些字节空间,然后读、写这些地址空间。
为什么将地址称为指针呢,因为地址具有指向作用,通过这个地址就可以找到某个字节空间,然后操作(读、写)字节空间,因为地址具有指向作用,因此我们就给地址取了个外号叫指针。
(2)变量空间开辟于内存
1) 所开辟的空间的字节数>=1字节
比如:
(a) char型变量;1个字节
(b) short型变量;2个字节
(c) int型变量; 4个字节
(d) float型变量; 4个字节
(e) double型变量; 8个字节
sizeof运算符:这个运算符用于获取该类型数据存储与内存时,所需要的空间的字节。
sizeof只是一个运算符,不是一个函数,虽然使用它的时候,样子蛮像函数调用的,但是它确实不是函数。
sizeof()内部写类型或者变量都可以得到它的空间大小。
2)变量的每个字节都有一个地址
3) 变量的第一个字节的地址代表整个变量的地址
也就是说,变量的指针就是变量第一个字节的地址。
例如 int a ,变量a在内存中占用4个字节,每个字节都有一个地址,4个字节中的第一个字节才是变量a的地址,也 即 4个字节中的第一个字节才是变量a的指针。
我用这个变量的指针(第一个字节的地址),就能找到这个变量空间,找到这个变量空间干嘛呢,操作它,读或者写 变量空间。
4) 如何使用变量的指针找到整个变量空间?
(a) 第一步:通过变量第一个字节的地址(指针)找到第一个字节。
(b) 根据变量的类型知道变量总共所占的字节,从第一个字节往后数,就能找到该空间所有的字节。
例如int a, 你找到a的第一个字节后,往后数到4这个4个字节就属于变量a的空间。
所有通过指针找到某个(变量)空间,都是采用我们刚才讲的那种方式,先通过指针找到第一个字节,然后再通过 它所知道的整个空间的字节数,从第一个字节往后数,最后数到的所有的字节就是这个空间所占的总的字节数。
我们后讲数组的时候,数组更是典型的使用我们所讲的方式来访问数组,不仅仅是数组,所有类型变量(结构体变 量)都是使用这样的方式来找到整个变量空间的。
3. 变量的指针就是一个数
前面说过,变量的指针就是变量第一个字节的地址,通过这个地址(指针)就可以找到这个变量。
而这个地址(指针),或者说这个指针就是一个数。
1) 指针这个数好比门牌号
指针就是一个数,只是这个数有点特殊,它是一个地址。
就好比门牌号一样,门牌号也是一个数,不过它是房间的地址,用来指向某个房间的。指针就是每个字节的门牌号,每个字 节就相当于一个房间。
2) 在C语言中,我们使用什么方法才能得到一个变量的指针的呢?
方法就是使用取地址符&,比如,
int a;
&a表示的就是变量的地址。变量的地址是编译器分配的,程序员是不知道的。
这里再次强调,变量的指针指的是第一个字节的地址,因此&a得到的指针就是a变量的第一个字节的地址。
例子:
int a;
scanf("%d", &a);
解释:&a得到的是a变量第一个字节的地址,也就是a变量的指针,用a变量的指针拿来干嘛呢?通过a变量的指针能够找 到a变量的空间,就能够把键盘输入的数写入到变量空间里面去。
地址是一个特殊的整型数,按道理我们打印地址我们应该用%p的格式,
用%p的形式来打印就是以16进制的形式来显示的。
总结:打印一个变量的指针这个数时,我们可以使用%d的形式打印,这也说明地址就是一个比较特殊的整型数,但是你 不能说他是整型数,它是一个地址,我们也可以使用%p,这个是地址的专用打印格式,只是打印出来的地址结果 是以十六进制形式显示的。如果你希望好理解的话,你也可以使用%d,以十进制形式打印,十进制的数更好理 解。
3) 如何使用一个数
a. 使用普通的整型数
直接使用
比如:printf("%d\n", 10);
直接使用,就是以常量方式使用。
间接使用
先把这个数放到对应类型的变量中,通过使用变量来使用这个数
正常情况下,在不涉及类型转换时,我们要求将某个类型的数据,放到对应类型的变量中,要求=两边的类型必须 一 致
放到变量当中使用呢,就是以变量形式使用。
int a = 0;
int *pa = &a; //先把数放到篮子里面,定义了一个指针变量,用来存放地址(指针),存放指针这个数的变量 我们就叫指针变量,与前面类似,专门放整型的变量叫整型变量
printf("%d\n", &a); //直接把这个数打印出来
printf("%d\n", pa); //到这个篮子里面去取
4. 一级指针与一级指针变量
(1)什么是一级指针
所有普通变量的指针都是一级指针。
(2) 什么是普通变量
只要在变量的声明或者定义中,看不见*的就是普通变量。
例如:
char a;
int b;
short c = 100;
float d;
double e = 12.6;
(3)普通变量指针的类型
变量的指针是个啥,是一个数,这个数肯定是有类型的,
&a; //普通变量a的指针,是一个数,第一个字节的地址
这个数是有类型的,类型为 : char *, char型的一级指针类型,*表示一级(如果有两个*就表示二级,依次类推), char表示这个指针指向的变量空间是一个char型变量空间。所有的普通变量的指针都是一级的(只有一个*)。
一级的意思就是这个指针指向的是一个普通变量,因为只有普通变量的指针才是一级的。
&b; //普通变量b的指针,是一个数
int *类型, *表示一级,什么叫一级,一级的意思就是这个&b指针指向的变量是一个普通变量,只有普通变量的指针我 们才称为一级。普通变量的指针是一级,反过来一级指针指向的一定是一个普通变量。
int 是什么意思呢,表示指针指向的空间是int型的,4个字节,用于存放整型数的。
总结:所有的数都有类型,更何况指针这个数
指针的类型,我们是怎么写的
例如:short * 由普通类型+*组合而成
普通类型:表示这个指针所指向空间类型
*:表示是一个指针类型,一个*表示一级,为普通变量的地址,普通变量的地址是一级的,如果有多个*, 表示多级指针
(4) 变量存放数据时,什么类型的数就放到什么类型的变量中
a) 普通变量为例
比如10这个数为int型的,就必须放到int型的变量中,因此定义变量时,为int b,表示这个变量是int型的,专门用来 存放int型的数。
int b = 10;
b) 对于指针和指针变量来说道理是一样的
什么是指针变量:用于存放指针这种数的变量,就是指针变量
指针变量的命名规则与普通变量的命名规则相同
指针变量在命名时喜欢使用P打头,为什么呢,因为P表示pointer
在定义变量时,如果没有*表示定义的是普通变量,如果有*表示定义的是指针变量,用来干嘛呢,存放指针这种数。
注意:*的前后都可以加空格,一般采用这种写法 int *pb = b; *的前面加空格,后面不加空格
当我们说某个指针变量指向某个变量空间时,本质上不是这个指针变量指向了某个空间,实际上是指针变量里面放的 指针这个数,它指向某个空间。但是当我们用熟悉了以后,都认为指针变量指向了某个空间。
指针变量也是一个变量,既然是变量,那么里面放的数就是可变的,
如果指针变量里面放的指针这个数变了,那么指向就发生了改变了
(5)使用指针访问空间
1)什么类型的指针就放到什么类型指针变量里面去
int a = 10;
int *pa = &a;
float b = 12.63;
float *pb = &b;
错误示例:
float *p_a = &a; 等号左右两边类型不匹配,&a是int *类型的
float *p_a = (float *)&a; 通过强制转换是等号两边类型一致
pa既然是个变量,当然也有地址,也有指针。
2) 如何找到a变量空间,然后读、写a变量空间
(a)直接通过a这个变量名操作
a = 100; 直接通过变量名写入数据
int b = a; //从a里面读数据
(b)使用指针操作a变量
使用指针访问变量空间时,所涉及的符号*
写数据:
*pa = 200; //等价于a = 200;
如果我知道a的地址是多少,比如说是010,则可以这么写:
*010 = 200;或者 *&a = 200;
读数据:
b = *pa; //等价于b = a;
此时,*pa代表的就是a变量空间,我们可以对其读写
这里的*是找空间操作,找到指针变量pa里面存放的指针(数据)所指向的空间
*的作用就是找到指针所指向空间,找到后我们就可以对这个空间进行读写操作。
使用*,是如何找到指针指向的空间的
第一步:使用pa所存放的指针,找到a变量的第一个字节
第二步:pa是int *型,说明pa里面所放的指针所指向的空间是int型的,int 是4个字节,从指针找到的第一 个字节往后数4个字节,就是a变量的空间。
前面我直接使用变量名操作,不是更加好理解吗,使用指针这种方式,绕着弯来访问,不是多此一举吗? (c)*的两种用途
--定义指针变量时使用 ,比如
int *pa; //定义一个int型的一级指针变量。 几个*就是几级
*表示,你定义的是一个指针变量,一个*表示是一级指针变量。
因此在定义变量时使用*,起一个说明作用,用于说明你定义的是指针变量。没有*的就是普通变量
-- 访问空间时使用*
int a;
int *pa = &a; //说明是个指针变量
*pa = 100; //此时的*是找空间操作,找到pa这个指针变量里面放的指针所指向空间,整个*pa就代表指 针所指向的空间,这里*pa就代表a,找到该空间后,就可以对其进行读写操作。
b = *pa; //等价于b = a, *pa 代表的就是a空间,然后从a空间都数据,写入b变量
5. 函数调用时,普通的传参
(1)普通传参的缺点
1)在普通传参的情况下,被调用函数不能操作调用函数的局部变量的空间
2)因为通过变量名的方式访问局部变量时,局部变量会受到作用域的限制。
5.1 函数调用时,传递指针
1)传递指针的好处
可以突破作用域的限制
5.2 传递普通数值与传递指针的异同
1) 相同点
都是将实参的数赋值给形参
2) 区别
一个是传递的是一个普通的数,另一个传递的是门牌号, 即地址(指针),可以通过地址操作它所指向的变量空间。
6. 当我们说“指针”这个词时
当我们把指针用熟以后,我们把一切与地址相关的东西,统统都称为指针,但是你应该要知道,指针这个词有两层含义
(1)指针是一个数
&a, &b, &c
指针就是一个地址,变量的指针指的都是变量第一个字节的地址。
(2)指针变量
指针变量是一个变量,用来存放指针这个数的,指针和指针变量是两个不同的概念,只是我们后面说惯嘴以后,我们统统都称为指针,不管是指针这个数,还是指针变量,我们都简称指针。
当一个指针变量里面放了指针这个数以后,我们就说这个指针变量指向了某个空间,但是你自己应该非常清楚,本质上并不是指针变量指向某个空间,而是这个指针变量里面放的指针这个数指向某个空间。