一,指针和地址
电脑在处理数据时,需要的数据是从内存中读取的,为了能够快速的定位并找到相应的内容,就需要用到地址,而c语言中指针就是用来表示地址的。甚至可以说,指针就是地址的别名。
1,指针变量的创建
指针变量,顾名思义,就是存放指针(地址)的变量,如下图
其中,a是一个整型值,&是取地址操作符,&a就是取出a变量的地址。 p是一个指针变量,对于第二排可以这样解读:*表示p是一个指针变量,int表示p指向的是int型的变量。
2,指针变量的解引用
存入变量的地址是为了为了能够找到相应的变量,这是就要用到解引用操作符:*,与刚才的指针变量的标志*不同,这里的*是对紧随其后的指针变量进行解引用,即通过地址找到对应的值,用法如下:
第三排的*是解引用操作符,*是指针变量,*p就是指针(地址)p所对应的值,也就是说,*p等价于a,这一点从下图可以看出:
a本来是0,通过*p间接的将其改为了1
3,const修饰指针变量
在学习指针之前,const修饰一个变量后被修饰的变量就不能被直接修改了,但通过解引用操作符来修改变量的置属于间接修改(也叫间接访问)。那这样的话是否constant是否就失去意义了呢,其实并非如此,因为const不仅可以修饰变量,也能修饰指针变量,下面逐一介绍:
1,const在*之前:这样做可以使指针指向的值本身不能被改变,但可以修改指向的地址()
可以从图中看到在const修饰后,如果想通过地址来简介修改变量a的值的话,程序还没运行就显示错误了;(const 只要在*之前就行,在指针变量类型的前面或后面都可以)
2,const在*之后:这样做可以使指针变量的指向(指向的地址)不能改变,但可以修改指向的地址所对应的值。
从图中可以看到,存放了变量a的地址的指针变量p如果让它存放变量b的地址(即指向变量b),就会报错。
3,用两个const分别置于*的前后:这样做就是兼顾上面两种const的功能,是指针的指向和指向的值都不可修改。
指针变量的类型及意义
首先说明,不同类型指针变量的是相等的,大小由所处的环境决定,如下图:
32位环境
64位环境
那么,既然不同类型指针变量的大小相等,是不是说分这么多类型存储是多余的呢,其实并非如此,请看下文:
1,对解引用操作符的影响:
解引用操作符在对指针变量解引用的时候会根据类型的不同访问不同大小的内存,即类型决定的解引用操作符能访问多少个字节
- char * : 访问1个字节
- int * : 访问4个字节
- float * : 访问4个字节
- double * : 访问8个字节
影响的具体体现:
可以看到通过整形指针将a的四个字节都改为了0。
可以看到字符型指针值更改了一个字节的内存。
2,对指针加减1的影响
从图中不难看出,整形指针加1后地址增加了4,而字符型指针加1后地址增加了1;
3,void*指针
void*指针是较为特殊的指针类型,它代表指针变量没有某个具体的类型,可以通过强制类型转换来接收各种类型的地址(这一点在之后有非常之大的作用)。
- void*的指针不能直接进行解引用
指针的运算
1,指针加减整数:
这一点刚才提到过,就是根据指针类型的不同向前或向后移动相应的字节,此处不再赘述
2,指针减指针:
首先请注意,指针和指针间只有相减,没有相加。为了方便解释,用数组来理解
arr表示数组首元素的地址,指向arr[0]即元素1;arr+3表示首元素地址+3,因为数组元素是int型,所以向后移动3*4个字节,到了arr[3],即元素4.不难看出,两者地址间有三个元素,所以相减的结果是3. arr-(arr+4)的结果同理。
野指针
野指针指向的空间不确定或者对内存越界访问的指针
可能的成因:
- 指针变量未初始化
- 进行指针加减时不加以限制导致越界访问
规避野指针的一般方法:
- 每次创建指针变量时记得初始化,如果不知道要指向谁,可以赋值为空指针NULL
- 对指针进行运算时要时刻注意程序申请了多大的内存空间,避免越界
- 某个指针不再使用后要将其赋值为NULL
assert断言
在使用指针判断指针是否有效就可以用到asset(),包含头文件<assert.h>就可以使用,它用于对括号内的内容进行判断,为真则继续执行程序,为假则在控制台显示错误的内容的行号。
对于指针变量的判断就可以这样使用:
可见对指针有效性的判断时非常便利的
指针的基本使用方法
strlen的模拟实现
strlen函数用于判断字符串\0之前的字符个数
arr表示数组首元素的地址,arr+1则向后访问一个字节的内容(指针变量的类型为char*),*arr则是arr指向的内容,当arr的地址指向字符串末尾隐藏的'\0'时,循环结束