概述
学好C语言,必须学好指针,本篇适合指针有个印象但是还没有弄懂弄透的同学,指针比较复杂的原因是因为:
- 二义性:*操作符有两种意思,申明和提取,这样让没经过大量练习的同学在阅读代码的时候需要判断,个人觉得这是C语言设计的缺陷,搞两个操作符不行么?
- 不直观:指针是变量,但是存放的是地址,拿到值需要提取,无形之中给大脑增加了一层理解记忆负担,简单的代码还好,代码复杂后理解难度增加。
- 套娃:指针可以一直套,也可以和各种类型组合,大脑不像计算机能放几层堆栈,不经过刻意练习,一般就2层,要想快速反应比较难。
先记住:
- 一个内存单元的容量一般是一个字节
- 32位系统存放地址的大小空间就是32位4字节,同理64位8字节
- 指针的计算是根据指针类型大小偏移的,比如int型指针,偏移就是4个字节
- []的优先级比*要高
指针类型
指针放的是地址,地址的大小是固定的,4或者8字节,指针类型是用于数据提取的时候,比如int型指针,在提取的时候就向后读4个字节。void*通用指针类型,只能打印指针地址,但是不能提取数据,也不能进行算术运算。
- 普通指针:int *p
- 二级指针:int **p
- 数组指针:int p[]
- 方法指针:void (*p)(int,int)
普通指针
int i = 6;
int *p = &i; //养成习惯:*跟着变量 代表他是指针变量。
用一个抽象内存图来理解:
p == 0x0004;
&p == 0x0001;
*p == 6;
可以把变量p理解成变量i的地址的别名
二级指针
int* *pp = &p;
pp == &p;
*pp == &a;
**pp == 6;
三级指针以此类推。
数组指针
数组是一个比较特殊的指针,不用定义*,是常量只能读不能写
int arrayP[5] = {}; // arrayP是数组指针
int (*p)[5] = &arrayP; // p也是数组指针
数组有两坑需要注意
- sizeOf(数组名),表示的是整个数组,不是首元素的大小,而是整个数组的大小
- &数组名,表示的是整个数组
其余的都只是代表首元素地址。
看看下面这些打印值你能否知道:
int a[] = {1, 2, 3, 4};
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a + 0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a + 1));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0] + 1));
指针数组:int *p[]
指针数组容易和数组指针混淆,指针数组是数组,里面的元素是指针
int a = 10;
int b = 20;
int c = 30;
int *p1 = &a;
int *p2 = &b;
int *p3 = &c;
int *ps[3] = {p1, p2, p3};
printf("%p\n", *ps);
printf("%d\n", **ps);
函数指针
函数指针就是函数的地址。一般用作回调
char* myFunction(char* dest,const char* src) {} // 目标函数定义
char* (*mf)(char*,const char*) = myFunction // 函数指针
函数指针数组
int (*parr[4])(int, int) = {};
特殊情况:野指针
1.指针未初始化
int *p; // 野指针
int *p = NULL;//正确的变量初始化
局部指针变量不初始化,就被初始化随机值
2. 指针越界访问,比如数组
3. 指针指向的内存空间释放也就是范围指针
比如方法调用,子方法返回了一个指针,但是指针所指向的局部变量已经释放。