当前位置: 首页 > news >正文

指针的进阶2

六、函数指针数组

字符指针数组 - 存放字符指针的数组 char* arr[10]

整型指针数组 - 存放整型指针的数组 int* arr[10]

函数指针数组 - 存放函数指针的数组

void my_strlen()
{}
int main()
{
    //指针数组
    char* ch[5];

    int arr[10] = {0};
    //pa是是数组指针
    int (*pa)[10] = &arr;

    //pf是函数指针
    int (*pf)(const void*) = &my_strlen;

    //函数指针数组
    int (*pf[10])(const void*) = {&my_strlen};//pf的类型是int (*[10])(const void*)
    return 0;
}

解释pf 先和 [ ] 结合,说明 pf是数组,数组的内容是 int (*)() 类型的函数指针。

6.1转移表

函数指针数组的用途:转移表

举例:写一个计算器实现整数的加、减、乘、除

计算器的一般实现:

void menu()
{
    printf("********************\n");
    printf("***1. add  2. sub***\n");
    printf("***3. mul  4. div***\n");
    printf("***0. exit       ***\n");
    printf("********************\n");
}

int Add(int x,int y)
{
    return x + y;
}

int Sub(int x,int y)
{
    return x - y;
}

int Mui(int x,int y)
{
    return x * y;
}

int Div(int x,int y)
{
    return x / y;
}

int main()
{
    int input = 0;
    int x = 0;
    int y =0;
    int ret = 0;
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d",&input);
        switch(input)
        {
        case 1:
            printf("请输入两个操作数:>");
            scanf("%d %d",&x,&y);
            ret = Add(x,y);
            printf("%d\n",ret);
            break;
        case 2:
            printf("请输入两个操作数:>");
            scanf("%d %d",&x,&y);
            ret = Sub(x,y);
            printf("%d\n",ret);
            break;
        case 3:
            printf("请输入两个操作数:>");
            scanf("%d %d",&x,&y);
            ret = Mul(x,y);
            printf("%d\n",ret);
            break;
        case 4:
            printf("请输入两个操作数:>");
            scanf("%d %d",&x,&y);
            ret = Div(x,y);
            printf("%d\n",ret);
            break;
        case 0:
            printf("退出计算器\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    }while(input);
    return 0;
}

使用函数指针数组实现:

void menu()
{
    printf("********************\n");
    printf("***1. add  2. sub***\n");
    printf("***3. mul  4. div***\n");
    printf("***0. exit       ***\n");
    printf("********************\n");
}

int Add(int x,int y)
{
    return x + y;
}

int Sub(int x,int y)
{
    return x - y;
}

int Mui(int x,int y)
{
    return x * y;
}

int Div(int x,int y)
{
    return x / y;
}

//用函数指针数组存放上述函数的地址
//转移表
int (*pf[5])(int,int) = {NULL,Add,Sub,Mul,Div};//第一个元素是NULL(0),是为了使得函数与菜单中的选项数字一致

int main()
{
    int input = 0;
    int x = 0;
    int y =0;
    int ret = 0;
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d",&input);
        if(input == 0)
        {
            printf("退出计算器\n");
            break;
        }
        else if(input >= 1 && innput <=4)
        {
            printf("请输入两个操作数:>");
            scanf("%d %d",&x,&y);
            ret = pf[input](x,y);
            printf("%d\n",ret);
        }
        else
        {
            printf("选择错误\n");
        }

    }while(input);    
    return 0;
}

七、指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针

int main()
{
    //数组指针
    int arr[10];
    int (*pa)[10] = &arr;

    //函数指针
    int (*pf)(int,int) = &test;

    //函数指针数组
    int (*pf[5])(int ,int) = {test};
    pf[0] = test;

    //ppf是指向函数指针数组的指针
    int (*(*ppf)[5])(int,int) = &pf//ppf的类型是int (*(*)[5])(int,int)

    return 0;
}

八、回调函数

回调函数就是⼀个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被用来调用其所指向的函数
时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

可以使用回调函数来实现计算器,使其更加简化:

void menu()
{
    printf("********************\n");
    printf("***1. add  2. sub***\n");
    printf("***3. mul  4. div***\n");
    printf("***0. exit       ***\n");
    printf("********************\n");
}

int Add(int x,int y)
{
    return x + y;
}

int Sub(int x,int y)
{
    return x - y;
}

int Mui(int x,int y)
{
    return x * y;
}

int Div(int x,int y)
{
    return x / y;
}

//以上都称为回调函数

void calc(int (*pf)(int ,int))
{
    int x = 0;
    int y =0;
    int ret = 0;
    printf("请输入两个操作数:>");
    scanf("%d %d",&x,&y);
    ret = pf(x,y);
    printf("%d\n",ret);
}

int main()
{
    int input = 0;
    
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d",&input);
        switch(input)
        {
        case 1:
            calc(Add);
            break;
        case 2:
            calc(Sub);
            break;
        case 3:
            calc(Mul);
            break;
        case 4:
            calc(Div);
            break;
        case 0:
            printf("退出计算器\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    }while(input);
    return 0;
}
把调用的函数的地址以参数的形式传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函数的功能

8.1qsort函数的使用

首先回顾一下冒泡排序:思想:两两相邻的元素进行比较

例如进行升序排序:

void bubble_sort(int arr[],int sz)
{
    //趟数
    int i = 0;
    for(i = 0;i < sz-1; i++)
    {
        //一趟冒泡排序的过程(一趟冒泡泡排序的对数是变化的)
        int j = 0;
        for(j = 0;j < sz-1-i; j++)
        {
            if(arr[j] > arr[j+1])
            {
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}
int main()
{
    //对数组进行排序,升序
    int arr[10] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    
    bubble_sort(arr,sz);
    //0 1 2 3 4 5 6 7 8 9
    //打印
    int i = 0;
    for(i = 0;i <sz; i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

冒泡排序是有缺点的:只能排序整数,结构体、浮点型等不可以

qsort函数的使用

库函数中有一个排序函数:qsort(快速排序)

void* base - 待排序的数组的起始位置(地址) 
size_t num - 数组元素个数
size_t size - 数组中每一个元素的大小(一个元素是几个字节)
int (*compar)(const void*, const void*) - 两个元素的比较函数(函数指针),第一个参数是进行比较的两个元素中第一个的地址,第二个参数是另一个元素地址
比较函数的参数是void*类型的原因:因为无法确定想要比较的类型是int、结构体等类型中的哪一种,所以设置成void(无具体类型)类型

int main()
{
	int a = 10;
	float f = 5.5f;
	int* p = &a;
	//p = &f;//如果把f的地址取出来赋给p,浮点型的地址赋给整型的指针,编译器会报警告:类型不兼容

	//如果写成void*的指针类型
	void* pp = &f;//void*的指针可以接收float类型的地址,且不会报警告
	pp = &a;//用void*的指针接收整型地址也不会警告
	//pp - 垃圾桶(可以接受任何类型的地址,因为pp是void*类型的指针变量,无具体类型)

	printf("%f\n", *pp);//不能直接打印,会报错,void*的指针不能直接解引用
	pp++;//进行++也不行,因为不知道具体类型,不知道具体加一步要走多远(大小不知道)

	return 0;
}

但是也有缺点:没有办法直接使用,因为void*的指针不能直接解引用,不知道具体类型,如果想要使用,要进行强制类型转换,就可以使用了

---------------------------------------------------------------------------------------------------------------------------------

进一步解析比较函数:当比较函数接收的两个元素进行比较时,p1>p2 - 返回大于0的数字

p1<p2 - 返回小于0的数字;p1=p2 - 等于0

前面我们说过要对数组进行升序排序,那么我们就可以根据以上的信息,转化为两个元素相减的结果,再根据此结果来判断是否需要进行交换 ,如果相减的结果大于0,说明p1>p2,要进行交换

qsort函数的使用需要包含头文件 #include <stdlib.h>

8.1.1使用qsort函数排序整型数据

#include <stdio.h>
#include <stdlib.h>

int cmp_int(const void* p1,const void* p2)
{
    return *(int*)p1 - *(int*)p2;//升序
    return *(int*)p2 - *(int*)p1;//降序
}
int main()
{
    //对数组进行排序,升序
    int arr[10] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    
    qsort(arr,sz,sizeof(arr[0]),cmp_int);
    
    int i = 0;
    for(i = 0;i <sz; i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

8.1.2使用qsort函数排序结构体数据

按照年龄排序:

#include <stdio.h>
#include <stdlib.h>

struct Stu
{
    char name[20];
    int age;
};

//按照学生的年龄来排序
int cmp_stu_by_age(const void* p1,const void* p2)
{
    return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

int main()
{
    struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};
    int sz = sizeof(s) / sizeof(s[0]);
    
    qsort(s,sz,sizeof(s[0]),cmp_stu_by_age);
    
    int i = 0;
    for(i = 0;i < sz; i++)
    {
        printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);
    }
    return 0;
}

按照姓名排序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Stu
{
    char name[20];
    int age;
};

//按照学生的姓名来排序
int cmp_stu_by_name(const void* p1,const void* p2)
{
    return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
}

int main()
{
    struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};
    int sz = sizeof(s) / sizeof(s[0]);
    
    qsort(s,sz,sizeof(s[0]),cmp_stu_by_name);
    
    int i = 0;
    for(i = 0;i < sz; i++)
    {
        printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);
    }
    return 0;
}

字符串是按照字典序排序的:
字典序是一种基于字母或字符顺序的排列方式,类似于字典中单词的排列规则。在C语言中,常用于字符串或字符数组的比较和排序。

字典序的核心规则‌
逐字符比较‌从左到右逐个比较字符的ASCII。若字符不同,ASCII值较小的字符所在的字符串更小。若字符相同,继续比较下一个字符。
长度优先‌:若所有字符相同,则较短的字符串更小。
‌示例‌:"apple" < "banana"(因为 'a' < 'b')
"app" < "apple"(前三个字符相同,但前者更短)
‌C语言中的字典序实现‌:
使用标准库函数 strcmp
strcmp 是C语言标准库(<string.h>)中用于比较字符串的函数,按字典序返回结果:

‌返回值‌:
< 0:第一个字符串小于第二个。
= 0:两个字符串相等。
> 0:第一个字符串大于第二个。

注意事项:字符编码默认基于ASCII值,大写字母(A-Z,ASCII 65~90)比小写字母(a-z,ASCII 97~122)小

字符串以终止符\0结尾,比较时会自动处理

8.2qsort函数的模拟实现

根据qsort函数,改造冒泡排序,使得这个函数可以排序任意指定的数组(以冒泡排序的思维实现qsort函数)

8.2.1排序整型数组

//使用我们自己写的bubble_sort函数排序整型数组

void Swap(char* a,char* b,int size)
{
    int i = 0;
    for(i = 0;i < size; i++)
    {
        char tmp = *a;
        *a = *b;
        *b = tmp;
        a++;
        b++;
    }
}
int cmp_int(const void* p1,const void* p2)
{
    return *(int*)p1 - *(int*)p2;
}
void bubble_sort(void* base,size_t num,size_t size,int (*cmp)(const void* p1,const void* p2))
{
    //趟数
    size_t i = 0;
    for(i = 0;i < num-1; i++)
    {
        size_t j = 0;
        for(j = 0;j < num-1-i; j++)
        {
            if(cmp((char*)base+j*size,(char*)base+(j+1)*size) > 0)
            {
                Swap((char*)base+j*size,(char*)base+(j+1)*size,size);//实现两个元素的比较
            }
        }
    }
}
int main()
{
    int arr[10] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    
    bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);

    int i = 0;
    for(i = 0;i < sz; i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

8.2.2排序结构体数组

按照年龄排序:

struct Stu
{
    char name[20];
    int age;
};
void Swap(char* a,char* b,int size)
{
    int i = 0;
    for (i = 0; i < size; i++)
	{
		char tmp = *a;
		*a = *b;
		*b = tmp;
		a++;
		b++;
	}
}
//按照年龄排序
int cmp_stu_by_age(const void* p1,const void* p2)
{
    return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
void bubble_sort(void* base,size_t num,size_t size,int (*cmp)(const void* p1,const void* p2))
{
    //趟数
    int i = 0;
    for(i = 0;i < num -1; i++)
    {
        int j = 0;
        for(j = 0;j < num-1-i; j++)
        {
            if(cmp((char*)base+j*size,(char*)base+(j+1)*size) > 0)
            {
                Swap(cmp((char*)base+j*size,(char*)base+(j+1)*size,size);
            }
        }
    }
}
int main()
{
   struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};
   int sz = sizeof(s) / sizeof(s[0]);
    
   bubble_sort(s,sz,sizeof(s[0]),cmp_stu_by_age);
    
   int i = 0;
   for(i = 0;i < sz; i++)
   {
       printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);
   }
   return 0;
}

按照姓名排序:

struct Stu
{
    char name[20];
    int age;
};
void Swap(char* a,char* b,int size)
{
    int i = 0;
    for (i = 0; i < size; i++)
	{
		char tmp = *a;
		*a = *b;
		*b = tmp;
		a++;
		b++;
	}
}
//按照姓名排序
int cmp_stu_by_name(const void* p1,const void* p2)
{
    return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
}
void bubble_sort(void* base,size_t num,size_t size,int (*cmp)(const void* p1,const void* p2))
{
    //趟数
    int i = 0;
    for(i = 0;i < num -1; i++)
    {
        int j = 0;
        for(j = 0;j < num-1-i; j++)
        {
            if(cmp((char*)base+j*size,(char*)base+(j+1)*size) > 0)
            {
                Swap(cmp((char*)base+j*size,(char*)base+(j+1)*size,size);
            }
        }
    }
}
int main()
{
   struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};
   int sz = sizeof(s) / sizeof(s[0]);
    
   bubble_sort(s,sz,sizeof(s[0]),cmp_stu_by_name);
    
   int i = 0;
   for(i = 0;i < sz; i++)
   {
       printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);
   }
   return 0;
}

相关文章:

  • ModelSim联合仿真
  • spring cloud微服务API网关详解及各种解决方案详解
  • SAP系统客户可回收包材库存管理
  • 自动驾驶---自动驾驶端到端的一般形态
  • 第五篇:Python面向对象编程(OOP)深度教程
  • 关于 微服务负载均衡 的详细说明,涵盖主流框架/解决方案的对比、核心功能、配置示例及总结表格
  • OracleLinuxR5U5系统重启后启动数据库oracle23ai
  • 【前端小技巧】实现详情页滚动位置记忆,提升用户体验
  • Vue接口平台学习六——接口列表及部分调试页面
  • asm汇编语言源代码之-获取环境变量
  • 【c语言】结构体习题
  • Vitis: 使用自定义IP时 Makefile错误 导致编译报错
  • Spring AI使用tool Calling和MCP
  • spring cloud alibaba 的服务治理框架(服务注册与发现)Nacos 使用详解
  • 微信小程序开发常用语法和api
  • 基本的DOS命令
  • 程序化广告行业(80/89):近年发展动态与技术标准演进
  • dolphinscheduler创建文件夹显示存储未启用的问题--已解决
  • Windows for Redis 后台服务运行
  • 驱动学习专栏--写在前面
  • 成都网站建设推广/重庆网站建设公司
  • 经营性网站icp/网址大全123
  • dw设计个人网页/百度seo点击器
  • 网站建设销售销售流程图/灰色词排名代做
  • 打开网站说建设中是什么问题/网络销售平台
  • 深圳市网站制作公司/常德网站建设公司