1、指针是什么
指针就是地址,平常说的指针通常指的是指针变量,指针变量是来存放内存地址的变量。
一字节对应一个地址
在32位平台下,一个指针的大小是4字节
在64位平台下,一个指针的大小是8字节
2、指针和指针类型
指针也是有类型的,但是指针的类型改变的并不是指针的大小(在对应平台下,指针的大小是不变的)
那指针类型的意义是什么呢?
1.指针+-整数
通过计算我们可以看到,pc和pi其实并无差别因为都是首字节的地址,但是+1之后,跳过的字节并不相同
总结:指针的类型决定了指针向前或者向后走一步有多大(距离)
2.指针的解引用
通过计算可以看到,pi可以将n改为0x00 00 00 00
而pc只能将n改为0x00 22 33 44
也就是说int*指针解引用时可以访问4个字节
char*指针解引用访问1个字节
总结:指针的类型决定了,对指针解引用的时候有多大的访问权限(能操作几个字节)
3、野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
空指针 ≠ 野指针
空指针是NULL
1.野指针成因
a.指针未初始化
b.指针越界访问
c.指针指向的空间释放
2.如何规避野指针
a.指针的初始化
b.小心指针越界
c.指针指向空间释放,及时置NULL
d.避免返回局部变量的地址
返回的时候a变量的空间被释放没有权限再访问了 所以&a指向不明确 所有造成了野指针现象
p存放的是a的地址没错,但是a在出作用域时已经销毁了,就算找到那么地址存放的也有可能不是a,因为可能会被其他语句给占用
比如
这里打印的*p就不再是10了而是随机值
e.指针使用之前检查有效性
4、指针运算
1.指针+-整数
这里的第二行是创建了一个values数组
*vp++ = 0;可以拆成两步:
因为后置++所以先解引用*vp = 0;
然后再vp++;
*vp指向的是地址所对应的值,vp指向的是地址
画图理解:
其实上面类似下面这道题:
如何给数组每个元素赋值
arr是首元素地址直接赋给p
还可以这么写:
这里的p+i就相当于第i个元素(i从0开始),很巧妙
2.指针-指针
指针-指针得到的绝对值,是两个指针之间元素的个数
不是所有的指针都能相减,指向同一块空间的两个指针才能相减
这里可以用指针-指针来实现自定义的strlen函数
指针+指针没有意义
就相当于日期+日期没有意义一样
3.指针的关系运算
画图理解:
此时只是访问了第五个元素的地址,但是没有修改就不算越界
代码简化:
但是简化之后,会访问第0个元素前面一位的地址
我们应该避免这样写
5、指针和数组
数组:一组想同类型元素的集合
指针变量:是一个存放地址的变量
数组名和数组首元素的地址是一样的
(两种情况除外,数组章节讲过了)
那么就可以把首元素的地址存放到指针变量中去
int* p = arr;
既然知道了首元素的地址,那么我们就可以通过指针来访问每一个元素的地址
我们可以发现,&arr[i]和p+i的地址是一一对应的
那我们就可以通过指针来访问每一个数组元素了
即*(p+i),因为p里面存放的是首元素地址,那也可以写成*(arr+i)
那么我们就可以知道其实arr[i]等价于*(arr+i)
6、二级指针
指针变量也是一个变量,是变量就有地址,那么指针变量的地址存放在哪里?
这,就是二级指针。
写法:
int a = 10;
int* pa = &a;
int** ppa = &pa;
pa是一级指针,ppa是二级指针
对二级指针的运算:
- *ppa通过对ppa中存放的地址进行解引用,这样找到的是pa,*ppa其实访问的是pa
int b = 20;
*ppa = &b;//等价于pa = &b;
- **ppa先通过*ppa找到pa,然后对pa进行*pa操作即对pa解引用,找到的是a
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
不能理解为地址的地址!!!
7、指针数组
指针数组是存放指针的数组
这就是一个简单的指针数组
那我们可以想到,数组名是数组首元素的地址,那么我们可以通过指针数组来实现二维数组的创建。
parr[i][j]为啥不用解引用?
[ ]就相当于解引用,因为arr[i]等价于*(arr+i)
其实可以理解为用parr[i]代替了arr
parr是数组,所以parr[0]等价于数组第一个元素,即arr1,所以parr[0][j]可以理解为arr1[j]
第一个参数i是指针数组对应的arr数组下标,第二个参数j是对应arr数组中的元素下标。