对指针的初步认知
这里主要就是简单总结一下在C初级阶段我们接触的指针
- 指针是对内存地址的编号,用来存放地址,地址唯一标识一块内存空间。
- 指针的大小是固定的4或8个字节(32位平台/64位平台)。
32位/64位指的是计算机CPU中通用寄存器单位时间能处理的二进制的位数,以32位平台为例,会产生32根地址线,每根地址线会有0和1两种可能,32个0或1就是32个bit,也就是4个byte。
- 指针也是有类型的,指针的类型决定了指针±整数的步长,指针解引用操作的权限(操作几个字节)。
- 还有关于一些指针简单的运算(指针-指针=两指针间的元素个数)。
指针可不止这些内容欧,还有更多的知识等着我们去学习,接下来让我们一起了解一下指针的高级主题。
对指针的深度剖析
字符指针
在指针的类型中我们知道有一种指针类型为字符指针char*
一般使用:
char ch = 'w';
char *pc = &ch;
*pc = 'w';
还有一种使用方法:
char* pstr = "hello world";
注意:这段代码的意思是把这一个常量字符串的首字符地址存放到指针变量pstr中
让我们做一个小小的练习:
//数组
char str1[] = "hello world";
char str2[] = "hello world";
//指针
char *str3[] = "hello world";
char *str4[] = "hello world";
str1 == str2(错误)
用相同常量字符串初始化不同数组时,就会开辟不同的内存块,所以str1 == str2错误。
str3 == str4(正确)
指针指向同一字符串的时候,实际指向同一内存。
指针数组 VS 数组指针
指针数组是数组还是指针?数组指针是指针还是数组?他们有什么区别呢?你能很好的区分它们吗?让我们一起来看看吧!
指针数组
指针数组是一个存放指针的 数组!数组!数组!它的每个元素都为指针,用来存放指针。
int* arr1[10],char* arr[20],char** arr[30]
数组指针
数组指针是一个指向数组的 指针! 指针!指针!它指向数组。
int (*p)[10]
:[ ]的优先级高于✳,所以必须加上()使 p先和 ✳结合,p为一个指针变量,指向的是一个大小为10的整型数组。
数组指针的使用
既然数组指针指向的是数组,那么数组指针中存放的应该是数组的地址
。
话不多说我们看代码:
void test(int (*arr)[5])
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
显而易见,数组指针用于接收二维数组的传参,因为二维数组的首元素为第一行(一维数组),数组指针来接收这个一维数组的地址。
了解了指针数组和数组指针我们来一起看看下面代码的意思
int (*p[10])[5]
p先和[ ]结合表示数组,这个数组有10个元素,每个元素的类型为int (*)[5],即每个元素的类型为数组指针,int (*p[10])[5] 表示p数组里有10个元素,每个元素的类型都是数组指针,数组指针指向数组的地址,被指向的这个数组里有5个整型元素。
即为存放数组指针的数组。
数组名 VS &数组名
int arr[10];
虽然arr和&arr的值是一样的,但是意义不一样。
arr:数组首元素的地址,arr+1跳过该指针类型的大小。
&arr:数组的地址,&arr+1跳过整个数组的大小。
数组参数
在实际问题中我们有时会把数组传给函数,那么函数的参数因该如何设计呢?
一维数组传参
arr1中首元素地址是 int *
arr2中首元素地址是 int **
void test1(int *arr)
void test1(int arr[10])
void test1(int arr[])
void test2(int **arr)
void test2(int *arr[20])
int main()
{
int arr1[10] = {0};
int *arr2[20] = {0};
test1(arr1);
test2(arr2);
}
二维数组传参
上面我们已经说到,二维数组传参用数组指针接收。
void test(int (*arr)[5]);
void test(int arr[][5]);
int main()
{
int arr[3][5] = {0};
test(arr);
}
sizeof(arr) = 3×5×4 = 60,sizeof(数组名)求数组的大小
sizeof(arr+1) = 4,arr为首元素地址,类型为数组指针int (*)[5]
sizeof(arr[2]) =4×5= 20,二维数组第三行 为一维数组,类型int*
函数指针
经过上面对数组指针的了解,我们类比学习函数指针,函数指针是一个指向函数的指针!指针!指针!
void test()
{
printf("welcome");
}
int main()
{
printf("%p\n",test);
printf("%p\n",&test);
return 0;
}
输出:
013211DB
013211DB
这段代码输出的是两个地址,这连个地址是test函数的地址,我们要向将函数的地址保存起来,怎么保存?
首先能储存地址,就要求得是一个指针
即void (*pfun)()这个函数指针来存放函数的地址。
pfun1先和*结合,说明它是一个指针,指针指向的是一个函数,指向的函数返回值是void,无参数。
如果想调用一个函数: 1.函数指针解引用(*pfun)2.用函数名直接调用(test())
函数指针数组
顾名思义,函数指针数组是一个数组,数组中每个元素都是一个函数指针。这个数组中存储了函数的地址。
函数指针数组的定义int (*parr[10])();
parr先和[ ]结合,说明parr是一个数组,数组的内容是什么的呢?是int(*)()类型的函数指针。
用途:转移表
这里函数指针数组中存储的是 add,sub,mul,div相对应的加减乘除函数的指针
int(*p[5])(int x,int y) = {0,add,sub,mul,div}
调用时
ret = (*p[input])(x,y);
这样做可以大大的减少代码冗余,不用switch case语句那样繁琐。
指向函数指针数组的指针
指向函数指针数组的指针,首先它是一个指针,指向一个数组,数组的每个元素都是一个函数指针。
函数指针:
void (*pfun)(const char*) = test;
函数指针数组:
void (*pfunArr[5])(const char* str)
指向函数指针数组的指针:
void (*(*ppfunArr)[10])(const char*) = &pfunArr
回调函数
回调函数,回调回调就是经过这个函数之后,到了某个特定的条件返回去再调用它。普通函数就是顺序调用。
说的官方一点:回调函数就是一个通过函数指针调用的函数,把函数指针的地址作为参数传递给另一个函数。
函数的实现:
这里将函数int_cmp_less的地址传给函数bubble_sort,用函数指针来接收。
void bubble_sort(int*arr, int sz,int(*pf_com)(int,int))
这个指针就会被用来调用所指向的函数
if (pf_com(arr[i - 1],arr[i]) > 0)
函数的构建:
int int_cmp_less(int a, int b){//a小于b返回大于0
return b - a;
}
main函数中:
bubble_sort(arr, sizeof(arr) / sizeof(arr[0]),int_cmp_less);
到这里我们的Blog就结束了,经过这几个主题的学习和练习,相信我们对指针有了更深层次的理解,在C和C++中指针这个主题是永远不会褪色的,只有对指针有了更深入的理解,我们才能对C和C++语言有更深的理解。