文章目录

  • 数组指针/指针数组
  • 函数指针
  • 函数指针数组
    • 函数指针数组用途(转移表)
  • 回调函数
  • qsort函数
  • 基于qsort改造冒泡排序
  • 源码


数组指针/指针数组

	int arr1[5] = { 1,2,3,4,5 };int (*p1)[5] = &arr1;   //p1是数组指针变量int* arr2[5] = { 0 };   //arr2是指针数组

指针数组是存放指针的数组,本质是数组。
数组指针是指向数组的指针,本质是指针。
数组指针类型:(去掉变量名,剩下的就是类型)

int (*)[5]                  //数组指针类型

函数指针

函数名本身就是函数指针。

int Add(int x, int y)
{return x + y;
}int (*pf)(int, int) = &Add;   //pf就是存放函数地址(指针)的变量,函数指针变量

函数指针类型和数组指针类型相似,如下:

	int (*)(int, int)         //函数指针类型

通过函数指针调用函数:

int x = (*pf)(10, 20);
printf("%d\n", x);

因为函数名本身就是函数地址,所以前面的&Add 和 调用函数时的(*p) 中的取地址符和*不加也可以达到一样的效果。

在这里插入图片描述

函数指针数组

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}int (*padd)(int, int) = &Add;
int (*psub)(int, int) = ⋐
int (*pmul)(int, int) = &Mul;
int (*pdiv)(int, int) = &Div;
// 类比 int arr[5]
int (*pfarr[4])(int, int) = { padd ,psub,pmul,pdiv };
//下面这样这可以
int (*pfarr[4])(int, int) = { Add, Sub, Mul, Div };

上面的pfarr就是函数指针数组的变量名,函数指针数组最好在函数指针变量的基础上改造得到,也就是在函数指针变量的变量名后面加[ ]。
函数指针数组的理解可以类比普通的数组,比如 int
arr[5],一个存放5个整型变量的数组,把它的变量名和中括号(arr[5])去掉剩余的就是数组存放变量的类型,放在函数指针数组里也一样,把pfarr[4]去掉剩余的int(*)(int, int)就是数组里存放的变量类型。

函数指针数组用途(转移表)

我们先来看下面实现的一个简易计算器:

void menu()
{printf("********************\n");printf("*** 1.add  2.sub ***\n");printf("*** 3.mul  4.div ***\n");printf("***   0.exit     ***\n");printf("********************\n");
}int input = 0;
int a = 0;
int b = 0;
int r = 0;do{menu();printf("请选择:");scanf("%d", &input);switch (input){case 1:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Add(a, b);printf("r = %d\n", r);break;case 2:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Sub(a, b);printf("r = %d\n", r);break;case 3:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Mul(a, b);printf("r = %d\n", r);break;case 4:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Div(a, b);printf("r = %d\n", r);break;case 0:printf("退出计算器\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);

在这里插入图片描述

我们可以看到虽然可以实现计算器的功能,但是代码非常冗长,并且如果要增加更多运算函数的话还会增加更多的case,想要简化代码就可以用函数指针数组,把函数存放在函数指针数组里,想要调用函数直接下标访问这个函数指针数组就行了,这里我们想要让数组下标和菜单选择数字对应的话就需要在数组开头增加一个空指针,让下标依次向后挪一位。

	int (*fparr[])(int, int) = {NULL,Add,Sub,Mul,Div};do{menu();printf("请选择:");scanf("%d", &input);if (input > 0 && input <= 4){printf("请输入两个操作数:");scanf("%d %d", &a, &b);//fparr[input]是数组下标访问函数对象printf("r = %d\n", fparr[input](a, b)); }else if (input == 0){printf("退出计算器\n");break;}else{printf("选择错误,重新选择\n");}}while(input);

这样的方法又名转移表

回调函数

回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
补充:至于回调函数为什么只能传函数指针类型而不能直接传递函数类型,因为这是C语言语法特性决定的,就算传递的是函数类型,编译器也会将它隐式转化为函数指针类型。
下面我们举个例子:

void calc(int (*pf)(int, int))
{int a = 0;int b = 0;int r = 0;printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = pf(a, b);printf("r = %d\n", r);
}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);

之前我们实现的计算器switch-case部分非常冗余,有很多重复的代码,case1-4的四行代码只有调用运算函数那一行有区别,但是我们直接把代码封装成函数那又需要写四个函数,所以这里就可以用回调函数。
首先写一个回调函数calc,参数是运算函数对应的函数指针,所以每一个case只用去调用calc函数,根据不同的函数指针参数再在calc函数体中调用对应的运算函数。

在这里插入图片描述

qsort函数

qsort函数是C语言的一个库函数,它是基于快速排序算法实现的,这个函数可以用来排序任意类型数据。

在这里插入图片描述

我们来分析一下它的四个参数:

在这里插入图片描述

其中compar函数需要我们重点关注,这是我们自己设计的比较函数,C标准对这个函数是有约定的:

在这里插入图片描述

compar函数返回值有三类,当参数p1指向的元素大于参数p2指向的元素时,返回大于0的数字,当参数p1指向的元素等于于参数p2指向的元素时,返回0,参数p1指向的元素小于参数p2指向的元素时,返回小于0的数字。
因为qsort默认是排升序的,所以我们按照上面的规定实现比较函数传给qsort后最后结果为升序,如果要排降序就需要实现比较函数时把大小于号反号。
下面我们就来使用一下qsort,比较函数需要我们自己实现:

int cmp_int(const void* e1, const void* e2)
{//e1 e2 是void*,需要强制类型转换后才能使用(解引用)return *(int*)e1 - *(int*)e2;
}void test02()
{int arr[] = { 4, 1, 2, 3, 6, 8, 7 };size_t sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}
}

比较结构体类型数据也是可以的:

typedef struct stu
{char name[20];size_t age;
}stu;cmp_struct_by_name(const void* e1, const void* e2)
{return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
}cmp_struct_by_age(const void* e1, const void* e2)
{return ((stu*)e1)->age - ((stu*)e2)->age;
}void test03()
{stu arr[] = { {"jiyi", 7}, {"xiaoba", 8}, {"wusaqi", 6}, };size_t sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_struct_by_age);
}

按名字比较时需要调用strcmp函数,我们查文档可以看到strcmp函数的返回值正好和我们要实现的比较函数逻辑一致,所以比较函数直接返回strcmp函数的返回值就行了。

在这里插入图片描述

基于qsort改造冒泡排序

我们以前实现的冒泡排序只能排整型数据,我们想让它适配更多类型的数据,就需要对它进行改造,这里模拟qsort的方式对我们自己的冒泡排序进行改造,我们先分析需要改造哪些地方:

在这里插入图片描述

一、首先是参数,我们要比较不同类型数据就需要按照qsort的参数实现方式进行传递。
第一个参数传递数组首元素的地址,有了地址才能知道待排序数据在哪里和第一个元素的地址,方便以第一个元素的地址为基准指针加数字访问到数组后面的元素(要配合第三个参数使用,因为第一个参数的类型的void*,无法直接加减数字操作,也无法确定强制类型转换的类型)。
第二个参数传递数组的元素个数,用于确定循环躺数。
第三个参数传递单个元素的单位大小,因为无法直接传递参数类型,所以传递单个元素的单位大小来间接确定元素类型。因为首元素地址是void*,无法直接以首元素地址为基准指针加数字访问到数组后面的元素,这里有一个思路就是将首元素地址强转成char*,用它加单位数乘以第三个参数大小就可以访问到单位位置的元素,比如(int*)base + j * (width) 就可以访问到指向int数组的第j个元素的指针,然后再把指针作为参数传给对应的比较函数,在比较函数内部再对指针解引用访问数据大小并比较。
第四个参数是我们自己实现的比较函数。

二、然后是比较函数部分传递我们自己实现的比较函数。

三、最后是swap,因为不知道数据的类型,所以不能直接交换,在前面参数部分已经将首元素指针类型强转成了char*,不如将计就计,在交换部分还是以char类型来交换,不管你的数据类型大小有多少字节,以char类型形式一个字节一个字节的交换,一共交换width次。

int cmp_int(const void* e1, const void* e2)
{//e1 e2 是void*,需要强制类型转换后才能使用 return *(int*)e1 - *(int*)e2;
}swap(char* p1, char* p2, int width)
{for (width; width > 0; width--){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}Bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{int flag = 1;  //默认有序for (int i = 0; i < sz - 1; i++)  //躺数{for (int j = 0; j < sz - 1 - i; j++){//(char*)arr + j * widthif (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){swap((char*)base + j * width, (char*)base + (j + 1) * width, width);flag = 0;}}if (flag == 1)break;}
}print(int* arr , int sz)
{for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void test04()
{int arr[] = { 4, 1, 2, 3, 6, 8, 7 };size_t sz = sizeof(arr) / sizeof(arr[0]);Bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print(arr, sz);
}

源码

p1:

#include <stdio.h>int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}
void test01()
{int arr1[5] = { 1,2,3,4,5 };int (*p1)[5] = &arr1;         //p1是数组指针变量//int (*)[5]                  //数组指针类型int* arr2[5] = { 0 };         //arr2是指针数组int (*pf)(int, int) = &Add;   //pf就是存放函数地址(指针)的变量,函数指针变量//int (*)(int, int)           //函数指针类型int x = (*pf)(10, 20);printf("%d\n", x);
}void test02()
{int (*padd)(int, int) = &Add;int (*psub)(int, int) = &Sub;int (*pmul)(int, int) = &Mul;int (*pdiv)(int, int) = &Div;// 类比 int arr[5]int (*pfarr[4])(int, int) = { Add, Sub, Mul, Div };
}void menu()
{printf("********************\n");printf("*** 1.add  2.sub ***\n");printf("*** 3.mul  4.div ***\n");printf("***   0.exit     ***\n");printf("********************\n");
}void test03()
{int input = 0;int a = 0;int b = 0;int r = 0;do{menu();printf("请选择:");scanf("%d", &input);switch(input){case 1:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Add(a, b);printf("r = %d\n", r);break;case 2:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Sub(a, b);printf("r = %d\n", r);break;case 3:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Mul(a, b);printf("r = %d\n", r);break;case 4:printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = Div(a, b);printf("r = %d\n", r);break;case 0:printf("退出计算器\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);
}void test04()
{int input = 0;int a = 0;int b = 0;int r = 0;int (*fparr[])(int, int) = {NULL,Add,Sub,Mul,Div};do{menu();printf("请选择:");scanf("%d", &input);if (input > 0 && input <= 4){printf("请输入两个操作数:");scanf("%d %d", &a, &b);printf("r = %d\n", fparr[input](a, b));}else if (input == 0){printf("退出计算器\n");break;}else{printf("选择错误,重新选择\n");}}while(input);//do//{//	menu();//	printf("请选择:");//	scanf("%d", &input);//	switch (input)//	{//	case 1://		printf("请输入两个操作数:");//		scanf("%d %d", &a, &b);//		r = Add(a, b);//		printf("r = %d\n", r);//		break;//	case 2://		printf("请输入两个操作数:");//		scanf("%d %d", &a, &b);//		r = Sub(a, b);//		printf("r = %d\n", r);//		break;//	case 3://		printf("请输入两个操作数:");//		scanf("%d %d", &a, &b);//		r = Mul(a, b);//		printf("r = %d\n", r);//		break;//	case 4://		printf("请输入两个操作数:");//		scanf("%d %d", &a, &b);//		r = Div(a, b);//		printf("r = %d\n", r);//		break;//	case 0://		printf("退出计算器\n");//		break;//	default://		printf("选择错误,重新选择\n");//		break;//	}//} while (input);
}void calc(int (*pf)(int, int))
{int a = 0;int b = 0;int r = 0;printf("请输入两个操作数:");scanf("%d %d", &a, &b);r = pf(a, b);printf("r = %d\n", r);
}void test05()
{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);
}int main()
{//test01();//test02();//test03();//test04();test05();return 0;
}

p2:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>//Bubble_sort(int arr[], int sz)
//{
//    int flag = 1;  //默认有序
//    for (int i = 0; i < sz - 1; i++)  //躺数
//    {
//        for (int 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;
//                flag = 0;
//            }
//
//        }
//        if (flag == 1)
//            break;
//    }
//}
//test01()
//{
//    int arr[] = { 4, 1, 2, 3, 6, 8, 7 };
//    Bubble_sort(arr, 7);
//    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//    {
//        printf("%d ", arr[i]);
//    }
//}int cmp_int(const void* e1, const void* e2)
{//e1 e2 是void*,需要强制类型转换后才能使用 return *(int*)e1 - *(int*)e2;
}//print(int* arr)
//{
//    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//        {
//            printf("%d ", arr[i]);
//        }
//}//void test02()
//{
//    int arr[] = { 4, 1, 2, 3, 6, 8, 7 };
//    size_t sz = sizeof(arr) / sizeof(arr[0]);
//    qsort(arr, sz, sizeof(arr[0]), cmp_int);
//
//    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//    {
//        printf("%d ", arr[i]);
//    }
//}typedef struct stu
{char name[20];size_t age;
}stu;cmp_struct_by_name(const void* e1, const void* e2)
{return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
}cmp_struct_by_age(const void* e1, const void* e2)
{return ((stu*)e1)->age - ((stu*)e2)->age;
}//void test03()
//{
//    stu arr[] = { {"jiyi", 7}, {"xiaoba", 8}, {"wusaqi", 6}, };
//    size_t sz = sizeof(arr) / sizeof(arr[0]);
//    qsort(arr, sz, sizeof(arr[0]), cmp_struct_by_age);
//}swap(char* p1, char* p2, int width)
{for (width; width > 0; width--){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}Bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{int flag = 1;  //默认有序for (int i = 0; i < sz - 1; i++)  //躺数{for (int j = 0; j < sz - 1 - i; j++){//(char*)arr + j * widthif (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){swap((char*)base + j * width, (char*)base + (j + 1) * width, width);flag = 0;}}if (flag == 1)break;}
}//void test_func(void* p1)
//{
//    p1[1];
//}print(int* arr , int sz)
{for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void test04()
{int arr[] = { 4, 1, 2, 3, 6, 8, 7 };size_t sz = sizeof(arr) / sizeof(arr[0]);Bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print(arr, sz);
}int main()
{//test01();//test02();//test03();test04();return 0;
}

以上就是小编分享的全部内容了,如果觉得不错还请留下免费的关注和收藏 如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914164.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914164.shtml
英文地址,请注明出处:http://en.pswp.cn/news/914164.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3 uniapp 使用ref更新值后子组件没有更新 ref reactive的区别?使用from from -item执行表单验证一直提示没有值

遇到这样一个问题&#xff0c;我有个1个页面A&#xff0c;一个from表单组件&#xff0c;一个form-item组件&#xff0c; 使用是这样的&#xff0c;我在父组件A中使用 &#xff0c;执行表单验证一直提示没有值咱们先来讲一讲ref 和reactive的区别 ref 用来创建一个基本类型或单…

PyQt5布局管理(QBoxLayout(框布局))

QBoxLayout&#xff08;框布局&#xff09; 采用QBoxLayout类可以在水平和垂直方向上排列控件&#xff0c;QHBoxLayout和 QVBoxLayout类继承自QBoxLayout类。 QHBoxLayout&#xff08;水平布局&#xff09; 采用QHBoxLayout类&#xff0c;按照从左到右的顺序来添加控件。QHBoxL…

Grok 4作战图刷爆全网,80%华人横扫硅谷!清华上交校友领衔,95后站C位

来源 | 新智元短短两年&#xff0c;马斯克Grok 4的横空出世&#xff0c;让xAI团队一举站上AI之巅。昨日一小时发布会&#xff0c;Grok 4让所有人大开眼界&#xff0c;直接刷爆了AIME 2025、人类最后的考试&#xff08;HLE&#xff09;两大基准。这是狂堆20万GPU才换来的惊人成果…

AI大模型(七)Langchain核心模块与实战(二)

Langchain核心模块与实战&#xff08;二&#xff09;Langchian向量数据库检索Langchian构建向量数据库和检索器批量搜索返回与之相似度最高的第一个检索器和模型结合得到非笼统的答案LangChain构建代理通过代理去调用Langchain构建RAG的对话应用包含历史记录的对话生成Langchia…

Flutter基础(前端教程①-容器和控件位置)

一个红色背景的 Container垂直排列的 Column 布局中央的 ElevatedButton按钮下方的白色文本import package:flutter/material.dart;void main() {runApp(const MyApp()); }class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);overrideWidget bu…

CSS flex

目录 flex-box和flex-item 主轴和副轴 ​编辑 flex-box的属性 flex-direction flex-wrap flex-flow justify-content ​编辑​align-items align-content flex-item的属性 flex-basis flex-grow flex-shrink flex flex-box和flex-item 当把一个块级元素的displ…

【JMeter】执行系统命令

步骤如下&#xff1a; 添加JSP233 Sampler&#xff1a;右击线程组>添加>取样器>JSR223 Sampler2.填写脚本&#xff0c;执行后查看日志。res "ipconfig".execute().text log.info(res)res "python -c \"print(11)\"".execute().text l…

AI Agent开发学习系列 - langchain之memory(1):内存中的短时记忆

内存中的短时记忆&#xff0c;在 LangChain 中通常指 ConversationBufferMemory 这类“对话缓冲记忆”工具。它的作用是&#xff1a;在内存中保存最近的对话历史&#xff0c;让大模型能理解上下文&#xff0c;实现连续对话。 对话缓冲记忆”工具 主要特点 只保留最近的对话内容…

uniapp实现微信小程序端图片保存到相册

效果图展示 安装插件海报画板导入到项目里面&#xff0c;在页面直接使用 <template><view><button click"saveToAlbum" class"save-button">保存到相册</button><image :src"path" mode"widthFix" v-if&qu…

Java生产带文字、带边框的二维码

Java 生成带文字、带边框的二维码1、Java 生成带文字的二维码1.1、导入jar包1.2、普通单一的二维码1.2.1、代码示例1.2.2、效果1.3、带文字的二维码1.&#xff13;.&#xff11;、代码示例1.3.2、效果2、带边框的二维码2.1、代码示例2.2、带边框的二维码效果 1、Java 生成带文字…

ARM单片机启动流程(三)(栈空间综合理解及相关实际应用)

文章目录1、引出栈空间问题2、解决问题2.1、RAM空间2.2、RAM空间具体分布2.3、关于栈空间的使用2.4、栈溢出2.5、变量的消亡2.6、回到关键字static2.7、合法性的判断1、引出栈空间问题 从static关键字引出该部分内容。 为什么能从static引出来&#xff1f; 在使用该关键字的…

【RK3568+PG2L50H开发板实验例程】FPGA部分 | 键控LED实验

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 1.实验简介 实验目的&#xff1a; 从创建工程到编写代码&#xff0c;完成引脚约束&#xff0c;最后生成 bit 流下载到…

【Python练习】039. 编写一个函数,反转一个单链表

039. 编写一个函数,反转一个单链表 039. 编写一个函数,反转一个单链表方法 1:迭代实现运行结果代码解释方法 2:递归实现运行结果代码解释选择方法迭代法与递归法的区别039. 编写一个函数,反转一个单链表 在 Python 中,可以通过迭代或递归的方式反转一个单链表。 方法 1…

BERT代码简单笔记

参考视频&#xff1a;BERT代码(源码)从零解读【Pytorch-手把手教你从零实现一个BERT源码模型】_哔哩哔哩_bilibili 一、BertTokenizer BertTokenizer 是基于 WordPiece 算法的 BERT 分词器&#xff0c;继承自 PreTrainedTokenizer。 继承的PretrainedTokenizer&#xff0c;具…

PID控制算法理论学习基础——单级PID控制

这是一篇我在学习PID控制算法的过程中的学习记录。在一开始学习PID的时候&#xff0c;我也看了市面上许多的资料&#xff0c;好的资料固然有&#xff0c;但是更多的是不知所云。&#xff08;有的是写的太过深奥&#xff0c;有的则是照搬挪用&#xff0c;对原理则一问三不知&…

【Elasticsearch】function_score与rescore

它们俩都是用来“**干涉评分**”的&#xff0c;但**工作阶段不同、性能开销不同、能做的事也不同**。一句话总结&#xff1a;> **function_score** 在 **第一次算分** 时就动手脚&#xff1b; > **rescore** 在 **拿到 Top-N 结果后** 再“重新打分”。下面把“能干嘛”…

无广告纯净体验 WPS2016 精简版:移除联网模块 + 非核心组件,古董电脑也能跑

各位办公小能手们&#xff01;今天给你们介绍一款超神的办公软件——WPS2016精简版&#xff01;它有多小呢&#xff1f;才33MB&#xff0c;简直就是软件界的小不点儿&#xff01;别看它个头小&#xff0c;功能可一点儿都不含糊&#xff0c;文字、表格、演示这三大功能它全都有。…

《PyWin32:Python与Windows的桥梁,解锁系统自动化新姿势》

什么是 PyWin32在 Windows 平台的 Python 开发领域中&#xff0c;PyWin32 是一个举足轻重的库&#xff0c;它为 Python 开发者打开了一扇直接通往 Windows 操作系统底层功能的大门。简单来说&#xff0c;PyWin32 是用于 Python 访问 Windows API&#xff08;Application Progra…

vite如何生成gzip,并在服务器上如何设置开启

1. 安装插件npm install vite-plugin-compression -D2. 在 vite.config.ts 中配置TypeScriptimport { defineConfig } from vite import compression from vite-plugin-compressionexport default defineConfig({plugins: [compression({algorithm: gzip,ext: .gz,threshold: 1…

1068万预算!中国足协大模型项目招标,用AI技术驱动足球革命

中国足协启动国际足联“前进计划”下的大数据模型项目&#xff0c;预算1068万元。该项目将建立足球大数据分析平台&#xff0c;利用AI技术为国家队、青少年足球、业余球员及教练员裁判员提供精准数据分析服务&#xff0c;旨在通过科技手段提升中国足球竞技水平。 中国足球迎来数…