一、指针的作用
- 使程序简洁、紧凑、高效
- 有效地表示复杂的数据结构
- 动态分配内存
- 能直接访问硬件
- 能够方便的处理字符串
- 得到多于一个的函数返回值
二、内存、地址和变量
1、内存地址
2、变量和地址
1)变量用来在程序中保存数据
比如: int k = 58; //声明一个int变量k,CPU为k分配4个字节,
可以存储数值58
2)关于变量
- 输入变量的值
- 打印变量的地址
- 打印变量占用的内存空间
三、指针
- 指针变量一般形式如下:
<数据类型> * <指针变量名> ;
char * p ;
1. *,指针运算符,表示定义的是一个指针类型的变量
2. 数据类型,指针变量要保存地址,数据类型要和这个地址中保存的数据的数据类型保持一致。
1、指针的初始化
指针在说明的同时, 也可以被赋予初值,称为指针的初始化
一般形式:
<数据类型> *<指针变量名> = <地址量> ;
int main() {
int a = 10; //在内存中开辟一块内存空间
int *p =&a; //使用&(取地址操作符),取出变量a的起始地址
//a变量占4个字节,将a的第一个字节的地址放在p
变量中,p就是一个指针变量
}
2、指针和变量
四、指针的目标和“解引用”
1、指针的目标
- 指针指向的内存区域中的数据称为指针的目标
int a = 126; int *p = &a;
2、指针的解引用
3、关于指针的三种写法
设p为一个指针,则:
- p — 指针变量, 它的内容是地址量
- *p — 指针的目标,它的内容是数据
- &p — 指针变量占用的存储区域的地址,是个常量
五、指针的赋值
- 指针的赋值运算指的是通过赋值运算符向指针变量送一个地址值
- 向一个指针变量赋值时,送的值必须是地址常量或指针变量,不能是普通的整数(除了赋零以外)
- 指针赋值运算常见的有以下几种形式:
① 把一个普通变量的地址赋给一个具有相同数据类型的指针
double x=15, *px;
px=&x;
② 把一个已有地址值的指针变量赋给具有相同数据类型的另一个指针变量
float a, *px, *py;
px = &a;
py = px;
③ 把一个数组的地址赋给具有相同数据类型的指针。
int a[20], *pa;
pa = a; //等价 pa = &a[0]
六、指针的大小
对于32位的系统,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平和低电平。那么32根地址线产生的地址就是32个(1或者0)
总结:
- 32位的系统,地址是32个0或者1组成二进制序列,地址需要4个字节的空间来存储,一个指针变量的大小就是4个字节。
- 64位系统,有64个地址线,一个指针变量的大小就是8个字节
- uname -m命令,查看系统是多少位
七、空指针
- 不同于没有初始化的指针
- 在C语言中,NULL本质是0
#define NULL (void *)0 // 在C中NULL是强制类型转换为void *的0
- 为什么指向0地址处?2个原因。
① 指针如果没有给定初始值,值是不确定的
② 0地址是一个特殊地址,在一般的操作系统中都是不可被访问的,如果C语言程序员不按规矩(不检查是否等于NULL就去解引用)写代码直接去解引用就会触发段错误
八、野指针及成因
1、什么是野指针
- 指针指向的位置是不可知的(随机的、不正确的、没有明确指向的)
- 指向被释放的内存或者没有访问权限的内存的指针(非法访问)。
2、野指针是怎么造成的
- 野指针的错误来源就是指针定义了以后没有初始化,也没有赋值(总之就是指针没有明确的指向一个可用的内存空间),然后去解引用。
① 指针没有初始化
② 指针越界访问
③ 指针指向的空间释放了
3、野指针的危害
- 野指针指向别的变量(指针所指向的那个变量)的地址
- 即指向了一个地址是不确定的变量,去解引用,结果是不可知的。
① 段错误
指向不可访问(操作系统不允许访问的敏感地址,譬如内核空间)的地址,结果是触发段错误
② 程序错误被掩盖
指向一个可用的、而且没什么特别意义的空间(譬如我们曾经使用过但是已经不用的空间),这时候程序运行不会出错,也不会对当前程序造成损害这种情况下会掩盖你的程序错误,让你以为程序没问题,其实问题是非常严重的
③ 程序出现离奇错误
指向了一个可用的空间,而且这个空间其实在程序中正在被使用(譬如说是程序的一个变量x),那么野指针的解引用就会刚好修改这个变量x的值,导致这个变量莫名其妙的被改变,程序出现离奇的错误。一般最终都会导致程序崩溃,或者数据被损害。
4、如何避免野指针
野指针的错误来源就是指针定义了以后没有初始化,也没有赋值(总之就是指针没有明确的指向一个可用的内存空间),然后去解引用。
① 定义指针时,同时初始化为NULL
② 在指针使用之前,将其赋值绑定给一个可用地址空间
③ 在指针解引用之前,先去判断这个指针是不是NULL
④ 小心指针越界
⑤ 指针使用完之后,将其赋值为NULL
⑥ 避免返回局部变量的地址
int a = 10;
int *p = &a;
int *q = NULL;
if (a > 0){
int b = 20;
q = &b;
a += *q;
}
(*q)++;
*p = *q;