指针的进阶
本章重点:
- 字符指针
- 数组指针
- 指针数组
- 数组传参和指针传参
- 函数指针
- 函数指针数组
- 指向函数指针数组的指针
- 回调函数
- 指针和数组面试题的解析
复习:
- 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
- 指针的大小是固定的4/8个字节(32位平台/64位平台)。
- 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
- 指针的运算。
1、字符指针(char*)
一般情况:
int main()
{
char ch=‘w’;字符变量ch
char *pc=&ch;将字符变量赋值给指针pc,pc的类型是char *
return 0;
}
还有一种情况是:
int main()
{
const char* pstr="hello world";//这里是把字符串“hello world”的首字符的地址放到指针pstr里
printf("%s\n",pstr);
return 0;
}
意思是把一个常量字符串的首字符 h 的地址存放到指针变量 pstr 中
下面通过代码加深理解:
编辑
结果是
编辑
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
2.指针数组(是一个数组,用来存放指针的)
指针数组的复习
int* arr 1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
对于指针数组的理解:
编辑
用法:使用指针数组打印二维数组:
编辑
3.数组指针(是指向数组的指针,存放数组的地址)
3.1 数组指针的定义
arr(数组名)是首元素的地址
&arr[0] 是首元素的地址
&arr 是数组的地址
int *p1[10]; 因为[ ]的优先级高于“ * ”,所以p1先于[ ]结合成为数组然后再和“ * ”结合成为指针
int (*p2)[10]; 用了( )强制将p2先于“ * ”结合成为指针再和[ ]结合成为(指针)数组
3.2 &数组名VS数组名
&arr 表示的是整个数组的地址,而不是数组首元素的地址。
感受一下
编辑
很明显arr+1(步长为4个字节)和&arr+1(步长为40个字节)跳过的指针步长不一样,数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40
3.3 数组指针的使用
编辑
数组名arr,表示首元素的地址, 但是二维数组的首元素是二维数组的第一行所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址可以数组指针来接收
对于上述函数print2中打印的对象,可能还有些模糊,那接下来就再次对其进行解读:
p指的是首元素的地址(也就是arr当中的第一行)加一个" * "(*p)就是arr数组(此时的arr是一个一维数组,因为我们传过来的参数就是首元素的地址,也就是把arr[3][4]看成一个一维数组,即忽略了列相当于arr[3]后面的那个[4]就相当于固定了的一个常量,所以每次跳过一个步长,就会是一个行的所有元素)的首元素,相当于数组里面的arr[i]((*p)+ i )中的i就是指每行,此时这个((*p)+ i )再加上j就是每行里面的每个元素了,也就是二维数组里的列(此时指的是地址)对其整体解引用就是元素本身了,相当于数组里面的arr[i][j]。
让我们再来对数组加深下理解:
int arr[5]; arr是一个数组,数组里面有5个元素,每个元素的类型是int
int *parr1[10]; parr1先于[ ]结合成为数组,然后数组parr1[10]与“ * ”结合成为指针,这是一个指针数组,数组里面有10个元素, 指针的类型是int *
int (*parr2)[10]; parr2首先和“ * ”结合称为指针,然后(*parr2)和[10]结合成为数组,这是一个数组指针,指针指向数组,数组里有10个元素,每个元素的类型是int
int (*parr3[10])[5]; parr3[10]是一个整体说明数组parr3有10个元素,然后parr3[10]和“ * ”结合成为指针,(*parr3[10])再和[5]结合成为数组,这是一个数组指针,也是一个二维数组,parr3有10个元素,相当于10行,每行里面有5个元素,也就是5列,parr3这个有10个元素的数组指针,指向一个数组,这个被指向的数组里面有5个元素,每个元素的类型是int
4.数组参数、指针参数
4.1一维数组传参
编辑
4.2 二维数组的传参
编辑
4.3 一级指针传参
编辑
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
可以传一个(1)变量的地址(2)存放变量的一级指针的变量
编辑
4.4 二级指针的传参
编辑
当函数的参数为二级指针的时候,可以接收什么参数?
(1)一级指针的地址(2)二级指针变量本身(3)存放一级指针数组的数组名
5. 函数指针
函数也是有地址的,接下来让我们看一下函数的地址
编辑
这两个地址是函数test的地址
对于函数地址的保存:
编辑
接下来带大家看两个有趣的代码:
//代码1
((void ()())0)();
//代码2
void (signal(int , void()(int)))(int);
编辑
在此给大家再普及一点知识对于平时我们使用指针如int* p其中int *是指针类型p是指针变量的名称,所以当指针变量的类型去掉剩下的就是指针类型,然后再来看这两个代码。
编辑
signal是一个函数声明,signal的参数有两个,一个是int另一个是函数指针,该函数指针指向的函数参数是int返回类型是void
signal函数的返回类型也是一个函数指针,该函数的参数是int,返回类型是void
而对于以上的表达式可读性较差,我们对其进行简化处理
编辑
对于函数指针
编辑
6. 函数指针数组
编辑
函数指针数组的用途:转移表
编辑
7. 指向函数指针数组的指针
指向函数指针数组的指针是一个指针:指针指向一个数组,数组的元素都是函数指针
如何定义呢?
编辑
8. 回调函数
回调函数就是一个通过函数指针调用的函数:如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用它所指向的函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
qsort(quick sort) - 库函数 - 排序
MSDN上面对qsort函数的描述是这样的
编辑
头文件的引用是:include
编辑
演示一下sqort函数的实现:
编辑
对于void* 类型指针的介绍
编辑
使用回调函数,模拟实现qsort(采用冒泡函数的方式)