1.指针
指针就是地址,地址就是指针。
指针是内存单元的一个编号。
通常 int *p=&a;
这里的p其实就是指针变量,代表着a在内存中的地址。
内存可以比喻乘是一个一个连续的房间组成的,而地址就是房间的编号,每个房间的大小为1字节。
房间越多,地址范围越大,通常32位机器,可以支持2^32个房间编号,如果想用二进制能表达这么大的数,需要32个二进制位表示,因此地址所占空间大小为4字节。同理,64位机器上,地址所占空间大小为8字节。
2.指针和指针类型
指针也有类型,然而指针的类型又有什么特别的意义呢?
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
例如:char * pc=NULL;
pc代表变量名.
*代表这个变量是指针类型.
char代表pc存放的是字符类型的地址
指针类型决定了指针在解引用的时候,能够访问几个字节的内存空间
当执行到第8行的时候,char类型的指针,仅仅修改了1字节的内容。
当指针为short类型时候,修改了2字节的内容。
指针类型的多样性方便我们更加灵活的根据需求访问内存,想用什么样的类型访问内存,就用什么样的指针。指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
2.1 指针 + -整数
看下面代码:
可以观察到:char类型指针pc,pc+1的结果就是地址+1(步长为字符类型大小);
int类型指针pi,pi+1的结果就是地址+4(步长为整型大小);
指针的类型,决定了指针向前或向后走一步多大距离
3.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1野指针产生原因
- 指针未初始化
2. 指针越界访问
3. 指针指向的空间释放(没有及时将指针置为NULL)
3.2 如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放,及时置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
4. 指针运算
4.1指针+ -整数
指针+ -整数的实质就是运用指针来访问或修改变量的数据。
int my_strlen(char * str) { int count = 0; while (*str != '\0') { count++; //str++;//指针+整数 str = str + 1; } return count; }
例如求字符串长度的函数,就是利用了该知识点。
4.2 指针-指针
指针-指针得到的是指针之间的元素个数
4.3指针关系运算
for(vp = &values[5]; vp > &values[0];) { *--vp = 0; } //代码简化,修改为: for(vp = &values[4]; vp >= &values[0];vp--) { *vp = 0; }
但我们还是更推崇第一种判断方法:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
5.指针和数组
指针就是指针,数组就是数组
二者的联系是:
- 指针可以通过解引用访问数组的各个元素
- 数组名,其实就是数组的首地址
下面这段代码展示了用指针访问数组元素,和数组本身访问其实是一样的。
#include <stdio.h> int main() { int arr[] = {1,2,3,4,5,6,7,8,9,0}; int *p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr)/sizeof(arr[0]); for(i=0; i<sz; i++) { //数组的地址和指针访问出的地址区别 printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p+i); } return 0; }
6. 二级指针
指针也是变量,那么指针变量存放在哪里。
二级指针是存放指针变量地址的指针。
如下图所示:
可以看出,用二级指针同样能访问到变量a.
6.指针数组
指针数组本质上就是存放指针的数组,它还是数组。
形式为:int *arr[3];(整型指针数组)
如下简例,介绍了指针数组的简单用法:
7 .字符指针
指向字符的指针
注:1.这里的str1,str2是两个不同的数组,因此需要开辟两个不同的内存空间。(地址不一样),str1,str2是数组名,指向数组首元素的地址。
2.str3,str4存放的是字符串常量,字符串常量被存放在静态区,只有一份数据。因为常量不可改变,因此str3,str4指向同一位置,指向字符串首元素地址。
8. 数组指针
8.1数组指针基本定义
数组指针本质上是指针,指向数组的指针。
基本格式:整型指针:int * p; 数组指针:int (*p)[10];
因为[]优先级高于*,因此要把 *给括起来。这样先与*结合,就是指针,指针指向的是整型数组。
8.2 数组名和 &数组名的区别
- arr和&arr值一样,都是地址。
- arr指向的是数组首元素的地址,&arr是整个数组的地址。
- arr+1:前者是跨越一个变量的大小,后者跨越整个地址的大小。
8.3 数组指针的使用(简单例子)
注:二维数组的数组名,实际上是第一行的一维数组的地址。二维数组名+1(arr+1),得到的为第二行数组的地址。
第i行第j个元素*(*(arr+i)+j)。
9.函数指针
指向函数的指针
由下面的例子可以看出,函数是有地址的。
格式: void (*p)().
(与数组指针一个意思)
具体使用格式如下:
10.函数指针数组
存放函数指针的数组
格式: int (*parr1[10])();
解释:parr1首先和[]结合,说明parr1是数组,接着再和int(*)()结合,说明类型是函数指针。
详细使用请看下面链接:
链接: 函数指针数组详解
11.指向函数指针数组的指针
直接看代码:
12.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
例如: qsort函数模拟与实现
以上是指针的一部分内容,后续还会继续补充,如有问题,恳请大佬指点💖