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

【C语言】 指针的进阶 看这一篇就够了

目录
1.字符指针
2.数组指针
3.指针数组
4.数组传参和指针传参
5.函数指针
6.函数指针数组
7.指向函数指针数组的指针
8.回调函数
9.qsort排序和冒泡排序

1.字符指针

让我们一起来回顾一下指针的概念!
1.指针就是一个变量,用来存放地址,地址唯一标识一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台/64位平台)
3.指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作时候的权限。

int main()
{char ch = 'w';char* pc = &ch;//pc就是字符指针char* p = "abcdef";//常量字符串,p中存放的是首字符的地址//*p = 'w';error这是错误的,常量字符串不能被修改return 0;
}

如果我们想修改上面的常量字符串该怎么办呢?方法如下:

int main()
{char arr[] = "abcdef";char* p = arr;*p = 'w';printf("%s\n", arr);//wbcdefreturn 0;
}

一道来自剑指offer的题目:

int main()
{char str1[] = "hello world.";char str2[] = "hello world.";const char* str3 = "hello world.";const char* str4 = "hello world.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

在这里插入片描述

2.指针数组——是数组

存放字符的指针数组——字符指针数组
char* arr3[5];
存放整型指针的数组——整型指针数组
int* arr[6];

int main()
{char* arr[] = { "abcdef","hehe","qwer" };//指针数组int i = 0;for (i = 0; i < 3; i++){printf("%s\n", arr[i]);//每个字符串首字符地址}return 0;
}
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//arr就是一个存放整型指针的数组//arr[i]=*(arr+i)int* arr[] = { 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("%d ", *(arr[i] + j));}printf("\n");}return 0;
}

3.数组指针——是一种指针

数组指针就是指向数组的指针。

int arr[10];
&arr;取出的是数组的地址
int (*pa)[10]=&arr;//pa就是指针,pa指向的是数组arr

int main()
{int arr[10] = { 0 };printf("%p\n", arr);printf("%p\n", &arr[0]);printf("%p\n", &arr);return 0;
}

&数组名和数组名的区别

数组名绝大部分情况下是数组首元素地址,两个例外:
1.&数组名,取出的是整个数组的地址。从地址值角度来讲和数组首元素地址是一样的,但是意义不一样。
2.sizeof(数组名),数组名表示整个数组,计算得到的是数组的大小。

int main()
{int arr[10] = { 0 };int(*p)[10] = &arr;//p是一个数组指针//类型:int(*)[10]//printf("%d", sizeof(arr));printf("%p\n", arr);//int*printf("%p\n", arr+1);//4printf("%p\n", &arr[0]);//int*printf("%p\n", &arr[0]+1);//4printf("%p\n", &arr);//int(*)[10]=&arr;printf("%p\n", &arr+1);//40return 0;
}

在这里插入图片描述
访问数组的几种方法:
使用下标的形式来访问数组:

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

使用指针的方式访问数组:

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;int* p = arr;for (i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}

数组指针什么情况下使用呢?

//一维数组传参,形参是数组
void print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);print(arr, sz);return 0;
}
//一维数组传参,形参是指针
void print(int* arr, int sz)
{int i = 0;for (i = 0; i < sz; i++){//printf("%d ", arr[i]);printf("%d ", *(arr + i));}printf("\n");
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);print(arr, sz);return 0;
}
//二维数组传参,形参部分写成二维数组
void print(int arr[3][5], int r, int c)
{int i = 9;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.首元素的地址就是第一行的地址,是一个一维数组的地址

//二维数组传参,形参部分写成指针
void print(int(*arr)[5], int r, int c)//通过上面引用的解读,所以这里传参,我们只传了第一行的地址,也就是数组指针
{int i = 9;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", *(*(arr + i) + j));//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;
}

4.数组传参和指针传参

一维数组传参,形参可以是数组,也可以是指针
当形参是指针的时候,要注意类型

void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int* arr)//ok
{}
void test2(int* arr[20])//ok
{}
void test2(int** arr)//No
{}
int main()
{int arr[10] = { 0 };int* arr2[20] = { 0 };test(arr);test2(arr2);
}

二维数组传参
参数可以是指针,也可以是数组
如果是数组,行可以省略,但是列不能省略
如果是指针,传过去的是第一行的地址,形参就应该是数组指针

void test(int arr[3][5])//ok
{}
void test(int arr[][])//no
{}
void test(int arr[][5])//ok
{}
void test(int* arr)//no
{}
void test(int* arr[5])//no
{}
void test(int(*arr)[5])//ok
{}
void test(int** arr)//no
{}
int main()
{int arr[3][5] = { 0 };test(arr);
}

一级指针传参

void print(int *p, int sz)
{
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d\n", *(p+i));
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}

二级指针传参

void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
//还可以传一级指针数组的数组名
test(&p);
return 0;
}

函数指针

注意:函数名和&函数名是一样的,二者没有区别。

int Add(int x, int y)
{return x + y;
}
int main()
{printf("%p\n", Add);printf("%p\n", Add);return 0;
}

函数指针的地址要存起来,就要放在函数指针变量中

int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf)(int, int) = Add;//pf就是函数指针变量,类型是int(*)(int,int)
}
int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf)(int, int) = Add;/*int ret = (*pf)(3, 5);int ret = Add(3, 5);*/int ret = pf(3, 5);return 0;
}

注意!!!

typedef void(*pf_t2)(int);//pf_t2是类型名
void(*pf)(int);//pf是函数指针变量的名字

函数指针数组

数组的每个元素是一个函数指针。

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 main()
{//转移表int(*pf[4])(int, int) = { Add,Sub,Mul,Div };//放到函数指针数组里的函数类型应保持一致int i = 0;for (i = 0; i < 4; i++){int ret = pf[i](8, 4);printf("%d\n", ret);}return 0;
}

放到函数指针数组里的函数类型应保持一致,包括参数和返回类型。

指向函数指针数组的指针

int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int main()
{int (*pf)(int, int) = Add;//函数指针数组int (*pfArr[4])(int, int) = { Add,Sub };int (*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个指向函数指针数组的指针return 0;
}

回调函数

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

使用回调函数实现计算器:

//解决了普通语句的冗余
void menu()
{printf("**********************\n");printf("***1.Add      2.Sub***\n");printf("***3.Mul      4.Div***\n");printf("***0.exit           ***\n");
}
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 Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:>\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}
int main()
{int input = 0;do{menu();printf("请输入选项:>\n");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;
}

qsort排序和冒泡排序

冒泡排序:

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;}}}
}
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
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);print_arr(arr, sz);return 0;
}

冒泡排序的缺点是什么呢?
它只能排序整型家族类型,而对于结构体等一些其他类型有一定的限制,所以我们引出qsort快速排序。

qsort排序

qsort可以直接使用。
qsort可以排任意类型的数据。
排什么类型的数据,要排序的数据就直接提供比较方法。

void qsort(void* base,//指向待排序数组的第一个元素size_t num, //待排序的元素个数size_t width, //每个元素的大小,单位是字节int(__cdecl* compare)(const void* elem1, const void* elem2));//指向一个函数,这个函数可以比较两个元素的大小

在这里插入图片描述

在进行举例之前,我们还需要知道下面的知识:
因为qsort可以排任意类型的数据,所以在传参的方面就要注意。

我们在普通的地址接收时,不能出现以下不兼容的情况。

这是错误的:

int main()
{int a = 0;char* p = &a;//errorreturn 0;
}

在这里我们可以使用void * 无具体类型的指针,它可以接收任何类型的地址。但是需要注意:
1.void 的指针不能解引用操作符
2.如果是void
p,也不能进行p++
3.使用时,我们可以将它进行强制类型转换为其他类型的指针再进行解引用使用。

int main()
{int a = 10;void* p = &a;*(int*)p;
}

对于qsort的各参数类型,我们都不陌生,我们为什么要引出上面使用void*进行传参呢?

对于我们程序员来说,我们清楚的知道接下来我们要排序的数据类型是什么,但是在进行传参的过程中,很容易出现参数类型不兼容的情况,所以我们使用void*来接收各个类型的参数,之后按照什么类型排序就将它强制类型转换为什么形式,这样就会很清晰。

测试qsort排序函数排序整数

#include <stdlib.h>
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
int cmp_int(const void* p1, const void* p2)
{//升序return *(int*)p1 - *(int*)p2;//降序//return *(int*)p2 - *(int*)p1;}
test()
{int arr[10] = { 2,1,3,5,6,4,8,7,9,0 };int sz = sizeof(arr) / sizeof(arr[0]);//提供一个比较函数qsort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}
int main()
{test();return 0;
}

测试qsort排序函数排序结构体数据

#include <stdlib.h>
#include <string.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 cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
test()
{struct Stu s[] = { {"zhangsan",30},{"lisi",40},{"wangwu",50} };int sz = sizeof(s) / sizeof(s[0]);//测试按照年龄来排序qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);//测试按照姓名来排序qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{test();return 0;
}

利用冒泡排序的思想来模拟实现qsort功能的冒泡排序函数bubble_sort()。
1.实现整型排序

int cmp_int(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;
}
void Swap(char* buf1, char* buf2, size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void bubble_sort(void* base, size_t num, size_t width, int(*cmp)(const void* p1, const void* p2))
{//确定趟数size_t i = 0;int flag=1;//假设有序for (i = 0; i < num - 1; i++){//一趟冒泡排序的过程size_t j = 0;for (j = 0; j < num - 1 - i; j++){//两个相邻元素的比较//arr[j] arr[j+1]if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)//降序<0{//交换flag=0;Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}if (flag == 1){break;}}}
}
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
int main()
{int arr[10] = { 2,1,3,6,4,5,7,8,9,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);return 0;
}

实现结构体排序:

#include <string.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 cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
void Swap(char* buf1, char* buf2, size_t width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void print_s(struct Stu s[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s %d\n", s[i].name,s[i].age);}
}
void bubble_sort(void* base, size_t num, size_t width, int(*cmp)(const void* p1, const void* p2))
{//确定趟数size_t i = 0;int flag = 1;//假设有序for (i = 0; i < num - 1; i++){//一趟冒泡排序的过程size_t j = 0;for (j = 0; j < num - 1 - i; j++){//两个相邻元素的比较//arr[j] arr[j+1]if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0){//交换flag = 0;Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}if (flag == 1){break;}}}
}
int main()
{struct Stu s[] = { {"zhangsan",30},{"lisi",40},{"wangwu",50} };int sz = sizeof(s) / sizeof(s[0]);bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);print_s(s, sz);return 0;
}

感谢友友们阅读,欢迎批评指正!

相关文章:

【C语言】 指针的进阶 看这一篇就够了

目录 1.字符指针 2.数组指针 3.指针数组 4.数组传参和指针传参 5.函数指针 6.函数指针数组 7.指向函数指针数组的指针 8.回调函数 9.qsort排序和冒泡排序 1.字符指针 让我们一起来回顾一下指针的概念&#xff01; 1.指针就是一个变量&#xff0c;用来存放地址&#xff0c;地址…...

redis set list

Listlist: 插入命令&#xff1a;lpush / rpush 查看list列表所有数据(-1 表示最后一个)&#xff1a;lrange key 0 -1 查看列表长度(key 不存在则长度返回0 ): llen key list长度 获取下表 为 0 的元素 修改下标为0的元素&#xff0c;改为haha 移除列表的第一个元素 或最后一…...

如何解决DNS劫持

随着互联网的不断发展&#xff0c;DNS(域名系统)成为了构建网络基础的重要组成部分。而DNS遭到劫持&#xff0c;成为一种常见的安全问题。那么DNS遭到劫持是什么意思呢?如何解决DNS劫持问题呢?下面就让小编来为您一一解答。 DNS遭到劫持是什么意思? DNS遭到劫持指的是黑客通…...

【LeetCode】剑指 Offer(28)

目录 题目&#xff1a;剑指 Offer 54. 二叉搜索树的第k大节点 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 题目&#xff1a;剑指 Offer 55 - I. 二叉树的深度 - 力…...

「ML 实践篇」模型训练

在训练不同机器学习算法模型时&#xff0c;遇到的各类训练算法大多对用户都是一个黑匣子&#xff0c;而理解它们实际怎么工作&#xff0c;对用户是很有帮助的&#xff1b; 快速定位到合适的模型与正确的训练算法&#xff0c;找到一套适当的超参数等&#xff1b;更高效的执行错…...

域名解析协议-DNS

DNS&#xff08;Domain Name System&#xff09;是互联网上非常重要的一项服务&#xff0c;我们每天上网都要依靠大量的DNS服务。在Internet上&#xff0c;用户更容易记住的是域名&#xff0c;但是网络中的计算机的互相访问是通过 IP 地址实现的。DNS 最常用的功能是给用户提供…...

分享:包括 AI 绘画在内的超齐全免费可用的API 大全

AI 绘画已经火出圈了&#xff0c;你还不知道哪里可以用嘛&#xff1f;我给大家整理了超级齐全的免费可用 API&#xff0c;包括 AI 绘画在内&#xff0c;有需要的小伙伴赶紧收藏了。 AI 绘画/AI 作画 类 AI 绘画&#xff1a;通过AI 生成图片&#xff0c;包括图生文、文生图等。…...

虹科新闻 | 虹科与Overland-Tandberg正式建立合作伙伴关系

虹科Overland-Tandberg 近日&#xff0c;虹科与美国Overland-Tandberg公司达成战略合作&#xff0c;虹科正式成为Overland-Tandberg公司在中国区域的认证授权代理商。未来&#xff0c;虹科将携手Overland-Tandberg&#xff0c;共同致力于提供企业数据管理和保护解决方案。 虹科…...

架构设计三原则

作为程序员&#xff0c;很多人都希望成为一名架构师&#xff0c;但并非简单地通过编程技能就能够达成这一目标。事实上&#xff0c;优秀的程序员和架构师之间存在一个明显的鸿沟——不确定性。 编程的本质是确定性的&#xff0c;也就是说&#xff0c;对于同一段代码&#xff0c…...

Android 性能优化——ANR监控与解决

作者&#xff1a;Drummor 1 哪来的ANR ANR(Application Not responding):如果 Android 应用的界面线程处于阻塞状态的时间过长&#xff0c;会触发“应用无响应”(ANR) 错误。如果应用位于前台&#xff0c;系统会向用户显示一个对话框。ANR 对话框会为用户提供强制退出应用的选项…...

Machine Learning-Ex3(吴恩达课后习题)Multi-class Classification and Neural Networks

目录 1. Multi-class Classification 1.1 Dataset 1.2 Visualizing the data 1.3 Vectorizing Logistic Regression 1.3.1 Vectorizing the cost function&#xff08;no regularization&#xff09; 1.3.2 Vectorizing the gradient&#xff08;no regularization&#…...

【Java】SpringBoot事务回滚规则

SpringBoot事务回滚规则SpringBoot事务回滚规则SpringBoot事务回滚规则 在SpringBoot中&#xff0c;如果一个方法被声明为Transactional&#xff0c;则会开启一个事务。如果这个方法中的任何一个步骤失败了&#xff08;比如抛出了异常&#xff09;&#xff0c;则该事务将会回滚…...

使用cocopod就那么容易

第一节、配置coopod 打开终端替换ruby镜像源&#xff0c;系统自带的镜像源(gem sources --remove https://rubygems.org/)被墙挡住了或者&#xff08;https://ruby.taobao.org/&#xff09;已过期。需替换成新的镜像源。 1&#xff09;.先查看已有的镜像是否是&#xff1a;ht…...

第14届蓝桥杯C++B组省赛

文章目录A. 日期统计B. 01 串的熵C. 冶炼金属D. 飞机降落E. 接龙数列F. 岛屿个数G. 子串简写H. 整数删除I. 景区导游J. 砍树今年比去年难好多 Update 2023.4.10 反转了&#xff0c;炼金二分没写错&#xff0c;可以AC了 Update 2023.4.9 rnm退钱&#xff0c;把简单的都放后面…...

面向对象编程(进阶)3:方法的重写

目录 3.1 方法重写举例 Override使用说明&#xff1a; 3.2 方法重写的要求 3.3 小结&#xff1a;方法的重载与重写 &#xff08;1&#xff09;同一个类中 &#xff08;2&#xff09;父子类中 3.4 练习 父类的所有方法子类都会继承&#xff0c;但是当某个方法被继承到子类…...

2023年第十四届蓝桥杯Java_大学B组真题

Java_B组试题 A: 阶乘求和试题 B: 幸运数字试题 C: 数组分割试题 D: 矩形总面积试题 E: 蜗牛试题 F: 合并区域试题 G: 买二赠一试题 H: 合并石子试题 I: 最大开支试题 J: 魔法阵【考生须知】 考试开始后&#xff0c;选手首先下载题目&#xff0c;并使用考场现场公布的解压密码解…...

APIs --- DOM事件进阶

1. 事件流 事件流指的是事件完整执行过程中的流动路径 任意事件被触发时总会经历两个阶段&#xff1a;【捕获阶段】和【冒泡阶段】 事件捕获 概念&#xff1a;从DOM的根元素开始去执行对应的事件&#xff08;从外到里&#xff09; 捕获阶段是【从父到子】的传导过程 代码&…...

awk命令详解以及使用方法

awk命令详解以及使用方法 awk 是一种文本处理工具&#xff0c;它可以逐行扫描文本文件&#xff0c;根据用户指定的规则进行匹配和处理&#xff0c;并输出结果。awk 的名称来自于三位创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的首字母缩写。 awk 通常用于处理以…...

vue-router3.0处理页面滚动部分源码分析

在使用vue-router3.0时候&#xff0c;会发现不同的路由之间来回切换&#xff0c;会滚动到上次浏览的位置&#xff0c;今天就来看看这部分的vue-router中的源码实现。 无论是基于hash还是history的路由切换&#xff0c;都对滚动进行了处理&#xff0c;这里分析其中一种即可。 无…...

走心Python实战应用:【requests+re 模块】快速下载原shen图片

人生苦短&#xff0c;我用python 这次给大家带来的是模块实战 以便大家理解学习 觉得写的好的话&#xff0c;可以给我多多点赞鸭~ 走心Python实战应用&#xff1a;【requestsre 模块】快速下载原shen图片一、理解Python requests 模块二、requests 方法三、ruqusets 模块实战…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

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

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