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

指针的进阶

指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。

 字符指针

 在指针的类型中我们知道有一种指针类型为字符指针 char*

一般使用

int main()
{char ch = 'w';char *pc = &ch;*pc = 'w';return 0;
}

还有一种使用方式如下:

int main()
{const char* pstr = "hello jzy.";//这里是把一个字符串放到pstr指针变量里了吗?不是的,是将字符串首元素地址传过去了,最好前边要加const 修饰常量字符串printf("%s\n", pstr);char arr[] = "abcdef";//[a b c d e f \0],这种初始化方式等价于char arr[]={'a','b','c','d','e','f','\0'};,因为字符串末尾自动跟一个\0return 0;
}

这个字符串是存在代码段的(内存的只读常量区)

26613168c6754d19b38a99e96258444c.png     “hello jzy."3001b7169e774c5c839fdad2305c6906.png

                                                         

                                                             

面试题:

#include <stdio.h>
int main()
{
char str1[] = "hello jzy.";
char str2[] = "hello jzy.";
const char *str3 = "hello jzy.";
const char *str4 = "hello jzy.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
输出:
22b6360d11fd4477b0a8d02ebe2c52e0.png
这里str3和str4指向的是一个同一个常量字符串。(因为常量字符串不做修改,可以多个指针指向字符串)C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。

指针数组(就是存放指针的数组)

在《指针》章节我们也学了指针数组,指针数组是一个存放指针的数组。
这里我们再复习一下,下面指针数组是什么意思?
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
我们看个例子
使用指针数组模拟实现二维数组
int main()
{int arr1[] = { 1,2,3,4,5 };//arr1 - int*int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//指针数组int* arr[3] = { arr1, arr2, arr3 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0;
}

数组名是数组首元素的地址,arr[1]拿到的是arr1数组名,再使用下标就能访问数组的元素,很简单

补充一下数组名的理解    数组名是数组首元素的地址
有2个例外:
1. sizeof(数组名),这里的数组名不是数组首元素的地址,数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
2. &数组名,这里的数组名表示整个数组, &数组名取出的是整个数组的地址
除此之外,所有的地方的数组名都是数组首元素的地址
825480c471ce4bce9488c3b36c459e85.png
可以看到sizeof数组名并不是4或者8,而是整个数组的大小40,这是一个例外
27bf26d87ce64f16b3866e7c707da8ce.png
可以看到arr+1跳过4个字节1个整形,说明arr正常是首元素地址
&arr+1,取出来整个数组的地址,所以+1会跳过整个数组40字节

数组指针(指向一个数组的指针)

数组指针的定义
数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?
int *p1[10];//是存放指针的数组,指针数组
int (*p2)[10];//是指向数组的指针,数组指针
//p1, p2分别是什么?
解释
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
数组指针的使用(遍历可以用到),p是一个数组指针变量,解引用p拿到数组名然后可以用方括号进行访问元素
4b6dae2a2f1349a6a6fe454eb2e3b908.png
void Print(int arr[3][5], int r, int c)
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };Print(arr, 3, 5);return 0;
}

//数组指针怎么使用呢?一般在二维数组上才方便
//
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7
//

//二维数组传参,形参是二维数组的形式

也可以换一种形式

//二维数组传参,形参是指针的形式

void Print(int (*p)[5], int r, int c)
{
    int i = 0;
    for (i = 0; i < r; i++)
    {
        int j = 0;
        for (j = 0; j < c; j++)
        {
            printf("%d ", *(*(p + i) + j));
        }
        printf("\n");
    }
}

int main()
{
    int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };

    //arr 是数组名,数组名表示数组首元素的地址

    Print(arr, 3, 5);
        
    return 0;
}


void test1(int arr[5], int sz)
{}
void test2(int* p, int sz)
{}int main()
{int arr[5] = { 0 };test1(arr, 5);test2(arr, 5);return 0;
}

//一维数组传参,形参的部分可以是数组,也可以是指针

void test3(char arr[3][5], int r, int c)
{}void test4(char (*p)[5], int r, int c)
{}
int main()
{char arr[3][5] = {0};test3(arr, 3, 5);test4(arr, 3, 5);return 0;
}

//二维数组传参,形参的部分可以是数组,也可以是指针

以上就是对数组指针的介绍

数组参数、指针参数

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
1 一维数组传参
#include <stdio.h>
void test(int arr[])//ok?ok数组名传参可以是指针可以是数组
{}
void test(int arr[10])//ok?//ok注意方块里的数字填不填都可以,因为本质这部分不会创建数组,本质是指针
{}
void test(int *arr)//ok?//包ok的
{}
void test2(int *arr[20])//ok?包ok的是一个指针数组
{}
void test2(int **arr)//ok?包的,二级指针,一级指针数组首元素的地址包是二级指针类型
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}
2 二维数组传参
void test(int arr[3][5])//ok?包的,类似一维数组
{}
void test(int arr[][])//ok?不可以之只能省略第一个数,第二个必须要有,因为要知道一行几个元素
{}
void test(int arr[][5])//ok?包可以的
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?包不行的,二维数组数组名是第一行数组的地址,这是整形地址
{}
void test(int* arr[5])//ok?不行这是指针数组
{}
void test(int (*arr)[5])//ok?包可以的,最规范的写法
{}
void test(int **arr)//ok?不行数组指针跟二级指针差距很大
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}

函数指针(指向函数的指针)

92817f90e18d45fab5482be3f274da77.png

看段代码

#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}

8c061b8883fb40129a652964e6e9d300.png

输出的是两个地址,这两个地址是 test 函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:
void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();//对
void *pfun2();//err
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
我们来阅读两段有趣的代码
//代码1
(*(void (*)())0)();//本质上是调用0地址处的函数,先将0强制类型转换为void (*)()  类型的函数指针,后调用0地址处的函数-》函数名()只是没有传参
//代码2
void (*signal(int , void(*)(int)))(int);
:推荐《C陷阱和缺陷》
这本书中提及这两个代码。
代码2太复杂,如何简化:
typedef void(*pfun_t)(int);//把类型重命名
pfun_t signal(int, pfun_t);
函数指针数组
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:
int *arr[10];
//数组的每个元素是int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。
用函数指针数组的方式实现一个转移表
//函数指针数组的方式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 menu()
{printf("***************************\n");printf("*****  1.add  2.sub  ******\n");printf("*****  3.mul  4.div  ******\n");printf("*****  0.exit        ******\n");printf("***************************\n");
}
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;//函数指针数组的使用 - 转移表int (* pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};//                            0     1    2    3    4do{menu();printf("请选择:>");scanf_s("%d", &input);if (input >= 1 && input <= 4){printf("请输入两个操作数:");scanf_s("%d %d", &x, &y);ret = pfArr[input](x, y);printf("ret = %d\n", ret);}else if(input == 0){printf("退出计算器\n");}else{printf("选择错误,重新选择\n");}} while (input);return 0;
}

830137abcc0049cebf0a0acb9d5adb44.png就可以玩了,原理代码就很清晰了

指向函数指针数组的指针(了解一下就行)
void test(const char* str)
{printf("%s\n", str);
}int main()
{void (*pf)(const char*) = test;//pf是函数指针变量void (*pfArr[10])(const char*);//pfArr是存放函数指针的数组void (* (*p) [10])(const char*) = &pfArr;//p指向函数指针数组的指针return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
5ffe9c780b2841d78bd8b7ef3b06d69b.png
ffb5b7a4d0a543be82ac9632c5c8944d.png
2fa3193f1e184d289a21005b896ba063.png
6f48ee6af5dc41de8798c1bc1f595fbc.png
直接使用qsort
#include <stdlib.h>int cmp_int(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}void print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}//测试qsort排序整型数据
void test1()
{int arr[10] = { 3,1,5,2,4,7,9,6,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);//默认是升序的qsort(arr, sz, sizeof(arr[0]),cmp_int);//print(arr, sz);
}int main()
{test1();return 0;
}

这里前三个参数都很简单,第四个参数要自己写,通过自己比较的数据相邻数据的大小判断升序降序,左边数据大于右边数据,返回大于0的数字,默认是升序(这就是回调函数,我们把函数指针传递给参数,等我们真正需要用的时候会调用这个cmp_int函数)!!!注意,我们自己写的cmp_int函数一定要和库里的保持一致,参数是const void*,在cmp_int函数体内,我们想比较什么类型的数据,就强转为什么类型很方便)

ea2397f89e2e4b2facb5e6216e45c6d0.png

测试结构体年龄排序


#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;
}void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}int main()
{test2();return 0;
}

387d44efb4b849ec8607ee3044d6ba3d.png

可以看到没排序之前是乱序

a38a7603c90649b0a9465e3d761d07e9.png

排序后是升序

测试结构体名字排序

#include <stdlib.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);
}void test3()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}int main()
{test3();return 0;
}

没排序之前是乱序

c437dd59ab1945c291b77008bff2ea1d.png

排序之后变成升序了

注意我们为什么要用void*作为参数接受呢

int main()
{int a = 10;float f = 3.14f;//int* pa = &a;//char* pc = &a;//err会提示类型不兼容void* pv = &a;pv = &f;*pv;//errpv++;//errreturn 0;
}


void* 的指针 - 无具体类型的指针
void* 类型的指针可以接收任意类型的地址(可以看到pv可以接收int也可以接收float没有报错,不能进行解引用和++操作,因为void*类型不确定)

我们采用回调函数模拟实现qsort函数(用冒泡排序实现,但是底层是快排)
8c716bb5935c4635906d84d0ae13f1ba.png

排序整形严格按照库里qsort函数的参数(注意:交换函数要实现为char* 因为这样可以一次拿到一个字节,进行交换,理解为两个整数的交换就行)

void Swap(char* buf1, char* buf2, int size)//交换arr[j],arr[j+1]这两个元素
{int i = 0;char tmp = 0;for (i = 0; i < size; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{int  i = 0;//趟数for (i = 0; i < num - 1; i++){int j = 0;//一趟内部比较的对数for (j = 0; j < num - 1 - i; j++){//假设需要升序cmp返回>0,交换if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp{//交换Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}}int cmp_int(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}//测试bubble_sort 排序整型数据
void test1()
{int arr[10] = { 3,1,5,2,4,7,9,6,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
}
int main()
{test1();return 0;
}

没排序之前

124ab6beebd349de9947e11dfa7f0069.png

排序之后

0dd12ad3f4d44afd98fcc301dfbaee1d.png

测试结构体age

void Swap(char* buf1, char* buf2, int size)//交换arr[j],arr[j+1]这两个元素
{int i = 0;char tmp = 0;for (i = 0; i < size; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{int  i = 0;//趟数for (i = 0; i < num - 1; i++){int j = 0;//一趟内部比较的对数for (j = 0; j < num - 1 - i; j++){//假设需要升序cmp返回>0,交换if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp{//交换Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}}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;
}//测试bubble_sort 排序结构体数据
void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}int main()
{test2();//test3();return 0;
}

没排序之前

7c772c7dcbfe44739fc319199d3389d6.png

排序之后可以看到以年龄排序成功

873d162792e84800a915664a5b4a03f0.png

测试结构体name

void Swap(char* buf1, char* buf2, int size)//交换arr[j],arr[j+1]这两个元素
{int i = 0;char tmp = 0;for (i = 0; i < size; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{int  i = 0;//趟数for (i = 0; i < num - 1; i++){int j = 0;//一趟内部比较的对数for (j = 0; j < num - 1 - i; j++){//假设需要升序cmp返回>0,交换if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp{//交换Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}}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);
}void test3()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sizeof(struct Stu));bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}int main()
{test3();return 0;
}

排序之前

9fb7faef6b0648a4b2caac8336fba67d.png

排序之后

499a31b6609f4ed1a2e5ad2cb21024d2.png

以上就是我对回调函数的认识和讲解,感谢支持!!!以后会创作更多有用文章

相关文章:

指针的进阶

指针的主题&#xff0c;我们在初级阶段的《指针》章节已经接触过了&#xff0c;我们知道了指针的概念&#xff1a; 1. 指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识一块内存空间。 2. 指针的大小是固定的4/8个字节&#xff08;32位平台/64位平台&#xff0…...

每日学习30分轻松掌握CursorAI:Cursor插件系统与扩展功能

Cursor插件系统与扩展功能 一、课程概述 今天我们将学习Cursor AI的插件系统&#xff0c;了解如何通过插件扩展和增强IDE功能。由于Cursor AI基于VS Code开发&#xff0c;我们可以利用丰富的VS Code插件生态系统。 1.1 学习目标 了解插件系统原理掌握插件安装管理使用常用开…...

【WEB】网络传输中的信息安全 - 加密、签名、数字证书与HTTPS

文章目录 1. 概述2. 网络传输安全2.1.什么是中间人攻击2.2. 加密和签名2.2.1.加密算法2.2.2.摘要2.2.3.签名 2.3.数字证书2.3.1.证书的使用2.3.2.根证书2.3.3.证书链 2.4.HTTPS 1. 概述 本篇主要是讲解讲一些安全相关的基本知识&#xff08;如加密、签名、证书等&#xff09;&…...

Dexie.js内存管理技巧:在大型数据集操作中避免浏览器崩溃

Dexie.js 内存管理技巧&#xff1a;避免浏览器崩溃 在使用 Dexie.js 操作 大型数据集 时&#xff0c;如果不注意内存管理&#xff0c;可能会导致浏览器内存溢出&#xff08;OOM&#xff0c;Out of Memory&#xff09;或崩溃。因此&#xff0c;以下 内存管理技巧 可用于优化性能…...

vscode项目依赖问题

必读 一定要将前端下拉的项目备份一下&#xff0c;很容易运行导致依赖报错&#xff0c;重新下载 命令 使用幽灵分解器安装 pnpm install 替代 npm install 设置淘宝NPM镜像源 yarn config set registry https://registry.npmmirror.com 查看目前依赖包的版本 npm list ant-d…...

LLMs之RAG:《EdgeRAG: Online-Indexed RAG for Edge Devices》翻译与解读

LLMs之RAG&#xff1a;《EdgeRAG: Online-Indexed RAG for Edge Devices》翻译与解读 导读&#xff1a;这篇论文针对在资源受限的边缘设备上部署检索增强生成 (RAG) 系统的挑战&#xff0c;提出了一种名为 EdgeRAG 的高效方法。EdgeRAG 通过巧妙地结合预计算、在线生成和缓存策…...

宇泰串口卡驱动在Ubuntu22.04编译、安装汇总

从官网下载驱动官网地址 上传到Ubuntu, 目录结构如下&#xff1a; 驱动源代码: 驱动代码是基于开源项目编译来的 编译路径不能有中文路径&#xff0c;否则可能有类似错误 源码是基于Linux2.3内核编译&#xff0c;我当前是6.8.0-51&#xff0c;数据结构有升级&#xff0c;需要调…...

python管理工具:conda部署+使用

python管理工具&#xff1a;conda部署使用 一、安装部署 1、 下载 - 官网下载&#xff1a; https://repo.anaconda.com/archive/index.html - wget方式&#xff1a; wget -c https://repo.anaconda.com/archive/Anaconda3-2023.03-1-Linux-x86_64.sh2、 安装 在conda文件的…...

(三)html2canvas将HTML 转为图片并实现下载

将 HTML 转为图片并实现下载&#xff0c;通常可以使用一个叫做 html2canvas 的 JavaScript 库。html2canvas 能够将 HTML 元素及其样式渲染成一个画布 (Canvas)&#xff0c;然后将该画布转换为图片格式&#xff08;如 PNG 或 JPEG&#xff09;&#xff0c;最终提供下载功能。 …...

安装Docker流程

1.卸载旧版 首先如果系统中已经存在旧的Docker&#xff0c;则先卸载&#xff1a; yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine 2.配置Docker的yum库 首先要安装一个…...

flutter 使用google_mlkit_image_labeling做图片识别

在AI横行的如今&#xff0c;相信大家或多或少都做过跟AI接轨的需求了吧&#xff1f;今天我说的是关于图片识别的需求&#xff0c;flutter的专属图片识别插件google_mlkit_image_labeling。 google_mlkit_image_labeling它是Google旗下的Google Cloud Vision API中分支出来的一部…...

宝塔php7.4安装报错,无法安装,php8以上可以安装,以下的不行,gd库什么的都正常

宝塔的依赖问题导致的问题&#xff0c;最后手动挂载后才解决。。。废了三天三夜终于搞好了。。。。无语&#xff5e; 建议&#xff1a;不要一直升级宝塔版本&#xff0c;升级前备份或者开服务商的实例镜像&#xff0c;方便恢复&#xff0c;不然&#xff0c;可就GG了&#xff5…...

python中的RPA->playwright自动化录制脚本实战案例笔记

playwright录制功能使用绕过登录操作 1、首先安装playwright pip install playwright2、 安装支持的浏览器 playwright install # 安装支持的浏览器&#xff1a;cr, chromium, ff, firefox, wk 和 webkit3、接着在自己的项目下运行录制命令&#xff1a; playwright codegen…...

Python在DevOps中的应用:自动化CI/CD管道的实现

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门&#xff01; 解锁Python编程的无限可能&#xff1a;《奇妙的Python》带你漫游代码世界 在现代软件开发中&#xff0c;DevOps理念的引入极大地提升了开发与运维的协作效率&#xff0c;而持续集成&#xff08…...

Centos 离线安装杀毒软件

离线部署实现&#xff1a; 1、去官网下载对应的软件包&#xff0c;centos就下载 .rpm软件包。https://www.clamav.net/downloads2、将下载的软件包上传到服务器后使用rpm命令进行安装&#xff0c;软件包里面已经将相关依赖这些打包好了&#xff0c;直接安装就行。 rpm -ivh --…...

TiDB使用过程中需要注意的坑点:避免踩雷

TiDB使用过程中需要注意的坑点&#xff1a;避免踩雷 TiDB作为一个分布式数据库&#xff0c;虽然在许多场景下表现出色&#xff0c;但在使用过程中也有一些“坑”需要开发者特别注意。尤其是在生产环境中&#xff0c;踩雷可能会导致性能问题&#xff0c;甚至系统宕机。今天&…...

Mysql--实战篇--大数据量表的分页优化(自增长主键,子查询主键主查询全部,查询条件加索引,覆盖索引等)

当Mysql数据表存储大量数据时&#xff08;百万级别数据&#xff09;&#xff0c;分页查询的性能问题是一个常见的挑战。特别是当使用LIMIT和OFFSET时&#xff0c;随着OFFSET的增加&#xff0c;查询性能会显著下降。原因在于MySQL需要扫描并跳过前面的行&#xff0c;这会导致I/O…...

Datawhale组队学习笔记task1——leetcode面试题

文章目录 写在前面刷题流程刷题技巧 Day1题目1、0003.无重复字符的最长子串解答&#xff1a;2.00004 寻找两个正序数组的中位数解答&#xff1a;3.0005.最长回文子串解答4.0008.字符串转换整数解答&#xff1a; Day2题目1.0151.反转字符串中的单词解答2.0043.字符串相乘解答3.0…...

【快速入门 LVGL】-- 1、STM32 工程移植 LVGL

目录 一、LVGL 简述 二、复制一个STM32工程 三、下载 LVGL 四、裁剪 源文件 五、工程添加 LVGL 文件 六、注册 显示 七、注册 触摸屏 八、LVGL 心跳、任务刷新 九、开跑 LVGL 十、控件的事件添加、响应处理 十 一、几个好玩小事情 十 二、显示中文 ~~ 约定 ~~ 在…...

Mac使用-快速开始总结(持续更新)

目录 Mac使用-快速开始总结常用快捷键 Mac使用-快速开始总结 第一次使用mac&#xff0c;发现很多细节上和windows不一样&#xff0c;以下是自己遇到的常用总结&#xff0c;帮助自己快速熟悉mac的使用&#xff5e; 常用快捷键 复制、粘贴 快捷键&#xff1f; 复制&#xff1a;…...

Kubernetes (K8s) 入门指南

Kubernetes (K8s) 入门指南 什么是Kubernetes&#xff1f; Kubernetes&#xff0c;通常简称为 K8s&#xff08;因为从 “K” 到 “s” 之间有八个字符&#xff09;&#xff0c;是一个开源的容器编排平台&#xff0c;用于自动化部署、扩展和管理容器化应用程序。它最初由谷歌设…...

归纳webpack

常用配置项 const HtmlWebpackPlugin require(html-webpack-plugin); // 通常用于生成HTML const MiniCssExtractPlugin require(mini-css-extract-plugin);// 用于分离CSS const cssMinimizerWebpackPlugin require("css-minimizer-webpack-plugin"); // 用于压…...

Web APP 阶段性综述

Web APP 阶段性综述 当前&#xff0c;Web APP 主要应用于电脑端&#xff0c;常被用于部署数据分析、机器学习及深度学习等高算力需求的任务。在医学与生物信息学领域&#xff0c;Web APP 扮演着重要角色。在生物信息学领域&#xff0c;诸多工具以 Web APP 的形式呈现&#xff…...

SpringBoot之OriginTrackedPropertiesLoader类源码学习

源码解析 /*** 作用是从给定的资源&#xff08;如文件或输入流&#xff09;中加载 .properties 文件&#xff0c;* 并将属性键值对转换为带有来源信息&#xff08;origin&#xff09;的 OriginTrackedValue 对象。*/ public class OriginTrackedPropertiesLoader {private fin…...

Flask学习入门笔记

Flask学习入门笔记 前言1. 安装Flask2. 创建一个简单的Flask应用3. 路由与视图函数3.1 基本路由3.2 动态路由3.3 HTTP方法 4. 请求与响应4.1 获取请求数据4.2 返回响应 5. 模板渲染5.1 基本模板渲染5.2 模板继承 6. 静态文件6.1 静态文件的目录结构6.2 在模板中引用静态文件6.2…...

List 接口的实现类

在 Java 中&#xff0c;List 是一个非常常用的接口&#xff0c;提供了有序、可重复的元素集合。List 接口有多个实现类&#xff0c;每个实现类都有其特定的特性和适用场景。以下是 Java 中主要实现了 List 接口的类及其详细介绍。 1. 常见的 List 实现类 1.1 ArrayList 简介&…...

SpringCloud-基于Docker和Docker-Compose的项目部署

一、初始化环境 1. 卸载旧版本 首先&#xff0c;卸载可能已存在的旧版本 Docker。如果您不确定是否安装过&#xff0c;可以直接执行以下命令&#xff1a; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logro…...

【人工智能】Python中的自动化机器学习(AutoML):如何使用TPOT优化模型选择

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门&#xff01; 解锁Python编程的无限可能&#xff1a;《奇妙的Python》带你漫游代码世界 随着机器学习在各行业的广泛应用&#xff0c;模型选择和优化成为了数据科学家面临的主要挑战之一。自动化机器学习&am…...

Kafka 超级简述

Kafka 就是一个 分布式的消息系统&#xff0c;它帮助不同的系统和应用之间传递信息。可以把它想象成一个超级高效的 “邮局”&#xff1a; 生产者&#xff08;Producer&#xff09; 就是把信息&#xff08;消息&#xff09;送到这个 “邮局” 的人。消费者&#xff08;Consume…...

tomcat项目运行后报500

HTTP状态 500 - 内部服务器错误 类型 异常报告消息 实例化Servlet类[com.mdy.servlet_02.LifeDemoServlet]异常描述 服务器遇到一个意外的情况&#xff0c;阻止它完成请求。例外情况jakarta.servlet.ServletException: 实例化Servlet类[com.mdy.servlet_02.LifeDemoServlet]异常…...

Chapter1:初见C#

参考书籍&#xff1a;《C#边做边学》&#xff1b; 1.初见C# 1.1 C#简介 C # {\rm C\#} C#编写了许多完成常用功能的程序放在系统中&#xff0c;把系统中包含的内容按功能分成多个部分&#xff0c;每部分放在一个命名空间中&#xff0c;导入命名空间语法格式如下&#xff1a; /…...

SQL-leetcode—620. 有趣的电影

620. 有趣的电影 表&#xff1a;cinema ------------------------ | Column Name | Type | ------------------------ | id | int | | movie | varchar | | description | varchar | | rating | float | ------------------------ id 是该表的主键(具有唯一值的列)。 每行包含…...

《鸿蒙Next平台:决策树面对噪声数据的鲁棒性逆袭》

在机器学习领域&#xff0c;决策树是一种强大的模型&#xff0c;但训练数据中的噪声往往会影响其性能和鲁棒性。在鸿蒙Next平台上&#xff0c;我们可以采用多种策略来增强决策树模型在面对噪声数据时的鲁棒性。 数据预处理层面 数据清洗&#xff1a;利用鸿蒙Next平台的数据处理…...

minio https配置

如果使用了官网的教程 使用minio-user作为启动用户 准备好&#xff1a;文件名要定死&#xff0c;因为minio只认识key和crt文件 将xxxxx.key重命名为private.key 将xxxxx.pem重命名为public.crt 将这两个文件放到/home/minio-user/.minio/certs/ 目录下 chown -r minio…...

Windows重装后NI板卡LabVIEW恢复正常

在重新安装Windows系统后&#xff0c;NI&#xff08;National Instruments&#xff09;板卡能够恢复正常工作&#xff0c;通常是由于操作系统的重新配置解决了之前存在的硬件驱动、兼容性或配置问题。操作系统重装后&#xff0c;系统重新加载驱动程序、清理了潜在的冲突或损坏的…...

Spring Initializr创建springboot项目 “java: 错误: 无效的源发行版:19”

我用的1.8的jdk&#xff0c;排查发现这是jdk和springboot版本冲突导致的。 1、File->Project Structure->Module->source和dependancies改成相应的版本 2、File->Setting->Build,Execution,Deployment->Compiler->Java Compiler->target bytecode ver…...

Vue computed属性原理及其惰性求值特点

1&#xff0c;computed属性 在 Vue.js 中&#xff0c; computed 属性是 Vue 响应式系统的一个核心特性&#xff0c;它允许开发者声明式地描述一个依赖其他响应式属性的计算值。 computed 属性是基于它们的依赖进行缓存的&#xff0c;只有当依赖发生变化时&#xff0c;它们才会…...

关于在 Kotlin DSL 中,ndk 的配置方式

在 Kotlin DSL 中&#xff0c;ndk 的配置方式有所不同&#xff0c;取决于 Android Gradle 插件版本。ndk { abiFilters(…) } 在 Kotlin DSL 中实际上是 externalNativeBuild 的一部分&#xff0c;需要通过正确的上下文调用。 错误代码&#xff1a; ndk {abiFilters("ar…...

机器学习(一)

一, Supervised Machine Learning (监督机器学习) 1,定义:学习X到Y或输入到输出的映射的算法&#xff0c;学习算法从正确答案中学习。即教机器 如何做事情(数据集学习算法模型)&#xff0c;根据已有的数据集&#xff0c;知道输入和输出结果 之间的关系&#xff0c;并根据这…...

得物App再迎开放日,全流程体验正品查验鉴别

近日&#xff0c;得物App超级品质保障中心再度迎来了开放日活动。近60位得物App的用户与粉丝齐聚超级品质保障中心&#xff0c;全流程体验正品查验鉴别。开放日当天&#xff0c;参与者有机会近距离观察得物App的商品质检区、鉴别区、收发流转区、实验室和正品库等关键功能区&am…...

《leetcode-runner》【图解】【源码】如何手搓一个debug调试器——表达式计算

前文&#xff1a; 《leetcode-runner》如何手搓一个debug调试器——引言 《leetcode-runner》如何手搓一个debug调试器——架构 《leetcode-runner》如何手搓一个debug调试器——指令系统 《leetcode-runner》【图解】如何手搓一个debug调试器——调试程序【JDI开发】【万字详解…...

Flink概述

一、Flink是什么 二、Flink特点 三、Flink vs SparkStreaming 表 Flink 和 Streaming对比 Flink Streaming 计算模型 流计算 微批处理 时间语义 事件时间、处理时间 处理时间 窗口 多、灵活 少、不灵活&#xff08;窗口必须是批次的整数倍&#xff09; 状态 有 …...

【Linux】信号

目录 一、信号的概念二、信号的产生2.1 通过键盘进行信号的产生2.2 通过系统调用进行信号的产生2.2.1 kill函数2.2.2 raise函数2.2.3 abort函数 2.3 通过异常的方式进行信号的产生2.4 通过软件条件的方式进行信号的产生2.4.1 关闭管道读端2.4.2 alarm函数 2.5 Core Dump&#x…...

【漏洞分析】DDOS攻防分析

0x00 UDP攻击实例 2013年12月30日&#xff0c;网游界发生了一起“追杀”事件。事件的主角是PhantmL0rd&#xff08;这名字一看就是个玩家&#xff09;和黑客组织DERP Trolling。 PhantomL0rd&#xff0c;人称“鬼王”&#xff0c;本名James Varga&#xff0c;某专业游戏小组的…...

【js进阶】设计模式之单例模式的几种声明方式

单例模式&#xff0c;简言之就是一个类无论实例化多少次&#xff0c;最终都是同一个对象 原生js的几个辅助方式的实现 手写forEch,map,filter Array.prototype.MyForEach function (callback) {for (let i 0; i < this.length; i) {callback(this[i], i, this);} };con…...

【VUE】计算属性+动态样式方法封装

【VUE】父子组件联动实现动态样式控制 【VUE】页面跳转实现动态样式控制 在utils下创建文件夹styleController 编写通用的方法 /*** 样式控制* 本文件主要提供一些动态控制样式的方法*//*** 控制表格表头中的 某些列 是否显示星号** param showStarActions boolean 当值为True时…...

Mac玩Steam游戏秘籍!

Mac玩Steam游戏秘籍&#xff01; 大家好&#xff01;最近有不少朋友在用MacBook玩Steam游戏时遇到不支持mac的问题。别担心&#xff0c;我来教你如何用第三方工具Crossover来畅玩这些不支持的游戏&#xff0c;简单又实用&#xff01; 第一步&#xff1a;下载Crossover 首先&…...

【后端面试总结】tls中.crt和.key的关系

tls中.crt和.key的关系 引言 在现代网络通信中&#xff0c;特别是基于SSL/TLS协议的加密通信中&#xff0c;.crt和.key文件扮演着至关重要的角色。这两个文件分别代表了数字证书和私钥&#xff0c;是确保通信双方身份认证和数据传输安全性的基石。本文旨在深入探讨TLS中.crt和…...

【Axure】配色库

配色库是一个专为设计师和创意工作者打造的在线资源平台&#xff0c;旨在提供丰富的色彩解决方案&#xff0c;帮助用户轻松找到或创造美观和谐的色彩搭配。其中&#xff0c;一个典型的配色库包含了以下几个核心元素&#xff1a; 渐变色&#xff1a;提供多样化的渐变色方案&…...

PL/SQL语言的语法糖

PL/SQL语言的语法糖 引言 PL/SQL&#xff08;Procedural Language/Structured Query Language&#xff09;是Oracle公司为其数据库管理系统&#xff08;DBMS&#xff09;设计的一种过程化语言。作为一种扩展SQL的语言&#xff0c;PL/SQL不仅支持数据的查询和操作&#xff0c;…...