1.在指针之前——内存和地址
如果说一栋宿舍楼有一百个房间,每个房间都有一个门牌号,可以根据门牌号来找到对应的人。那么内存就是这栋宿舍楼,有很多块内存单元,地址就是这些内存单元的门牌号,有了地址我们就能进行访问。每一个内存单元能放八个比特位,就像是一个宿舍的四人间。而指针——C语言给地址起的新名字—— == 内存单元的编号 == 地址.
2.指针变量与地址
2.1取地址操作符(&)
回到C语言,在C语言中创建变量就相当于在向内存申请空间,比如。
上述代码就是向内存申请了四个字节的空间用于将9储存在a中。
而我们要得到a的地址则是要借助到取地址操作符—— & 。
其中&a得到的是分配的四个字节的地址里地址最小的那个,所以虽然整形变量分配了四个字节,但是我们知道了第一个字节的地址就能顺藤摸瓜访问到其余地址的数据。
2.2指针变量
我们得到的地址则是存储在指针变量中,如下展示如何创建
对p的类型int *分析,*表明p是一个指针变量,int表示p这个指针变量指向的类型是int.
类似地,创建其他类型的指针变量同理,如,char* p。
2.3解引用操作符(*)
我们已经知道了如何取地址,但是我们需要使用地址内容的时候需要怎么做?
这时候就要用到解引用操作符——*。
这时我们可以将a打印出来看看结果。
可见通过解引用操作我们可以间接改变在a中的内容。
2.4指针变量的大小
要知道任何类型都有大小,那么指针变量的大小呢?
在64位的环境下打印,可见指针变量的大小是一样的,都为64字节;若是在32位环境下条件则是4字节。
3.指针变量的意义
3.1指针在强制类型转换条件下的解引用
看到上面的操作就有人要问了”为什么要通过指针来改变值,而不直接改变量啊“,那么我将举个栗子。
这里的*p=0改变了四个字节的内存中的内容。
当我们把p的类型强制转换为char*后再次解引用可以发现,内存只有一个字节被改变。
综上,每个类型的指针所有的权限是不一样的,可以根据需要来选择使用的指针类型。
3.2指针的加减法
如图,
在32位的环境下可以看出char*类型的指针+1,地址增加了一个字节,而int*类型的指针+1,地址增加了4个字节。
说明指针的类型决定了指针向后跳的距离。
3.3 void* 指针类型
这时候又有人要问了”这么多指针,有没有能接受任一类型的指针口牙“,有的兄弟,有的。
void*类型的指针(也叫泛型指针),这种指针能够接受任何类型,但是有局限性——不能直接进行指针的+-整数和解引用操作。
实际上就是接受的时候不用强制类型转换,但是用的时候需要强制类型转换。说明强制类型转换是守恒的(bushi
这时候又有人要问了“那这玩意怎么用啊“,实际上此事在一个排序函数qsort上亦有记载,但由于篇幅限制,请听下回分解。
4.const修饰指针
4.1 const修饰变量
如图, 修饰变量时,变量的值不能再次修改。
4.2 const修饰指针变量
4.2.1当const修饰在 * 前时
此时对pi进行解引用,不能进行修改。
但是能够对pi中存储的地址进行修改。
4.2.2 const修饰在 * 后时
如图代码
此时对pi解引用能够修改。
这时尝试对pi的中的地址进行修改产生报错。
故而 const在*前不能修改*pi内容,在*后不能修改pi的内容。
5.指针的运算
指针的运算有三种:指针+-整数、指针-指针、指针的关系运算
5.1 指针+-整数
以数组来举例,已知数组在内存中是连续存放的,所以只要知道第一个元素就能找到后面所有元素。
此时利用指针成功将数组打印。
字符数组也同理
5.2 指针-指针
以实现strlen功能的函数为例子。
5.3 指针的关系运算
以指针的大小比较为例
6.野指针
野指针就是指针指向的位置是不可知的
6.1 野指针成因
6.1.1 指针未初始化
局部变量的指针未初始化,默认为随机值。
6.1.2 指针越界访问
当打印超过10个之后打印的都是随机值,即打印十次之后p变为野指针。
6.1.3指针指向的空间释放
这串代码看似正常运行,但是暗藏玄机,因为函数中的n在出函数的时候已经释放,p实际上指向的是已释放空间,所以是野指针。
6.2 规避野指针
6.2.1 指针初始化
当有一个野指针的时候,将其赋值为NULL,也就是空指针如图
而当指针不再使用时,可以及时将指针赋值为空指针。
6.2.2 小心指针越界
使用指针的时候不超过规定的空间,就好比是一个整形数组a[10],不能访问超过a[9]的内容。
6.2.3 避免返回局部变量的地址
参考6.1.3
7.指针的使用和传址调用
在之前就展示过一个指针使用的例子,即strlen的实现
以下再举一个两数交换的例子来演示传址调用
通过传地址的方式交换了a b的数字。
至此这一部分结束,但指针的内容将不止于此。