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

深入理解回调函数qsort:从入门到模拟实现

🍁博客主页:江池俊的博客

💫收录专栏:C语言进阶之路

💡代码仓库:江池俊的代码仓库

🎪我的社区:GeekHub

🎉欢迎大家点赞👍评论📝收藏⭐

在这里插入图片描述

文章目录

  • 前言
  • 一、什么是回调函数?
    • 📌使用回调函数的优势
  • 二、`qsort` 函数及其用法
    • 📌qsort函数作用
    • 📌qsort函数4个参数的介绍
    • 📌为什么qsort函数的参数是这四个?
    • 📌第4个参数--->compar比较函数的剖析
  • 三、qsort函数实例
    • 📌排序int类型数组
    • 📌排序char类型数组
    • 📌排序浮点型数组
    • 📌排序结构体类型数组
      • ``1. 【按姓名来排序】``
      • `` 2. 【按年龄来排序】``
  • 四、模拟实现qsort函数
    • 🧩冒泡排序
    • 🧩bubble_sort函数(模拟实现的qsort函数)
      • 🚩Swap函数剖析
    • 🧩利用bubble_sort函数排序整型数组
    • 🧩利用bubble_sort函数排序结构体数组
      • ``1. 【按姓名来排序】``
      • ``2. 【按年龄来排序】``
  • 总结


前言

回调函数和 qsort 是 C语言编程中重要的概念,它们为我们提供了强大的工具,用于处理函数指针和数组排序。本篇博客将逐步介绍回调函数的概念,详细解释 qsort 函数的用法,并通过一个模拟实现,帮助初学者更好地理解这些概念。如果大家不知道函数指针是说明或还不清楚函数指针的内容,可以移步我这篇文章《掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!》

一、什么是回调函数?

回调函数是一种通过函数指针传递给其他函数,并由其他函数在适当时候调用的函数。回调函数的存在使得我们能够将某种特定的行为(代码逻辑)作为参数传递给另一个函数。这在编程中非常有用,因为它允许我们以灵活的方式自定义函数的行为。

📌使用回调函数的优势

  1. 代码重用: 可以将通用的操作封装在回调函数中,以供多个函数重复使用。
  2. 灵活性: 回调函数允许我们在运行时动态地指定要执行的代码,从而实现更高度的灵活性。
  3. 解耦合: 使用回调函数可以将代码分解成独立的模块,减少模块之间的耦合,提高代码的可维护性。

二、qsort 函数及其用法

qsort 是 C 标准库中提供的用于数组排序的函数,它接受一个 比较函数 作为参数,用于确定数组元素的顺序。(这个比较函数是使用者根据自己的需要设计的,因此qsort函数可以实现对任意类型数据的排序)qsort 函数的原型如下:

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

其中,base 是要排序的数组的指针;num 是数组中元素的数量;size 是每个元素的大小,以字节为单位;compar 是用于比较两个元素的函数指针。

这里我们可以通过cplusplus网来查询这个函数的使用方法,也可以使用菜鸟教程网来查询。

📌qsort函数作用

在这里插入图片描述

📌qsort函数4个参数的介绍

在这里插入图片描述

📌为什么qsort函数的参数是这四个?

qsort 函数之所以有这四个参数,是为了实现通用、灵活且可定制的排序功能。

这些参数的设计和使用有以下几个目的:

  1. 通用性: 由于 qsort 需要适应不同类型的数据,它通过 base 参数接受数组的指针,并使用 size 参数来了解每个元素的大小,从而使得排序操作可以应用于各种不同类型的数组。

  2. 灵活性: 通过传递比较函数的指针作为 compar 参数,我们可以在不同的排序场景中定义不同的比较逻辑。这使得我们可以根据需要实现升序、降序或自定义的排序规则。

  3. 可定制性: qsort 的设计允许我们在排序过程中自定义元素的比较方式。我们可以根据实际需求提供不同的比较函数,从而实现不同的排序需求。

  4. 高效性: qsort 内部使用一种高效的排序算法(通常是快速排序的变种),以确保在大多数情况下能够高效地完成排序操作。

综上所述,这四个参数的设计使得 qsort 函数成为一个强大且通用的排序工具,可以适应不同类型的数据、实现不同的排序规则,并且在实际应用中能够高效地完成排序任务。

📌第4个参数—>compar比较函数的剖析

在这里插入图片描述
qsort 函数中,要实现升序或降序排序,这需要根据比较函数的逻辑来确定元素的顺序。比较函数的返回值将决定元素的排列方式。

  • 如果比较函数返回负值,qsort 将认为第一个元素应该在第二个元素之前,从而实现升序排序。
  • 如果比较函数返回正值,qsort 将认为第一个元素应该在第二个元素之后,从而实现降序排序。
  • 如果比较函数返回零,qsort 将认为两个元素相等,它们的顺序将是未定义的。

注意:

  • qsort 函数的比较函数参数是两个 void 类型的指针,是为了提高灵活性和通用性。这样的设计允许您在不同的排序场景中使用同一个 qsort 函数,无论排序的数据类型是什么。
  • 当编写一个通用的排序函数时,我们无法预先知道要排序的数据类型是什么。因此,将比较函数的参数声明为 void 类型的指针,使得 qsort 函数可以接受任何类型的数据。

由此,我们可以得到qsort函数的使用模板如下:

#include <stdio.h>
#include <stdlib.h>// 定义一个的数据类型(示例:整数类型)
typedef int MyType;// 比较函数
int compareMyType(const void *a, const void *b) {return (*(MyType *)a - *(MyType *)b);//实现升序//return (*(MyType *)b - *(MyType *)a);//实现降序
}int main() {int numElements = ...; // 数组中元素的数量MyType arr[numElements]; // 声明并初始化一个数组// 使用 qsort 对数组进行排序qsort(arr, numElements, sizeof(MyType), compareMyType);// 打印排序后的数组for (int i = 0; i < numElements; i++) {printf("%d ", arr[i]); // 打印数组元素}return 0;
}

三、qsort函数实例

注意:以下统一以升序为例

📌排序int类型数组

代码展示:

#include<stdio.h>
#include<stdlib.h>
//实现一个比较整型的函数
int compare_int(const void* a, const void* b)
{return *(int*)a - *(int*)b;//强制转换为int类型并解引用
}//使用qsort对数组进行排序,升序
int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);printf("排序前:");for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}//排序qsort(arr,sz,sizeof(int),compare_int);//打印printf("\n排序后:");for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");return 0;
}

运行结果:

📌排序char类型数组

代码展示:

#include <stdio.h>
#include <stdlib.h>int compare_char(const void* a, const void* b)
{return *(char*)a - *(char*)b; //强制转换为char类型并解引用
}int main()
{char arr[] = { 'f', 'e','d','b','a','c' };int sz = sizeof(arr) / sizeof(arr[0]);printf("排序前:");for (int i = 0; i < sz; i++){printf("%c ", arr[i]);}//排序qsort(arr, sz, sizeof(arr[0]), compare_char);//打印printf("\n排序后:");for (int i = 0; i < sz; i++){printf("%c ", arr[i]);}printf("\n");return 0;
}

运行结果:

📌排序浮点型数组

代码展示:

#include <stdio.h>
#include <stdlib.h>int compare_float(const void* a, const void* b)
{float num1 = *(float*)a;float num2 = *(float*)b;if (num1 < num2) return -1;if (num1 > num2) return 1;return 0;
}int main()
{float arr[] = { 5.2 , 2.5 , 3.14 , 1.5 };int sz = sizeof(arr) / sizeof(arr[0]);printf("排序前:");for (int i = 0; i < sz; i++){printf("%f ", arr[i]);}//排序qsort(arr, sz, sizeof(arr[0]), compare_float);//打印printf("\n排序后:");for (int i = 0; i < sz; i++){printf("%f ", arr[i]);}printf("\n");return 0;
}

注意:

  • 由于浮点数的精度和范围有限,返回差值可能导致精度丢失和不稳定的结果,特别是在极端情况下。因此,在处理浮点数时,使用差值可能会引发一些问题。
  • 为了确保排序的稳定性和正确性,最好的做法是显式地使用 if 语句来比较元素的值,并返回 -1、0 或 1,以确保在各种情况下都能获得正确的比较结果。

运行结果:

📌排序结构体类型数组

代码展示:

1. 【按姓名来排序】

//按姓名来排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student
{char name[20];int age;
}stu;int compare_name(const void* a, const void* b)
{return strcmp( ((stu*)a)->name, ((stu*)b)->name );//比较字符大小使用strcmp函数//strcmp函数返回值与compare_name函数一致
}int main()
{stu s[3] = { {"张三",20},{"李四",18},{"王五",25} };int sz = sizeof(s) / sizeof(s[0]);printf("排序前:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}//排序qsort(s, sz, sizeof(s[0]), compare_name);//打印printf("\n排序后:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}printf("\n");return 0;
}

运行结果:
在这里插入图片描述

2. 【按年龄来排序】

代码展示:

//按年龄来排序
#include <stdio.h>
#include <stdlib.h>
typedef struct Student
{char name[20];int age;
}stu;int compare_age(const void* a, const void* b)
{return (((stu*)a)->age - ((stu*)b)->age);
}int main()
{stu s[3] = { {"张三",20},{"李四",18},{"王五",25} };int sz = sizeof(s) / sizeof(s[0]);printf("排序前:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}//排序qsort(s, sz, sizeof(s[0]), compare_age);//打印printf("\n排序后:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}printf("\n");return 0;
}

运行结果:
在这里插入图片描述

四、模拟实现qsort函数

这里我是基于冒泡函数的思路来实现qsort函数的(实际上qsort函数的排序思路是快速排序) 冒泡排序的设计在本篇文末

🧩冒泡排序

#include<stdio.h>
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 - i - 1; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int arr[] = { 3,1,7,5,8,9,0,2,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);printf("冒泡排序前:\n");for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}//冒泡排序bubble_sort(arr, sz);printf("\n冒泡排序后:\n");for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");return 0;
}

运行结果:
在这里插入图片描述

  1. 这里我们发现bubble_sort函数中用来接收待排序数组首元素地址的指针arr已经被写死了,是int*类型,这就表它只能对整型数组进行排序。
  2. 其次函数内部对数组元素的比较和交换只适用于int类型的数据。

现在将利用冒泡排序来实现qsort函数,让它能排序任意类型的数据,该怎么做呢?

  • 首先我们知道qsort函数的创作者,他并不知道我们将来需要排序什么类型的数组,但是呢?他却通过qsort函数实现了各种类型数组的排序,这是怎么做到的呢?这就得益于这个函数的4个参数了。
  • 因此,只要我们给qsort函数提供 待排序数组首元素的地址数组中元素的个数数组中每个元素所占内存空间的字节大小,以及一个 比较函数 就能实现对这个数组的排序。所以我们也可以通过这些参数来用冒泡排序的思想实现对任意类型数组的排序。

🧩bubble_sort函数(模拟实现的qsort函数)

值得注意的是,这里说的利用冒泡排序来实现qsort函数,仅仅是实现了qsort函数可以对任意类型的数组进行排序这一特点,并不是说实现了qsort函数的底层原理,qsort的底层其实是通过快速排序来实现的。

//利用冒泡排序实现qsort
void Swap(char* e1, char* e2, size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *e1;*e1 = *e2;*e2 = tmp;e1++;e2++;}
}
//注意:这里的compar函数需要根据待排序的类型来书写
void bubble_sort(void* arr, int sz, size_t width, int(*compar)(const void* e1, const void* e2))
//第一个参数 - 用来接收待排序数组的首元素地址,因为待排序的数组元素类型不确定,所以形参数组用void*来接收
//第二个参数 - 用来接收数组元素个数
//第三个参数 - 用来接收数组中每个元素的大小,单位是字节
//第四个参数 - 用来接收一个比较函数,根据待排序数组元素的类型来传递对应类型的比较函数
{int i = 0;//趟数for (i = 0; i < sz - 1; i++){int flag = 1;//假设数组是排序好的//一趟冒泡排序的过程int j = 0;for (j = 0; j < sz - 1 - i; j++){if (compar((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)//因为我们并不知道数组元素的类型,所以需要将元素转化为最小的char*类型,//即把arr强转为char*类型,arr就可以正常使用,且char*与width配合能访问到任意类型任意位置处的数组元素//char类型指针+1只会跳过一个字节,+ j*width表示跳过j个元素{//交换//由于这里的数组名已经被强转为char类型的指针//所以要交换数组中的元素,就只能一个字节一个字节进行交换Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);//前两个参数是待交换元素的地址,第三个参数是待交换元素的所占字节的大小flag = 0;//如果数组元素进行交换了,说明数组还没有排好序}	}if (flag == 1)//如果没有再交换数组元素,就说明数组已经排好序{break;}}
}

🚩Swap函数剖析

Swap 函数用于交换两个元素的内容,它接受三个参数,这三个参数的作用如下:

void Swap(void* e1, void* e2, size_t width);
  1. void* e1: 指向第一个待交换元素的指针。由于数组的元素类型是未知的,所以使用 void* 类型来表示元素的指针。在函数内部,你需要将其转换为正确的类型,以便进行元素交换。

  2. void* e2: 指向第二个待交换元素的指针。同样,你需要在函数内部将其转换为正确的类型,以便进行交换操作。

  3. size_t width: 表示每个元素所占的字节数。由于元素类型未知,但在 bubble_sort 函数中有提供,所以通过这个参数确保在进行元素交换时能够正确地按字节进行操作。

  • Swap 函数内部,通过使用 width 参数,以字节为单位逐个交换两个元素的内容。这种设计使得 Swap 函数在不知道元素实际类型的情况下,仍能够正确地交换元素内容。
  • 虽然在实际代码中,可能会使用更高级的语言特性来进行元素交换(例如 C++ 中的模板函数或 C 中的宏),但是在这个示例中,通过使用 void* 指针和 字节级的操作,实现了一个通用的元素交换函数。

🧩利用bubble_sort函数排序整型数组

代码展示:

#include<stdio.h>
//利用bubble_sort函数排序整型数组
void Swap(char* e1, char* e2, size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *e1;*e1 = *e2;*e2 = tmp;e1++;e2++;}
}void bubble_sort(void* arr, int sz, size_t width, int(*compar)(const void* e1, const void* e2))
{int i = 0;for (i = 0; i < sz - 1; i++){int flag = 1;//假设数组是排序好的int j = 0;for (j = 0; j < sz - 1 - i; j++){if (compar((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)//实现升序{Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);flag = 0;//如果数组元素进行交换了,说明数组还没有排好序}}if (flag == 1)//如果没有再交换数组元素,就说明数组已经排好序{break;}}
}
//比较函数
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
//主函数
int main()
{int arr[] = { 3,1,7,5,8,9,0,2,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);printf("排序前:\n");for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}//排序bubble_sort(arr, sz, sizeof(int), cmp_int);printf("\n排序后:\n");for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");return 0;
}

运行结果:
在这里插入图片描述

🧩利用bubble_sort函数排序结构体数组

1. 【按姓名来排序】

代码展示:

//按姓名来排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//利用bubble_sort函数排序结构体数组
void Swap(char* e1, char* e2, size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *e1;*e1 = *e2;*e2 = tmp;e1++;e2++;}
}void bubble_sort(void* arr, int sz, size_t width, int(*compar)(const void* e1, const void* e2))
{int i = 0;for (i = 0; i < sz - 1; i++){int flag = 1;//假设数组是排序好的int j = 0;for (j = 0; j < sz - 1 - i; j++){if (compar((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)//实现升序{Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);flag = 0;//如果数组元素进行交换了,说明数组还没有排好序}}if (flag == 1)//如果没有再交换数组元素,就说明数组已经排好序{break;}}
}
//声明一个结构体,并重命名为stu
typedef struct student
{char name[20];int age;
}stu;
//比较函数
int compare_name(const void* a, const void* b)
{return strcmp( ((stu*)a)->name, ((stu*)b)->name );//strcmp函数返回值与compare_name函数一致
}int main()
{stu s[3] = { {"张三",20},{"李四",18},{"王五",25} };int sz = sizeof(s) / sizeof(s[0]);printf("排序前:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}//排序bubble_sort(s, sz, sizeof(s[0]), compare_name);//打印printf("\n排序后:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}printf("\n");return 0;
}

运行结果:
在这里插入图片描述

2. 【按年龄来排序】

代码展示:

//按年龄来排序
#include <stdio.h>
#include <stdlib.h>
//利用bubble_sort函数排序结构体数组
void Swap(char* e1, char* e2, size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *e1;*e1 = *e2;*e2 = tmp;e1++;e2++;}
}void bubble_sort(void* arr, int sz, size_t width, int(*compar)(const void* e1, const void* e2))
{int i = 0;for (i = 0; i < sz - 1; i++){int flag = 1;//假设数组是排序好的int j = 0;for (j = 0; j < sz - 1 - i; j++){if (compar((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)//实现升序{Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);flag = 0;//如果数组元素进行交换了,说明数组还没有排好序}}if (flag == 1)//如果没有再交换数组元素,就说明数组已经排好序{break;}}
}
//声明一个结构体,并重命名为stu
typedef struct student
{char name[20];int age;
}stu;
//比较函数
int compare_age(const void* a, const void* b)
{return (((stu*)a)->age - ((stu*)b)->age);
}int main()
{stu s[3] = { {"张三",20},{"李四",18},{"王五",25} };int sz = sizeof(s) / sizeof(s[0]);printf("排序前:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}//排序bubble_sort(s, sz, sizeof(s[0]), compare_age);//打印printf("\n排序后:");for (int i = 0; i < sz; i++){printf("%s %d", s[i].name, s[i].age);if (i < sz - 1)printf(" | ");}printf("\n");return 0;
}

运行结果:
在这里插入图片描述


总结

回调函数和 qsort 是 C 语言编程中重要的概念,能够提供强大的灵活性和功能。通过理解回调函数的概念,我们可以将特定行为作为参数传递给其他函数,实现代码的模块化和解耦合。qsort 则为数组排序提供了便利,允许我们自定义比较逻辑以满足不同的需求。

通过以上的介绍和模拟实现,希望初学者们能够更好地理解回调函数和 qsort 的核心概念,为日后的编程实践打下坚实的基础。无论是构建灵活的程序结构还是优化代码性能,这些概念都将成为你编程工具箱中不可或缺的工具。


🔥今天的分享就到这里, 如果觉得博主的文章还不错的话, 请👍三连支持一下博主哦🤞

在这里插入图片描述

相关文章:

深入理解回调函数qsort:从入门到模拟实现

&#x1f341;博客主页&#xff1a;江池俊的博客 &#x1f4ab;收录专栏&#xff1a;C语言进阶之路 &#x1f4a1;代码仓库&#xff1a;江池俊的代码仓库 &#x1f3aa;我的社区&#xff1a;GeekHub &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐ 文章目录 前…...

【Git基础】获取远程仓库

我们通常从远程服务器克隆一个Git仓库或者将本地仓库初始化为Git仓库。 1 从远程服务器克隆一个Git仓库 $ git clone https://github.com/your-username/your-repo-name你可以自定义其仓库名称&#xff1a; $ git clone https://github.com/your-username/your-repo-name cu…...

chatGPT界面

效果图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html> <head><title>复选框样式示例</title> </head> <style>* {padding:0;margin: 0;}.chatpdf{display: flex;height: 100vh;flex-direction: row;}.chatpdf .pannel{widt…...

windows一键启动jupyter

windows一键启动jupyter jupyter简介 Jupyter是一个开源的交互式计算环境&#xff0c;主要用于数据分析、数据可视化和科学计算。它的名字来源于三种编程语言的缩写&#xff1a;Julia、Python和R&#xff0c;这三种语言都可以在Jupyter环境中运行。如果您想进行数据分析、科学…...

树形结构的快速生成

背景 相信大家都遇到过树形结构&#xff0c;像是文件列表、多级菜单、评论区的设计等等&#xff0c;我们都发现它有很多层级&#xff0c;第一级可以有多个&#xff0c;下边的每一个层级也可以有多个&#xff1b;有的可以设计成无限层级的&#xff0c;有的只能设计成两级。那么…...

Android笔记(二十七):自定义Dialog实现居中Toast

背景 记录实现符合项目需求的Toast弹窗 具体实现 class MyTipDialog private constructor(val context: Activity): Dialog(context, R.style.MyTipTheme) {val resId ObservableField(0)private val mainHandler Handler(Looper.getMainLooper())init {setCanceledOnTouc…...

css实现文字的渐变,适合大屏

1 在全局写一个全局样式&#xff0c;文字渐变 2 在组件中使用 CSS3利用-webkit-background-clip: text;实现文字渐变效果_css如何把盒子底部的文字变成透明渐变_I俩月亮的博客-CSDN博客 CSS 如何实现文字渐变色 &#xff1f;_css字体颜色渐变_一个水瓶座程序猿.的博客-CSDN博客…...

软考高级系统架构设计师系列论文八十七:论企业应用集成

软考高级系统架构设计师系列论文八十七:论企业应用集成 一、企业应用集成相关知识点二、摘要三、正文四、总结一、企业应用集成相关知识点 软考高级系统架构设计师系列之:企业集成平台技术的应用和架构设计二、摘要 本文讨论了某公司的应用系统集成项目。某公司为了应对市场变…...

C++设计模式之适配器模式

一、适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;用于将一个类的接口转换成另一个类所期望的接口&#xff0c;以便两个类能够协同工作。 适配器模式可以解决现有类接口与所需接口不匹配的问题&#xff0c;使得原本因接口不…...

山西电力市场日前价格预测【2023-08-24】

日前价格预测 预测明日&#xff08;2023-08-24&#xff09;山西电力市场全天平均日前电价为319.98元/MWh。其中&#xff0c;最高日前电价为370.78元/MWh&#xff0c;预计出现在19: 30。最低日前电价为272.42元/MWh&#xff0c;预计出现在12: 45。 价差方向预测 1&#xff1a; 实…...

一文速学-让神经网络不再神秘,一天速学神经网络基础(一)

前言 思索了很久到底要不要出深度学习内容&#xff0c;毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新&#xff0c;很多坑都没有填满&#xff0c;而且现在深度学习的文章和学习课程都十分的多&#xff0c;我考虑了很久决定还是得出神经网络系列文章&#xff0c;…...

百度Q2财报:营收341亿元实现加速增长,净利润高速增长44%,增长强劲全线重构

北京时间8月22日&#xff0c;百度发布了截至2023年6月30日的第二季度未经审计的财务报告。第二季度&#xff0c;百度实现营收341亿元&#xff0c;同比增长15%&#xff1b;归属百度的净利润&#xff08;non-GAAP&#xff09;达到80亿元&#xff0c;同比增长44%。营收和利润双双实…...

ARM DIY(二)配置晶振频率

文章目录 前言串口乱码问题定位内核修改晶振频率uboot 修改晶振频率番外篇 前言 上篇文章《ARM DIY 硬件调试》介绍了 DIY ARM 板的基础硬件焊接&#xff0c;包括电源、SOC、SD 卡座等&#xff0c;板子已经可以跑起来了。 但是发现串口乱码&#xff0c;今天就来解决串口乱码问…...

高等数学:线性代数-第三章

文章目录 第3章 矩阵的初等变换与线性方程组3.1 矩阵的初等变换3.2 矩阵的秩3.3 方程组的解 第3章 矩阵的初等变换与线性方程组 3.1 矩阵的初等变换 矩阵的初等变换 下面三种变换称为矩阵的初等变换 对换两行&#xff08;列&#xff09;&#xff0c;记作 r i ↔ r j ( c i …...

深入理解 SQL 注入攻击原理与防御措施

系列文章目录 文章目录 系列文章目录前言一、SQL 注入的原理二、防御 SQL 注入攻击的措施1. 使用参数化查询2.输入验证与过滤3.最小权限原则4.不要动态拼接 SQL5.ORM 框架6.转义特殊字符三、实例演示总结前言 SQL 注入是一种常见的网络攻击方式,攻击者通过在输入框等用户交互…...

QT5.12.12通过ODBC连接到GBase 8s数据库(CentOS)

本示例使用的环境如下&#xff1a; 硬件平台&#xff1a;x86_64&#xff08;amd64&#xff09;操作系统&#xff1a;CentOS 7.8 2003数据库版本&#xff08;含CSDK&#xff09;&#xff1a;GBase 8s V8.8 3.0.0_1 为什么使用QT 5.12.10&#xff1f;该版本包含QODBC。 1&#…...

爱校对发布全新PDF校对工具,为用户带来更为便捷的校正体验

随着数字化文档使用的普及&#xff0c;PDF格式已经成为最为广泛使用的文件格式之一。为满足广大用户对于高效、准确PDF文档校对的需求&#xff0c;爱校对团队经过深入研发&#xff0c;正式推出全新的PDF校对工具&#xff01; 这一全新工具针对PDF文件格式进行了深度优化&#…...

记录protocol buffers Mac安装

使用brew安装最新的protobuf 在Mac 上安装&#xff0c;使用brew 可以安装最新的protobuf。这个也比较简单&#xff0c;简单说一下。 首先先检查一下是否安装了brew。如果没有安装brew的话&#xff0c;请先安装brew.可以通过brew --version来检查 使用brew install protobuf 来…...

基于Jenkins自动打包并部署docker、PHP环境,ansible部署-------从小白到大神之路之学习运维第86天

第四阶段提升 时 间&#xff1a;2023年8月23日 参加人&#xff1a;全班人员 内 容&#xff1a; 基于Jenkins部署docker、PHP环境 目录 一、环境部署 &#xff08;一&#xff09;实验环境&#xff0c;服务器设置 &#xff08;二&#xff09;所有主机关闭防火墙和selinu…...

【附安装包】Midas Civil2019安装教程

软件下载 软件&#xff1a;Midas Civil版本&#xff1a;2019语言&#xff1a;简体中文大小&#xff1a;868.36M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.5GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

Mysql故障排插与环境优化

前置知识点 最上层是一些客户端和连接服务&#xff0c;包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念&#xff0c;为通过安全认证接入的客户端提供线程。同样在该层上可…...

盲盒一番赏小程序:引领盲盒新潮流

在盲盒市场日益火爆的今天&#xff0c;如何才能在众多盲盒产品中脱颖而出&#xff1f;盲盒一番赏小程序给出了答案&#xff0c;它以创新的玩法和优质的服务&#xff0c;引领着盲盒新潮流。 一番赏小程序的最大特色在于其独特的赏品分级制度。赏品分为多个等级&#xff0c;从普…...