说到指针,我真的想多说两句闲话。
指针在我看来,就是一个分水岭,如果你不会指针,你即使再牛B,也厉害不到哪去。反过来,如果你会指针,你即使再菜,你也菜不到哪去。C语言的指针有一种独特的魅力,学会它,那以后学习C语言,使用C语言就会得心应手,我们可以使用指针直接操作最底层的东西,想干嘛就干嘛。但是同时指针也是最难学的,说他难,不是难在使用,是难在灵活使用。
想必有学过指针的小伙伴都被“段错误”搞过。这种bug是最难找的,大概率事出现在你的逻辑上,这种bug,要改就要改一大片。小伙伴也不要慌张,只要按照语法规则,细心细心再细心,段错误是可以被避免的。
小伙伴们加油,让我们来学习指针吧~
目录
1、内存的概述
1.1 地址编号
在32位操作系统中,系统会给每个进程分配一个4G(0x0000 0000 ~ 0xffff ffff)的空间,其中,每一个字节都会分配一个地址编号(占4字节),相当于这个地址编号指向的就是内存空间
在64位操作系统中,地址编号占8字节。
计算机最小分配单位是字节。
计算机最小存储单位是二级制位。
1.2 地址、指针、指针变量的区别
地址:强调的是地址编号
指针:强调的是地址编号的类型,决定了内存访问的深度。
指针变量:本质是个变量,存储的是地址编号。
2、指针变量的定义
2.1 定义步骤
- 将*和指针变量名结合
- 将上面的整体看成变量名,定义你所需的变量。
例1:定义一个指针变量p,可以保存int data的地址。
int *p;
例2:定义一个指针变量p,可以保存int data[3]的地址。
int (*p)[3];
例3:定义一个指针变量p,可以保存int func(int a,char b)的地址。
//定义函数指针时,需要将形参变量去掉,只保留形参类型
int (*p)(int,char);
2.2 如何建立普通变量和指针变量的关系
//定义普通变量
int data = 10;
//定义指针变量
int *p;
//将p保存data的地址
p = &data;
注意 :定义时 * 代表这个是指针变量。
使用时 * 代表取内容,*p等价于data
&是取地址符,&data意思是获取data的地址
指针指向的是变量,指针保存的是变量的地址编号。
2.3 指针变量的初始化
C语言中有一个关键字是 NULL ,他代表什么也不指向,代表空
指针变量初始化可以写成,任何指针变量都可以初始化为NULL
int *p = NULL;
不能对未初始化 或者 初始化为NULL 的指针变量取内容,这样会导致段错误。
注:段错误就触发的情况有许多,最常见的是操作非法内存,读了不能读的,写了不能写的。
2.4 如何判断变量是什么(怎么读)
当遇到一个不认识的变量,不知道怎么读时,不要慌,可以用以下方法。
- 根据优先级,分别读出各个部分代表什么,注意不要去掉()
- 倒着把各个部分读出来
例:int (*arr[5])(int,int);是什么?
答:第一步:(指针数组)(函数)
第二部:函数指针数组
所以int (*arr[5])(int,int);是一个 函数指针数组
解析:本质是个数组,保存的是返回值为int,形参为(int,int)类型的函数指针。
3、指针变量的类型
指针变量的自身类型和指向类型实在定义的时候确立的。
3.1 指针变量的自身类型
指针变量的自身类型,是将变量名去掉,剩下的部分为自身类型
一般用于赋值语句中
例:int *p; 自身类型是 int *
int **p[3] 自身类型是 int **
int (*p)(int,char); 自身类型是 int (*)(int,char)
3.2 指针变量的指向类型
指针变量的指向类型,是将变量名和一个*去掉,剩下的部分是指向类型
一般用于决定指针变量的指向深度和单位跨度中
例:int *p; 指向类型是 int
int **p[3] 指向类型是 int *
int (*p)(int,char); 指向类型是 int (int,char)
4、指针变量指向类型的作用
4.1 决定变量取值范围
指向类型决定取值是的范围
注:大端存储和小端存储问题在以后得网络编程中会讲到(这里可以不用关注)。
4.2 决定加减的幅度
5、& 和 * 的关系
&为取地址符,与变量结合可以去这个变量的地址
*为取内容,与指针变量结合是取这个指针变量所指向的值。
在定义时*代表指针变量的意思。
可以对指针变量取地址,因为指针变量本质是变量。
int *p;//为一级指针
int **p;//为二级指针
以此类推;
注意:在使用时 * 和 & 可以从右往左相互抵消 。
例:*&*&*p == *p;
&*&p == &p;
6、万能指针
我们在前面学过一个关键字 void
void是一个空类型,不能使用void定义普通变量,因为无法为变量开辟空间。
void data;//错误,无法为data开辟空间
但是,可以用void定义指针变量,应为不管是什么类型的指针变量,所占用的内存空间都是一样的,32为操作系统为4B,64位操作系统为8B。
void *p;//正确,32位操作系统下指针变量p占4B空间
我们称p为万能指针,可以保存任何类型的一级地址编号。
注意:如果定义了 void *p;
- 不能 *p ,即不能对p去内容,应为无法根据屏的指向类型确定取值宽度。
- 不能p+1.无法根据p的指向类型确定单位跨度。
- 如果想使用p,必须对p进行强转。
- 万能指针一般用于函数形参,提高函数通性。
7、指针操作的注意点(有效防止段错误)
7.1 不能直接操作万能指针,万能指针使用必须强转
如果定义了 void *p;
- 不能 *p ,即不能对p去内容,应为无法根据屏的指向类型确定取值宽度。
- 不能p+1.无法根据p的指向类型确定单位跨度。
- 如果想使用p,必须对p进行强转。
- 万能指针一般用于函数形参,提高函数通性。
7.2 不能对未初始化的指针变量取值
如果定义了 int*p;
不能*p,应为p未初始化,内容不确定,*p的话就是从一个不确定的地址上取内容,容易出现段错误。
7.3 不要对初始化为NULL的指针变量取值
如果定义了 int *p = NULL;
不能*p,应为p初始化为NULL,*p是从地址编号0的位置取值,一般地址编号0是系统必要的内存结构,不允许用户操作,会出现段错误。
7.4 操作指针变量时不能越界
如果执行了以下语句:int data = 10;
int *p = &data;
p++;
如果此时对p取内容,即*p,就会操作非法内存空间,这就是所谓的指针变量越界,容易出现段错误。
好了,看到这里,指针的基本概念就差不多了,下一章,我们来讲讲指针变量在编写代码的时候具体怎么用。