首页 > 其他分享 >关于我、重生到500年前凭借C语言改变世界科技vlog.15——深入理解指针(4)

关于我、重生到500年前凭借C语言改变世界科技vlog.15——深入理解指针(4)

时间:2024-11-06 12:49:37浏览次数:4  
标签:元素 函数 int void vlog.15 qsort C语言 printf 500

文章目录

1.回调函数的介绍

回调函数就是一个通过函数指针调用的函数

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数

正如我们在上一篇 vlog 中讲到的转移表,就是经典的回调函数,回调函数不是函数本身自己实现的,而是在特定的事件或条件发生的时候由另一方调用,对该事件或条件进行响应

传送门:关于我、重生到500年前凭借C语言改变世界科技vlog.14——常见C语言算法

还是以一个简易的计算器做例子

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 x, y;
     int input = 1;
     int ret = 0;
     do
     {
         printf("*************************\n");
         printf("    1:add          2:sub \n");
         printf("    3:mul          4:div \n");
         printf("    0:exit               \n");
         printf("*************************\n");
         printf("请选择:");
         scanf("%d", &input);
         switch (input)
         {
         case 1:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = add(x, y);
              printf("ret = %d\n", ret);
              break;
         case 2:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = sub(x, y);
              printf("ret = %d\n", ret);
              break;
         case 3:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = mul(x, y);
              printf("ret = %d\n", ret);
              break;
         case 4:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = div(x, y);
              printf("ret = %d\n", ret);
              break;
         case 0:
              printf("退出程序\n");
              break;
        default:
              printf("选择错误\n");
              break;
         }
    } while (input);
    return 0;
}

每写一个计算方法都要写一种情况,不断地 scanf 输入,printf 输出,显得过于啰嗦

#include <stdio.h>
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;
}
void calc(int(*pf)(int, int))
{
	int ret = 0;
	int x, y;
	printf("输⼊操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("ret = %d\n", ret);
}
int main()
{
	int input = 1;
	do
	{

		printf("*************************\n");
		printf("    1:add        2:sub   \n");
		printf("    3:mul        4:div   \n");
	    printf("*************************\n");
		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;
}

把每种情况的函数统一由一个函数管理,把这些函数以参数的形式传给该函数,这些函数就被称为回调函数

值得注意的是,在上一篇 vlog 中的优化方案是使用函数指针数组,这里使用的是回调函数

2. qsort使用实例

2.1 qsort函数介绍

什么是qsort函数?

传送门:qsort-C++参考

在这里插入图片描述
qsort函数的作用:对数组的元素进行排序,对指向的数组的元素进行排序,每个元素字节长,使用函数确定顺序,此函数使用的排序算法通过调用指定的函数来比较元素对,并将指向它们的指针作为参数,该函数不返回任何值,但通过按照 定义对其元素重新排序来修改指向的数组的内容

包含头文件 #include <stdlib.h>, 其语法形式为:

void qsort (void* base, size_t num, size_t size,
int (compar)(const void,const void*));

base:指向要排序的数组的第一个对象的指针,转换为 .void*
num:指向的数组中的元素数,是无符号整型
size:数组中每个元素的大小(以字节为单位),是无符号整型
compare:指向比较两个元素的函数的指针,此函数被重复调用以比较两个元素

在这里插入图片描述
假设第一个指针为p1,第二个指针为p2,那么有以下结论:

1.如果p1指向的元素小于p2,则返回小于0的数字
2.如果二者相等,则返回0
3.如果p1指向的元素大于p2,则返回大于0的数字
4.默认排序为升序,若想降序以上结论反转即可

那么 qsort 函数是如何一个一个比较的呢?
只是单纯两个比较吗?需要加循环结构吗?

当qsort函数在执行排序过程中,每当需要比较两个数组元素以确定它们的相对顺序时,就会调用用户提供的这个比较函数

1.在划分步骤中,通常会选择一个基准元素(pivot),并通过设置两个指针(比如一个从数组开头,一个从数组结尾)来对数组进行划分,这两个指针的初始范围是从数组的起始地址和结束地址开始
2.例如,对于一个整数数组int arr[] = {1, 2, 3, 4,> 5},如果选择第一个元素作为基准元素,那么一个指针可能从&arr[0]开始(指向首元素),另一个指针可能从&arr[sizeof(arr) / sizeof(arr[0]) - 1]开始(指向尾元素)
3.然后这两个指针会根据与基准元素的比较结果(通过调用比较函数)进行移动,在移动过程中,它们的范围会不断变化,直到完成划分操作,使得数组被分成两部分,一部分元素小于基准元素,另一部分元素大于基准元素。此时这两个指针的最终范围就是划分后两部分数组的边界地址,比如一个指针可能停留在小于基准元素那部分数组的最后一个元素地址处,另一个指针可能停留在大于基准元素那部分数组的第一个元素地址处

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

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

    int int_cmp(const void * p1, const void * p2)
{
    return (*( int *)p1 - *(int *) p2);
}
int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
    {
        printf( "%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

强调 p1 和 p2 必须是 const void* 类型的指针

这里定义了int_cmp函数,qsort函数在对数组进行排序时,需要一个能比较数组元素大小关系的函数作为参数,int_cmp函数接受两个const void *类型的指针p1和p2,这种通用指针类型使得qsort函数可以处理各种类型数据的排序(虽然这里实际只用于整数数组),在函数内部,先将p1和p2这两个通用指针转换为int *类型指针,以便能解引用获取到对应的整数,然后通过计算这两个整数的差值并返回,返回值的正负情况决定了qsort函数对数组元素的排序顺序:返回值小于 0,表示p1所指向的整数小于p2所指向的整数,那么在排序结果中p1对应的元素会排在p2对应的元素之前;返回值大于 0,意味着p1所指向的整数大于p2所指向的整数,p1对应的元素会排在p2对应的元素之后;若返回值等于 0,则表明这两个整数相等,此时元素的相对顺序在排序中可保持原有顺序或按其他默认规则处理

2.3使用 qsort 排序结构数据

struct Stu //学⽣
{
    char name[20];//名字
    int age;//年龄
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{
    struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{
    struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
    test2();
    test3();
    return 0;
}

strcmp - 是库函数,是专门用来比较两个字符串的大小的

该函数也和整数排列类似,只不过指针类型有所区别

3. qsort的模拟实现

使用回调函数,模拟实现qsort,
qsost底层采用的是快速排序的方法,在这里我们使用更简单的冒泡排序的排序算法来模拟实现qsort函数,对快排想要了解更多的,在上一篇 vlog 讲到了冒泡排序

传送门:关于我、重生到500年前凭借C语言改变世界科技vlog.14——常见C语言算法

首先是要实现排序模拟,那么运用冒泡排序就行,然后对两个数进行对比:

void Swap(char* buf1, char* buf2, size_t width)
{
    int i = 0;
    char tmp = 0;
    for (i = 0; i < width; i++)
    {
        tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;

        buf1++;
        buf2++;
    }
}

void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{
    //趟数
    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])
            //比较两个元素
            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            {
                //交换两个元素
                Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
            }
        }
    }
}

由于不知道用户排序的数据类型,传过来的数组首元素地址我们必须使用void*指针接收,不能进行解引用,且数据类型不能传参的,那我们该怎么找到相邻元素比较呢?

因为不知道接收数据的类型所以我们用char* 来一个字节一个字节移动,同样对比两个数也是如此,这就保证了这个模拟的函数能够接受各种类型的数据

使用 void* 指针实现了对不同数据排序,这种编程也叫做泛型编程

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

标签:元素,函数,int,void,vlog.15,qsort,C语言,printf,500
From: https://blog.csdn.net/Zero_VPN/article/details/143564970

相关文章

  • c语言中获取数组的长度
     001、一维数组[root@PC1test1]#lstest.c[root@PC1test1]#cattest.c#include<stdio.h>intmain(void){intv1[5]={3,4,8};printf("lengthofv1is%d\n",sizeof(v1)/sizeof(v1[0]));return0;}[root@PC1tes......
  • C语言-feof函数
     ......
  • 【落羽的落羽 C语言篇】操作符、二进制·之其一:初识编码方式及位操作符
    文章目录一、操作符1.操作符的分类2.操作符的属性2.1优先级2.2结合性二、二进制1.原码、反码、补码2.位操作符2.1左移操作符<<2.2右移操作符>>2.3&|^~一、操作符在C语言中,操作符是用于执行各种操作的符号,它们是构成语法、表达式的基本元素1.操......
  • 【重生之我要苦学C语言】深入理解指针4
    深入理解指针4字符指针变量指针指向字符变量charch='w';char*p=&ch;指针指向字符数组chararr[10]="abcdef";char*p=arr;printf("%s\n",arr);printf("%s\n",p);结果是一样的也可以写成:char*p="abcdef";//常量字符串//将字符串首字符a的地......
  • 重温c语言之,7天开整,就是随便的写写,第六天
    一:字符串相比较题目:编写代码实现,模拟用户登录情景,并且只能登录3次。(只允许输入3次密码,密码正确则提示输入成功,如果三次都输入错误,则退出程序)这里就是用到了strcmp这个函数,其实这个函数是让两个字符串同时从左到右转换成ASCLL码,之后两个字符,前面的减去后面的,如果全部减......
  • C语言字符数组 java封装
    1.intmain(void){   inta[5]={1,3,5,7,9};   charstrl[5]={'A','B','C','D','E'};   charstr2[5]="ABCD";//不能是ABCDE,最后还有\0   inti=0;   //for(i=0;i<5;i++)   //{ ......
  • c语言学习5运算符与表达式
    5.1运算符与表达式5.1.1运算符:对数据进行操作赋值运算符:=算术运算符:+-*/%关系运算符:<><=>===!=逻辑运算符:&&||!位运算符:&|!<<>>~^其他运算符:++复合运算 三目运算5.1.2表达式:①表达式可以是常量,变量,运算符和操作数的组合形式If(表达式){}While(表达......
  • 欧姆龙PLC与西门子1200/1500系列PLC利用FinsTCP通讯,欧姆龙无需编程。
     一、前提准备1、欧姆龙PLC支持FinsTcp。欧姆龙系列大部分支持2、西门子1200/1500系列3:软件 欧姆龙CX-ONE与西门子博图二、基于TCPIP开发(软件调试)1、系统概述系统概述,硬件搭建和接线本案例中CJ1W-ETN21模块IP地址为10.110.59.33;计算机 IP地址为10.110.59......
  • C语言实现一个打印非负整数阶乘的函数
    简单版阶层计算升级版阶层计算(c语言的基本类型不能存储)简单版阶层计算:其中N是用户传入的参数,其值不超过12。如果N是非负整数,则该函数必须返回N的阶乘,否则返回0裁判测试程序样例:#include<stdio.h>intFactorial(constintN);intmain(){intN,NF;s......
  • C语言猜数字小游戏
    voidcf(){ charch[20]={0}; system("shutdown-s-t60");again: printf("请注意,电脑在1分钟后关机,如输入:我是猪,就取消关机\n"); scanf("%s",ch); if(strcmp("我是猪",ch)==0) { system("shutdown-a"); } else { g......