目录
何为指针
地址大小
野指针
成因
如何规避
有效性
指针计算
+-整数
编辑
指针比较运算
指针-指针
编辑 数组与指针关系
二级指针
指针数组
应用
何为指针
指针就是指针变量,用来存放内存空间的一个编号,将指针比作我们宾馆的客人,内存空间就是一个个的房间,每一个指针变量对应一个地址空间。
int a = 0;
int *pa = &a;
pa记录了a的地址,int除了代表指向数据的类型,还可以表示访问字节的能力。
内存单元的单位是字节,而每个内存单元都有自己唯一的编号,而指针指向的是首元素第一个字节的地址,当我们 对指针进行加减操作时,也是按照数据所占的字节数来向前向后偏移的。
也就是说char类型指针加一跳过一个字节,int类型指针加一跳过四个字节.......
地址大小
x86
x64
野指针
定义:指向未定义的空间(随机的、不正确、没有明确限制的)
成因
1.指针未初始化
int* p;//p就是野指针*p = 20;
2.越界访问
int arr[10] = {0};int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;for (i = 0; i <= sz; i++){*p = i;p++;}
对数组越界访问未初始化的空间,出现了野指针。
3.指向空间释放
int* test()
{int num = 100;return #
}int main()
{int* p = test();*p = 200;return 0;
}
函数销毁后通过指针接收归还给内存空间的地址,并对其进行修改,这也是一种典型的野指针。
如何规避
- 指针初始化(可初始化为NULL)
- 小心指针越界
- 指向空间释放(free)时,及时置NULL
- 避免返回局部变量(栈空间)的地址
- 指针使用前检查有效性
有效性
int* p = NULL;if (p != NULL)//不为空再使用{printf("%d\n", *p);}
指针计算
+-整数
指针接收一个数组实际接收的是它首元素的地址,而数组名等价于首元素地址,
指针接收一个数组实际接收的是它首元素的地址,而数组名等价于首元素地址,通过+-整数来实现向前向后的一个偏移,偏移量为该类型所占内存大小。
指针比较运算
#define N_VALUES 5 float arr[N_VALUES];float* vp;for (vp = arr; vp < &arr[N_VALUES];)//从前往后{*vp++ = 0;//++优先级高}
修改(不推荐):
#define N_VALUES 5
float arr[N_VALUES];
float* vp;
for (vp = &arr[N_VALUES-1]; vp >= &arr[N_VALUES];vp--)//从后往前{*vp = 0;}
第一种方案是与数组后面的指针进行比较,然后依次赋值,第二种方案是从后往前比较,在完成首元素赋值后对前一个地址进行比较不符合条件退出循环。
规定:允许指向数组元素的指针与指向数组最后一个元素后面的内存空间比较大小,不允许与指向数组首元素前的内存空间比较。所以前者是规范写法。
指针-指针
两个指针的相减不是简单的内存编号相减,而是指向同一块连续空间的同类型指针(地址)相减,所得结果为二者相差的元素个数(偏移量)。
我们可以在模拟实现strlen函数的时候利用这点得到字符的个数:
数组与指针关系
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);/*for (i = 0; i < sz; i++){printf("%d ", *p);p++;}*/for (i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}
观察上面的代码,发现数组和指针就是一个模子刻出来的一样,实际上它们是有区别的。
不同:数组是一块连续的空间,用于存放各种数据,它的大小取决于元素个数,而指针是一个变量,用于存放地址,大小为4\8字节。
联系:数组名是地址(指针),数组把首元素地址交给指针后,可以用指针来管理数组。
二级指针
和一级指针一样,二级指针也是用于存放地址,它存放的是一级指针的地址。
蓝色圆圈代表指针存放地址的那个变量的数据类型,*p代表了指针。如果想通过它访问a的值,只需进行两次解引用即可。
指针数组
顾名思义,存放指针的数组就叫做指针数组。
int* arr3[5];//存放整型指针的数组
char* arr4[6];//存放字符指针的数组
应用
我们可以用多个一维数组模拟二维数组来使用指针数组。
这里我们成功模拟出了二维数组,但它和实际的二维数组还是有些差别,比如二维数组是连续的空间,而指针数组的每个元素是不连续的,但毕竟是模拟,目的是为了了解指针数组的具体功能。