进阶指针——(2)

news2024/12/24 0:16:31

本次讲解重点:

6.  函数指针数组

7.  指向函数指针数组的指针

8.  回调函数

在前面我们已经讲解了进阶指针的一部分,我们回顾一下在进阶指针(1)我们学过的难点知识点:

int my_strlen(const char* str)
{
 return 0;
}

int main()
{
 //指针数组-数组
 //数组的定义:数组元素的类型 数组名 [常量表达式]
 //常量表达式:用来表示数组中元素的个数,即数组的大小(长度)
 char* ch[5];//指针数组,数组5个元素,每个元素的类型是char*

 //数组指针-指针
 //注意:[]的优先级要高于*的,所以必须加上()来保证指针变量先与*结合
 //对数组指针的理解:变量先和*结合,说明变量是一个指针变量,
 //     然后在前面的就是所指向数组的元素类型
 //     后面的[]是所指向的数组的大小
 //指针变量前的第一个*与变量结合表示它是指针,
 //再往前面的所用东西表示这个指针所指向对象的类型
 int arr[10];//整形数组,数组10个元素,每个元素的类型是int
 int(*pa)[10] = &arr;//指针数组,该指针指向一个数组
 //数组10个元素,每个元素是int类型

 //函数指针-指针
 //和数组指针类似,要注意操作符的优先级,所以必须加上()来保证变量先与*结合
 //对函数指针的理解:
 // 变量先与*结合,说明变量是一个函数指针,
 // 然后前面的是所指向函数的返回类型,后面的()是所指向函数的参数列表
 int (*pf)(const char*) = my_strlen;//函数指针,该指针指向一个函数
 //函数的返回类型是int,函数的形参列表为(const char*)

 return 0;
}

没看过的可以点下面链接观看:

指针的进阶——(1)_wangjiushun的博客-CSDN博客

接下来我们继续学习关于进阶指针的知识:


6.  函数指针数组

数组是一个存放相同类型的存储空间,在前面我们学习了指针数组。

比如:存放整形指针的数组

        int* arr[10];//arr先与[]结合,说明arr是数组,数组的元素类型是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组(即:函数指针数组-存放函数指针(地址)的数组)。

那函数指针数组如何定义呢?

        int (*parr[10])();

        //parr先和[]结合,说明parr是数组;数组的元素类型是int(*)()的函数指针

先回顾:数组的定义:数组的元素类型 数组名 [数组的大小]

函数指针数组的定义:

        ①数组名先和[]结合,说明它是一个数组;

        ②然后数组名和[数组的大小]移到函数指针的*后面

函数指针数组的写法:因为函数指针数组是在函数指针的基础上写出来的,所以我们①先写出函数指针;②再写数组名和数组大小。

例子:写一个计算器整数+ - * /

代码1:一般写法

//头文件
#include<stdio.h>
#include<windows.h>//预处理,对Sleep的声明
#include<stdlib.h>//预处理,对system的声明
//自定义函数-实现打印菜单
void menu()
{
	printf("*****************************************************\n");
	printf("********           1.add     2.sub           ********\n");
	printf("********           3.mul     4.div           ********\n");
	printf("********               0.exit                ********\n");
	printf("*****************************************************\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 main()
{
	int input = 0;//功能变量
	int x = 0;//操作数
	int y = 0;//操作数
	int ret = 0;//接收函数返回值
	//每次进入计算器至少打印一次菜单,选择功能
	do
	{
		//打印菜单
		menu();
		//提示输入功能
		printf("请选择:>");
		scanf("%d", &input);
		//switch选择语句-表达式与case标签后的常量相等,就执行该case后的语句
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;//实现分支
		case 2:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		//都不符合,执行default语句
		default:
			printf("选择错误!\n");
			break;//写不写均可
		}
		//每次执行完一次计算器清屏
		Sleep(2000);//Sleep函数是实现睡眠,单位是毫秒
		system("cls");//system是一个库函数,可以执行系统命令,cls是清屏的命令
	} while (input);
	return 0;
}

我们发现:

switch语句,随着功能增加,case增加,并且有部分功能代码重复,所以造成代码冗长。

优化:使用if多分支语句替换。

计算器的功能函数的返回类型和参数一样。

优化:使用函数指针数组——存放函数指针(地址)

代码2:转移表

//头文件
#include<stdio.h>
#include<windows.h>//预处理,对Sleep的声明
#include<stdlib.h>//预处理,对system的声明
//自定义函数-实现打印菜单
void menu()
{
	printf("*****************************************************\n");
	printf("********           1.add     2.sub           ********\n");
	printf("********           3.mul     4.div           ********\n");
	printf("********               0.exit                ********\n");
	printf("*****************************************************\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 main()
{
	int input = 0;//功能变量
	int x = 0;//操作数
	int y = 0;//操作数
	int ret = 0;//接收函数返回值
	//每次进入计算器至少打印一次菜单,选择功能
	do
	{
		//打印菜单
		menu();
		//提示输入功能
		printf("请选择:>");
		scanf("%d", &input);
		//计算器功能函数的返回类型,参数一样——使用函数指针数组
		//数组下标从0开始,由题意0退出计算器,NULL的本质是0
		int (*pf[5])(int, int) = { NULL, Add, Sub, Mul, Div };
		//if多分支选择语句
		if (input == 0)
		{
			printf("退出计算器\n");
		}
		else if(input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pf[input](x, y);//通过函数指针数组,找到函数地址,然后调用函数
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}
		//每次执行完一次计算器清屏
		Sleep(2000);//Sleep函数是实现睡眠,单位是毫秒
		system("cls");//system是一个库函数,可以执行系统命令,cls是清屏的命令
	} while (input);
	return 0;
}

函数指针数组——用途:转移表

理解:转移表——数组有跳转的含义:你给我一个下标,我通过下标找到数组里某个函数的地址,然后去调用函数,所以函数指针的用途叫转移表。

7.  指向函数指针数组的指针

指向函数指针数组的指针——指针

指针指向一个数组,数组的元素都是函数指针。

那我们怎么定义呢?

#include<stdio.h>

void test()
{
	printf("hehe\n");
}

int main()
{
	//函数指针
	//①变量先与*结合,说明变量是一个指针;
	//②然后前面的是所指向函数的返回类型,后面的()是所指向函数的参数列表
	void (*pfun)() = test;

	//函数指针数组——数组
	//写法:①先写函数指针;
	//		②在写数组名和数组大小
	void (*pfunArr[5])() = { test };//pfunArr先与[]结合,说明pfunArr是数组

	//函数指针数组的指针——指针
	//写法:①先写函数指针数组
	//		②在写指针变量
	void (*(*ppfunArr)[5])() = &pfunArr;//*与ppfunArr先结合,说明ppfunArr是指针

	return 0;
}

总结:函数指针数组是在函数指针的基础上写的,函数指针数组的指针是在函数指针数组的基础上写的。

8.  回调函数

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

简单的说:回调函数就是通过函数指针调用的函数。

例子1:计算器

那我们怎么解决呢?

我们发现只有调用的计算函数函数名不同,函数返回类型和函数参数相同,把计算函数写成函数指针作为calc函数的参数,即是回调函数的应用。如下:

//头文件
#include<stdio.h>
#include<windows.h>//预处理,对Sleep的声明
#include<stdlib.h>//预处理,对system的声明
//自定义函数-实现打印菜单
void menu()
{
	printf("*****************************************************\n");
	printf("********           1.add     2.sub           ********\n");
	printf("********           3.mul     4.div           ********\n");
	printf("********               0.exit                ********\n");
	printf("*****************************************************\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;
}

//自定义函数-实现调用不同计算的函数
void calc(int (*pf)(int, int))
{
	int x = 0;//操作数
	int y = 0;//操作数
	int ret = 0;//接收函数返回值
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}

int main()
{
	int input = 0;//功能变量
	//每次进入计算器至少打印一次菜单,选择功能
	do
	{
		//打印菜单
		menu();
		//提示输入功能
		printf("请选择:>");
		scanf("%d", &input);
		//switch选择语句-表达式与case标签后的常量相等,就执行该case后的语句
		switch (input)
		{
		case 1:
			calc(Add);//传Add函数的地址
			break;//实现分支
		case 2:
			calc(Sub);
			break;
		case 3:
			cals(Mul);
			break;
		case 4:
			calc(Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		//都不符合,执行default语句
		default:
			printf("选择错误!\n");
			break;//写不写均可
		}
		//每次执行完一次计算器清屏
		Sleep(2000);//Sleep函数是实现睡眠,单位是毫秒
		system("cls");//system是一个库函数,可以执行系统命令,cls是清屏的命令
	} while (input);
	return 0;
}

例子2:使用回调函数,模拟实现qsort(采用冒泡的方式)

qsort回调函数经典的例子。

我们先回顾一下冒泡排序:

代码1:对整数数组冒泡排序

 总结:冒泡排序

①冒泡排序:我们首先确定趟数,再确定一趟冒泡排序的过程。

②冒泡排序,趟数控制了一趟冒泡排序要进行多少对比较。

③有n个元素,要进行i(i=n-1)趟冒泡,一趟比较i-1对元素。

#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 - 1 - i; j++)
		{
			//升序
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	//对数组进行排序,升序
	int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	//计算数组的元素个数
	int sz = sizeof(arr) / sizeof(arr[0]);
	//调用冒泡排序函数,实现对数组排序,升序
	bubble_sort(arr, sz);
	//输出
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	return 0;
}

因为函数的参数类型已经固定为int了,所以只能排序整数数组。

如果现在我们要比较字符、浮点型、结构体数组,那怎么办呢?

答案是:我们发现冒泡排序进行排序的趟数和每一趟比较的元素对数是不变的,但是不同类型的比较方式不一样,交换的方式不一样;那想比较任意类型的数据,最好是不是把比较两个元素的方法抽离出来成为一个独立的部分(函数)就可以了。

我们模拟在C语言的库函数中有一个排序函数,qsort。qsort是通过快排来实现的,后期会在算法中讲解,今天就先来了解qsort的函数原型和怎么使用即可。

//qsort函数原型:
// void qsort(void* base,
//        size_t num,
//        size_t width,
//        int(__cdecl* compare)(const void*elem1, const void*elem2)
//            );
// 我们简化一下:
//void qsort(void* base,//指向要排序的数组的第一个元素的地址,即待排序数组的起始地址
//        size_t num,//由base指向的数组中元素的个数
//        size_t width,//数组中每个元素的大小(以字节为单位),即一个元素几个字节
//        int(* cmp)(const void*e1, const void*e2)//函数指针,指向两个元素的比较函数
//            );
//对int(* cmp)(const void*e1, const void*e2)函数指针的解读:
//    (1)cmp——指向一个比较函数(自己设计该函数的比较方式),
//            注意该函数的两个形参必须是const void*型
//            同时再调该函数时,传入的实参也必须转换成const void*型。
//            在该函数内部会将const void*型转换成实际类型
// (2)我们发现参数的类型是const void*:
//        ①const——e1不能修改
//        ②void*——设计qsort函数的设计者,不知道你要比较什么类型的元素类型
//                    所以设计成void*,void*是无具体类型指针
//                    void*的好处:可以存储任意类型的地址
//                    void的坏处:不能直接使用,因为无具体类型,自己都不知道是什么类型
//                        那怎么使用void*呢?
//                        答案是:强制类型转换,在设计比较函数时
//                            我们自己知道要进行排序的时候,是什么类型的数据排序,
//    (3)e1——你要比较的两个元素的第1个元素的地址
//    (4)e2——你要比较的两个元素的另一个元素的地址
//    (5)返回类型:
//            ①如果e1>e2,返回>0,那么e1所指向元素会排在e2所指向元素的右面
//            ②如果e1=e2,返回=0,那么e1和e2所指向元素的顺序不确定
//            ③如果e1<e2,返回<0,那么e1所指向元素会排在e2所指向元素的左面

首先我们演示几个qsort函数的使用:

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

//我们自己知道是什么类型的数据进行排序,
//自定义函数—— 实现对整数的比较,
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;//升序e1-e2,反之降序e2-e1
}

//①使用qsort对整形数组的排序,升序
void test1()
{
	//定义整形数组,并初始化
	int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	//计算数组的大小
	int sz = sizeof(arr) / sizeof(arr[0]);
	//调用qsort函数,对数组进行升序
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	//打印升序后的数组
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	//打印完换行
	printf("\n");
}

//创建简单的学生结构体
struct stu
{
	char name[20];//名字
	int age;//年龄
};

//比较学生,复杂的对象,我们要按照具体的方式进行比较,如该结构体
// ①按照学生的年龄来排序——注意返回类型和参数要与qsort的一致
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);
}
//使用qsort,对结构体数组进行升序
void test2()
{
	//创建结构体数组,并赋初值
	struct stu s[3] = { {"zhangsan",17},{"lisi",18},{"wangwu",19} };
	//调用qsort,
	//①按照学生年龄来升序
	qsort(s, 3, sizeof(s[0]), cmp_stu_by_age);
	//②按照学生名字来升序
	//qsort(s, 3, sizeof(s[0]), cmp_stu_by_name);
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s %d\n", s[i].name, s[i].age);
	}
}

int main()
{
	//调用test1测试
	test1();
	test2();

	return 0;
}

有了以上的基础,现在开始用冒泡排序模拟qsort。

1、函数参数的设计:

void bubble_sort(void* base,//为了接收任意类型的地址,所以是void*
    size_t num,//排序数组,要知道元素个数
    size_t width,//第一个参数的类型是void*不知道元素类型,
                 //想要知道跳过一个元素几个字节,就需要知道一个元素类型几个字节
    int(*cmp)(const void* e1, const void* e2)//因为不同类型的数据比较方式不同,所以把它
                    //抽离出来,并且参数类型为void*——函数指针指向一个比较函数,

                  //使用函数指针调用该函数
            )

//自定义函数——实现对任意类型数据的交换
void Swap(char*buf1,char*buf2,int 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* e1, const void* e2))
{
	//趟数
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			//调用cmp所指向的比较函数
			if (cmp((char*)base+j*width,(char*)base+(j+1)*width) > 0)
			{
				//交换
				//元素类型不知道,我们就一个字节一个字节的交换,所以传参时也要传width
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

如上就是使用冒泡排序模拟qsort函数的函数,可以实现对任意类型数组的排序。

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

本次知识点总结:

6.  函数指针数组——存放函数指针(地址)的数组

(1)定义:

                ①数组名先和[]结合,说明它是一个数组;

                ②然后数组名和数组大小移到函数指针的*的后面

(2)用途:转移表——数组有跳转的含义:你给我一个下标,我通过下标找到数组里某个函数的地址,然后去调用函数,所以函数指针的用途叫转移表。

7.指向函数指针数组的指针

        解读:指针,指针指向数组,数组的元素都是函数指针。

小结:函数指针数组是在函数指针的基础上书写的,函数指针数组的指针是在函数指针数组的基础上书写的,只需注意谁先与谁结合,变量先与*结合就是指针,先与[]结合的是数组。

8.回调函数

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

        ②经典例子:qsort函数

加油站:void*

        ①void*的好处:可以存储任意类型的指针(地址)

        ②void*的坏处:不能直接使用,只用先强制转换为一个明确的类型,才能使用。

指针进阶的主题的知识点就完结了,在下一次文章进行指针进阶的一些笔试题讲解。

有什么不足希望大家指出,我会更加努力写出更好的文章。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

创宇盾重保经验分享,看政府、央企如何防护?

三月重保已经迫近&#xff0c;留给我们的准备时间越来越少&#xff0c;综合近两年三月重保经验及数据总结&#xff0c;知道创宇用实际案例的防护效果说话&#xff0c;深入解析为何创宇盾可以在历次重保中保持“零事故”成绩&#xff0c;受到众多部委、政府、央企/国企客户的青睐…

HACKTHEBOX——Irked

nmapnmap -sV -sC -Pn -T4 -oA nmap 10.10.10.117可能是因为网络原因&#xff0c;与目标链接并不稳定&#xff0c;因此添加了参数-Pn&#xff0c;也只扫描了常见的端口扫描可以看到只开启了3个端口&#xff0c;22,80和111。但是在访问web时&#xff0c;页面提示运行着irc因此再…

WebRTC新增FFmpeg视频编解码模块

1、整体描述目前webrtc内置的视频编解码器包括&#xff1a;VP8、VP9、AV1和H264。一般情况下载pc端基本可以满足大部分的需求&#xff0c;但是有时候为了进行编解码器的扩展包括支持H265或者是支持硬件编解码以提升效率时需要新增编解码模块。2、新增外部编码器编码器实现的要点…

亿万级海量数据去重软方法

文章目录原理案例一需求&#xff1a;方法案例二需求&#xff1a;方法&#xff1a;参考原理 在大数据分布式计算框架生态下&#xff0c;提升计算效率的方法是尽可能的把计算分布式话、并行化&#xff0c;避免单节点计算过载&#xff0c;把计算分摊到各个节点。这样解释小白能够…

最新|移动机器人导航定位技术概述

前言目前工业界广泛落地使用的移动机器人&#xff0c;除了应用场景在餐厅、酒店、超市等小范围室内送餐机器人和消毒机器人外&#xff0c;另外一个“大赛道”应用场景就是在工厂、制造装配车间、电站或车站的物流搬运机器人和巡检机器人了。而在最开始&#xff0c;一切都得从AG…

spring cloud gateway (五)

Gateway简介 Spring Cloud Gateway是Spring公司基于Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代Netflix Zuul&#xff0c;其不仅提供统一的路由方式…

java 字典

java 字典 数据结构总览 Map Map 描述的是一种映射关系&#xff0c;一个 key 对应一个 value&#xff0c;可以添加&#xff0c;删除&#xff0c;修改和获取 key/value&#xff0c;util 提供了多种 Map HashMap: hash 表实现的 map&#xff0c;插入删除查找性能都是 O(1)&…

MySQL跨服务器数据映射

MySQL跨服务器数据映射环境准备1. 首先是要查看数据库的federated引擎 开启/关闭 状态2. 打开任务管理器&#xff0c;并重启mysql服务3. 再次查看FEDERATED引擎状态&#xff0c;引擎已启动映射实现问题总结在日常的开发中经常进行跨数据库进行查询数据。 同服务器下跨数据库进…

【SpringCloud】SpringCloud详解之Feign实战

目录前言SpringCloud Feign远程服务调用一.需求二.两个服务的yml配置和访问路径三.使用RestTemplate远程调用(order服务内编写)四.使用Feign远程调用(order服务内配置)五.自定义Feign配置(order服务内配置)六.Feign配置日志(oder服务内配置)七.Feign调优(order服务内配置)八.抽…

STM32之 串口

串口通信串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方 式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简 单&#xff0c;只…

Vue基础入门讲义(三)-指令

文章目录1.什么是指令&#xff1f;2.插值表达式2.1.花括号2.2.插值闪烁2.3.v-text和v-html3.v-model4.v-on4.1.基本用法4.2.事件修饰5.v-for5.1.遍历数组5.2.数组角标5.3.遍历对象6.key7.v-if和v-show7.1.基本使用7.2.与v-for结合7.3.v-else7.4.v-show8.v-bind8.1. 属性上使用v…

服务器处理发生异常:java.text.ParseException: Unparseable date

测试上传报文的时候遇见报错 服务器处理发生异常:java.text.ParseException: Unparseable date: “2023/03/03” 错误报文 实际需要的报文 错误原因 上传时间字段&#xff0c;与Date字段数据位数不匹配&#xff0c;Java类型&#xff1a;Date默认带有年月日 时分秒yyyy-mm-dd…

十年业务开发总结,如何做好高效高质量的价值交付

作者&#xff1a;杨博林 阿里大淘宝场景金融团队 软件交付是一个非常复杂的过程和体系&#xff0c;需要保障好每个阶段的质量和效率才能保障最终的质量和效率。本文将尝试从需求交付的前、中、后三个环节来阐述一下如何做高效高质量的价值交付。 一、背景 转眼间已经做了十年的…

JavaScript基础内容

日升时奋斗&#xff0c;日落时自省 目录 1、基础语法 2、DOM 2.1、选中页面元素 2.2、获取/修改元素内容 3、JS案例 3.1、网页版本猜数字 3.2、网页版表白墙 JS最初只是为了进行前端页面的开发后来JS也被赋予了更多的功能&#xff0c;可以用来开发桌面程序&#xff0c;手…

RHCSA-重置root密码(3.3)

方法1&#xff1a;rd.break &#xff08;1&#xff09;首先重启系统&#xff0c;在此页面按e键&#xff0c;在屏幕上显示内核启动参数 &#xff08;2&#xff09;知道linux这行&#xff0c;末尾空格后输入rd.break&#xff0c;然后按ctrlx &#xff08;3&#xff09;查看&#…

电脑桌面上的图标不见了怎么办?5个完美的解决技巧

案例&#xff1a;电脑桌面不显示任何东西&#xff1f; “救命&#xff01;电脑打开后&#xff0c;只有桌面&#xff0c;任何图标都没有怎么办&#xff1f;心急&#xff0c;不知道该怎么解决&#xff1f;” 电脑桌面上的图标消失是一个比较常见的问题&#xff0c;许多用户都会…

Hadoop集群启动从节点没有DataNode

目录 一、问题背景 二、解决思路 三、解决办法&#xff1a; 一、问题背景 之前启动hadoop集群的时候都没有问题&#xff0c;今天启动hadoop集群的时候&#xff0c;从节点的DataNode没有启动起来。 二、解决思路 遇见节点起不来的情况&#xff0c;可以去看看当前节点的日志…

各大加密算法对比(原理、性能、安全、运用)

原理按加密可逆可以分为&#xff1a;加密可逆算法和加密不可逆算法。加密可逆算法又可以分为&#xff1a;对称加密和非对称加密。1、加密不可逆算法&#xff1a;一般采用hash算法加密&#xff0c;其原理一般是将原文长度补位成64的倍数&#xff0c;接着初始化固定长度的缓存值&…

大数据框架之Hadoop:MapReduce(五)Yarn资源调度器

Apache YARN (Yet Another Resource Negotiator) 是 hadoop 2.0 引入的集群资源管理系统。用户可以将各种服务框架部署在 YARN 上&#xff0c;由 YARN 进行统一地管理和资源分配。 简言之&#xff0c;Yarn是一个资源调度平台&#xff0c;负责为运算程序提供服务器运算资源&…

Windows Cannot Initialize Data Bindings 问题的解决方法

前言 拿到一个调试程序, 怎么折腾都打不开, 在客户那边, 尝试了几个系统版本, 发现Windows 10 21H2 版本可以正常运行。 尝试 系统篇 系统结果公司电脑 Windows 8有问题…下载安装 Windows10 22H2问题依旧下载安装 Windows10 21H2问题依旧家里的 笔记本Window 11正常 网上…