C指针总结
指针是 C 语言中的一大难点,本文会对指针的类型解析,指针的算术运算,指针的赋值取值,指针的应用场景等方面一步步探索指针的奥妙,揭开指针神秘的面纱。
识别各种各样的指针
单指针
- 单指针很好理解,就是一个指向内存空间地址的变量;
- 指针变量的大小取决于所用机子的寻址方式,与所指内容无关,一般 32 位系统为 4 个字节,64 位系统为 8 个字节。
单指针定义与使用的方式如下
1 | int a = 0; |
二级指针
- 二级指针(又称二重指针,emmm,随便怎么叫吧)就是指针的指针;
- 与普通的单指针相比仅仅是所指向内容上的不同。
二级指针定义与使用的方式如下
1 | int a = 0; |
既然有二级指针,当然有三级、四级、五级……就不一一分析了,而且实际应用中也不会涉及太多级的指针。
数组指针
数组指针又称行指针,即指向数组的指针。其定义与使用方式如下
1 | int arr[][2] = {1, 2, 3, 4}; |
指针数组与数组指针写法类似,其定义与使用方法如下
1 | int *arr[3] = {0}; |
- 数组指针本质是指针,其指向一个数组内存空间的首地址
- 指针数组本质是数组,数组中的元素是指针
可以通过向右优先结合的方式来判断复杂指针的类型,如
1 | // 从p开始向右,先与[]结合,可知是一个数组。 |
1 | // 从p开始向右,遇到右括号,先结合括号内的*,可知是一个指针 |
函数指针
指针也可指向函数,其定义与使用方法如下
1 | int fun() |
函数指针容易和返回值为指针的函数混淆
1 | int *fun(); |
同样可以用上述的向右优先结合的方式来一步步分析
总结
类型名 | 定义格式 |
---|---|
单指针 | int *p; |
二级指针 | int **p; |
数组指针 | int (*p)[2]; |
指针数组 | int *arr[2]; |
函数指针 | int (*p)(); |
指针函数 | int *func(); |
指针的算术运算
- 指针可以加上或减去一个整数;
- 此加减以存储单元为单位,取决于指针的基类型大小;
下面的程序可以看出指针进行算术运算时的变化
1 | int main() |
程序执行的结果为:
p 和 p+3 之间的内存地址相差 0xc 即 12个 = 3个int的大小
运算符 & 和 *
- 这里 & 是取地址运算符, * 是间接运算符。
- &a 的运算结果是一个指针,指针的类型是 a 的类型加个*,指针所
指向的类型是 a 的类型,指针所指向的地址就是 a 的地址。 - *p 的结果是 p 所指向的东西,它的类型是 p 指向的类型,它所占用的地址是 p 所指向的地址。
1 | int a = 0; |
指针与数组
数组的数组名其实可以看作一个指针。看下例:
1 | int arr[] = {1, 2, 3, 4}; |
- 数组的元素可以用 数组名+偏移量 的方式找到;
- 同样,一个指向数组首地址的指针也可以用 [] 进行访问。
指针的类型转换
指针的类型被强制转换后,其指向的地址不受影响,但其进行算术运算时的大小会发生改变。
1 | int main() |
程序运行的结果是
指针的应用场景
给函数传不定量的参数
典型的示例就是 main 函数的 argc 和 argv
下面是一个找最长字符串的命令的实现
1 | int main(int argc, char *argv[]) |
程序运行结果如下
给排序函数传入比较规则
以一个选择排序为例,客户程序员可以不需要修改 Sort 函数的内容,只需要改变传入的参数,就可以实现按不同的方式排列。
1 | // 插入排序函数 |
指针的安全使用
指针的使用要注意安全性
- 要清楚指针到底指向了哪里,不能超出程序所能使用的内存范围,常见的错误如数组越界,强制转换成更大空间的指针(int -> double)等。
- 指针使用完毕后要及时至空,以免造成野指针的问题。
- 动态分配的内存空间要记得释放,避免发生内存泄露。
- 不要给指针直接赋非0数值,除非你在搞贴近硬件的开发,并且清楚对应的地址代表着什么。