目录
回顾
字符指针
char ch = 'a';
char* pc = &ch;
pc就是一个字符指针变量
char* p = "abcdef";
p也是一个字符指针变量, 指向字符a的地址
那么p的指向的地址就不能改了
但是仍然可以解引用使用*p, 将字符串中的内容改了
对此的解决办法是
const char* p = "abcdef";
使用const修饰指针p, 让其成为指针常量,通过*p不能改变内容, 作用是维护起来更加安全
数组指针
字符指针--本质是指向字符的指针--存储字符的地址
整形指针--本质是指向整形的指针--存储整形的地址
那么数组指针--本质是指针--存储数组的地址
对于数组名的理解:
数组名绝大部分情况下,数组名是数组首元素的地址,
但是有两个例外
1.sizeof(数组名), 数组名表示整个数组, 计算的是整个数组的大小, 单位是字节
2.&(数组名), 数组名也表示整个数组, 取出的是整个数组的地址
除此之外, 所有的数组名都是 数组首元素的地址!!!!
int arr[10];
int (*p)[10] = &arr;
把整个数组的地址都取出来给数组指针p[]
提一下, 二维数组传参的时候, 也就是第一行的地址,就可以使用数组指针来接收!
在这里可以顺便讲解一下数组指针和指针数组初学者如何快速分辨
我们知道运算符是有优先级的, ()>[]>*
当没有括号时
int* p[10]
这是p优先与运算符[]结合了,成为了数组, 因此是指针数组
当加上括号时
int (*p)[10]
因为有括号, p优先与*号结合了,成为了指针, 因此是数组指针
指针数组
本质是数组,存放指针的数组
char* arr[5];
这个就是存放字符指针的数组
int* arrr[5];
存放整形指针的数组
可以使用指针数组模拟实现二维数组的效果
数组传参和指针传参
数组传参的时候:
1.传递的是数组首元素的地址
1>一维数组传参,传递的是首元素的地址
2>二维数组传参,传递的是第一行的地址
2.数组传参的时候, 形式参数可以写成数组,也可以写成指针
void print(int arr[])
void print(int arr[10])
void print(int* arr)
以上形参等价
二维数组同理
int arr[3][5] = {0};
void test(int arr[3][5]);
void test(int arr[][5]);
void test(int (*p)[5]);
1.行可以省略, 列不能省略
2.形参使用数组指针
函数指针
函数指针的声明
&函数名 拿到的是函数的地址,函数名类似于数组,直接写数组名可以拿到数组地址, 直接写函数名也可以拿到函数地址
#include <stdio.h>
int Add(int a, int b){
return a+b;
}
int main(int argc, const char * argv[]) {
printf("%p\n", &Add);
printf("%p\n", Add);
int (*pf)(int, int) = &Add;
return 0;
}
运行结果
0x100003f10
0x100003f10
Program ended with exit code: 0
直接写函数名也可以拿到函数地址
int (*pf)(int, int) = &Add;
int (*pf)(int, int) = Add;
这就是函数指针的声明, 类型为int (*)(int, int) , 第一个int为返回类型,后面两个int为参数类型
此处函数地址赋给pf这个指针变量了
函数指针的使用
int (*pf)(int, int) = &Add;
int ret = pf(1,2);
printf("%d\n", ret);
3
Program ended with exit code: 0
函数指针数组
函数指针数组的声明
函数指针数组的核心词主语为 数组
数组是用来存放相同类型的多个元素
那么函数指针数组,就是用来存放同一个函数指针类型的数组,即
1.同为指针
2.同样的返回类型
3.同样的形式参数类型
#include <stdio.h>
int Add(int a, int b){
return a+b;
}
int Sub(int a, int b){
return a-b;
}
int main(int argc, const char * argv[]) {
int (*pf1)(int, int) = Add;
int (*pf2)(int, int) = Sub;
int (* pfArr[4])(int , int) = {Add, Sub};
return 0;
}
最里面是数组,因为主语是数组, 其次是指针,最外面的是函数形参类型和返回类型
函数指针数组的使用
先使用普通方式写一个计算器的代码
#include <stdio.h>
void menu(void){
printf("****1.加法****\n");
printf("****2.减法****\n");
printf("****3.乘法****\n");
printf("****4.除法****\n");
printf("****0.退出****\n");
}
int Add(int a, int b){
return a+b;
}
int Sub(int a, int b){
return a-b;
}
int Mul(int a, int b){
return a*b;
}
int Div(int a, int b){
return a/b;
}
int main(int argc, const char * argv[]) {
// int (* pfArr[4])(int , int) = {Add, Sub, Mul, Div};
int input;
int a, b;
int ret;
do {
menu();
scanf("%d", &input);
switch(input){
case 1:
printf("请输入>");
scanf("%d %d", &a, &b);
ret = Add(a, b);
printf("结果是%d\n", ret);
break;
case 2:
printf("请输入>");
scanf("%d %d", &a, &b);
ret = Sub(a, b);
printf("结果是%d\n", ret);
break;
case 3:
printf("请输入>");
scanf("%d %d", &a, &b);
ret = Mul(a, b);
printf("结果是%d\n", ret);
break;
case 4:
printf("请输入>");
scanf("%d %d", &a, &b);
ret = Div(a, b);
printf("结果是%d\n", ret);
break;
case 0:
printf("退出\n");
break;
default:
printf("请重新输入\n");
break;
}
} while (input);
return 0;
}
运行结果
****1.加法****
****2.减法****
****3.乘法****
****4.除法****
****0.退出****
1
请输入>4 4
结果是8
****1.加法****
****2.减法****
****3.乘法****
****4.除法****
****0.退出****
2
请输入>4 4
结果是0
****1.加法****
****2.减法****
****3.乘法****
****4.除法****
****0.退出****
3
请输入>4 4
结果是16
****1.加法****
****2.减法****
****3.乘法****
****4.除法****
****0.退出****
4
请输入>4 4
结果是1
****1.加法****
****2.减法****
****3.乘法****
****4.除法****
****0.退出****
0
退出
Program ended with exit code: 0
可以观察到,每一个cas语句中都重复写了以下三段代码,维护起来极其不美观, 这时候使用函数指针数组进行改造
首先
定义指针数组
int (* pfArr[4])(int , int) = {Add, Sub, Mul, Div};
但是这样输入0对应加法,这怎么办呢,可以将[0]位置为NULL(因为函数指针数组存储的是函数地址,简单来说就是指针, 因此置为NULL)
int (* pfArr[5])(int , int) = {NULL, Add, Sub, Mul, Div};
改后:
do {
menu();
scanf("%d", &input);
if(0 == input){
printf("退出\n");
}
else if (input>0 && input<5){
printf("请输入>");
scanf("%d %d", &a, &b);
ret = pfArr[input](a, b);
printf("结果是%d\n", ret);
}
else{
printf("请重新输入\n");
}
} while (input);
指向函数指针数组的指针
先类比一下:指向整形指针数组的指针
int a, b, c;
int* arr[] = {&a, &b, &c};
int* (*p)[3] = &arr;
这里p是一个指针, 指向整形指针数组的指针
那么指向函数指针数组的指针
int (*pfarr[4])(int ,int) = {add, sub, mul, Div};
int(* (*p)[4])(int, int) = &pfarr;
先定义函数指针数组
先数组, 再是指针, 最后函数类型
再定义指针,指针类型为 函数指针数组类型: int*表示p为指针,int (*)[4] (int, int)为函数指针数组类型
合并起来就是指向函数指针数组的指针
ps: 了这部分知识了解即可 为扩展知识
回调函数
还是计算器的代码
考虑如何封装这块代码 这块代码相似但不相同
需要使用回调函数
回调函数的定义
回调函数就是一个通过函数指针(函数地址)调用的函数,. 如果你把函数的指针(地址)作参数传递给另一个函数, 当这个指针被用来调用其所指向的函数是, 我们就说这是回调函数.
回调函数不是由该函数的实现方直接调用, 而是在特定的事件或条件发生时, 由另外的一方调用的, 用于对该事件或条件进行响应.
回调函数的声明与使用(举例说明)
void calc(int (*pf)(int ,int)){
int a, b;
printf("请输入>");
scanf("%d %d", &a, &b);
int ret = pf(a, b);
printf("结果是>%d\n", ret);
}
基于函数指针,在特定的事件或条件发生时, 由另外的一方调用的, 用于对该事件或条件进行响应.
如下,在switch对应的case语句中进行调用
do {
menu();
scanf("%d", &input);
switch(input){
case 1:
calc(Div);
break;
case 2:
calc(Div);
break;
case 3:
calc(Div);
break;
case 4:
calc(Div);
break;
case 0:
printf("退出\n");
break;
default:
printf("请重新输入\n");
break;
}
} while (input);
典型库中的回调函数qsort
底层使用的快速排序的方式, 对任意类型的数据进行排序
这个函数可以直接使用
可以对任意类型的数据进行排序,
以下是官网说明:
https://cplusplus.com/reference/cstdlib/qsort/?kw=qsort
void qsort (void* base, //需要排序的数组第一个元素的地址
size_t num, //待排序数字的数量
size_t size, //待排序数字的数字大小
int (*compar)(const void*,const void*) // 函数cmp指向了一个函数,这个函数是用来比较两个元素的
);
以上对官网的解释理解
对于compare函数 不同类型的数据要比较出大小, 方法是有差异的, 需要在这里自定义
对于void* 类型的指针, 不能进行解引用操作符, 也不能进行+=整数的操作
void* 类型的指针, 是用来存放任意类型数据的地址
char c = 'w';
void* pc = &c;
没有问题
char c = 'w';
int* pcc = &c;
但是把char字符的数据地址赋给int型指针会报警告
void*的指针就像一个垃圾桶,能够存放任意类型的数据类型的地址
因此compare函数参数为const void*类型指针
如果返回<0, 那么就把前者放在后者前面 p1 p2
如果返回=0, 那么不变
如果返回>0, 那么就把前者放在后者后面 p2 p1
示例1 排序数组
#include <stdio.h>
#include <stdlib.h>
int compare(const void* a, const void* b){
return *(int*)a-*(int*)b;
}
int main(int argc, const char * argv[]) {
int arr[] = {9,8,7,6,5,4,3,2,1};
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), compare);
for (int i=0; i<sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
1 2 3 4 5 6 7 8 9 Program ended with exit code: 0
示例2 排序结构体--根据年龄
struct Stu{
char name[20];
int age;
};
struct Stu student[] = {{"lisa", 10}, {"lucy", 20}, {"paci", 30}};
int compare(const void* a, const void* b){
return ((struct Stu*)a)->age - ((struct Stu*)a)->age;
}
int main(int argc, const char * argv[]) {
int sz = sizeof(student)/sizeof(student[0]);
qsort(student, sz, sizeof(student), compare);
return 0;
}
lisa 10
lucy 20
paci 30
示例3 排序结构体-根据姓名首字母ASCLL比较排序
使用库函数compare
https://cplusplus.com/reference/cstring/strcmp/?kw=strcmp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu{
char name[20];
int age;
};
struct Stu student[] = {{"aisa", 10}, {"ducy", 20}, {"faci", 30}};
int compare(const void* a, const void* b){
return strcmp(((struct Stu*)a)->name, ((struct Stu*)b)->name);
}
int main(int argc, const char * argv[]) {
int sz = sizeof(student)/sizeof(student[0]);
qsort(student, sz, sizeof(student[0]), compare);
for (int i=0; i<sz; i++) {
printf("%s %d\n", student[i].name, student[i].age);
}
return 0;
}
aisa 10
ducy 20
faci 30