【C进阶】回调函数(指针进阶2,详解,小白必看)

news2024/11/15 9:36:02

目录

6. 函数指针数组

6.1简单计算器

6.2函数指针数组实现计算器

7. 指向函数指针数组的指针(仅作了解即可)

8.回调函数

8.1关于回调函数的理解​编辑

8.1.1用回调函数改良简单计算器

8.2qsort库函数的使用

8.2.1冒泡排序

8.2.2qsort的概念

8.3冒泡排序思想实现qsort


         这篇文章包括但不限于函数指针数组指向函数指针数组的指针,回调函数等知识点的总结。承接着上文指针进阶(1)知识点总结,传送门-- > http://t.csdn.cn/mgVGJ

指针进阶(3):指针和数组笔试题解析总结,传送门--> http://t.csdn.cn/aKVsj 

        如有错误,欢迎大家指点与纠正,感谢你的来访!

6. 函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:
int * arr [ 10 ];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int ( * parr1 [ 10 ])();//1
int * parr2 [ 10 ]();//2
int ( * )() parr3 [ 10 ];//3

 答案是:parr1 //1

parr1 先和 [] 结合,说明 parr1 是数组,数组的内容是什么呢?
int (*)() 类型的函数指针。-->对于一个变量来说去掉它的名字,就是它的类型。
int my_strlen(const char* str)
{
	return 0;
}
int main()
{
  //指针数组
	char* arr[10];
  //数组指针
	int arr2[5] = { 0 };
	int(*p)[5] = &arr2;//p是一个指向数组的指针变量

	//函数指针
    int (*pf)(const char*) = my_strlen;//pf是一个指向函数的函数指针变量
	
	//函数指针数组-存放函数指针的数组
	int (*pfArr[10])(const char*);
    //1.pf先与[10]结合,代表这是一个数组,数组有10个元素;
    //2.去掉pf[10],剩余int (*)(int,int)为数组每个元素的类型--函数指针类型

	return 0;
}

以下三种写法均等价
1.   *(*pf)("abcdef");
2.   pf ("abcdef");
3.   my_strlen("abcdef");

我们通过实现一个计算器的函数来说明,函数指针数组的用途用在哪里:

6.1简单计算器

按照咱们正常的思路,写一个简单的计算器实现整数的加减乘除的功能,应该大致写成这种代码:

那么执行一下,看一下状况如何:

以上出现的问题是,输入0之后,没有立即打印"退出计算器"然后结束程序,而是要继续输入两个操作数之后才打印信息,还顺便打印了个6,这样的代码肯定存在问题,咱们稍微改进一下。

//写一个计算器能完成整数的+ - * /
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;
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		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 :
			printf("选择错误\n");
			break;
		}
	}while(input);
	return 0;
}

执行代码,继续测试:

 问题是解决了,可是又发现新的问题:

 以上的代码的switch case语句太多了,当我想要在这个计数器里面增加: << >> & | ^ && ||等功能,case语句的就会越来越多,那应该怎么去纠正呢?

这时候就要用到函数指针数组了。

例子:

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 };
	                        // 0    1   2   3
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int ret = pf[i](8, 4);
		printf("%d\n", ret);
	}

	return 0;
}

这段代码使用了函数指针数组,将四个运算函数的地址存储在数组中,然后通过循环遍历数组,依次调用四个运算函数进行计算并输出结果。这种方式可以减少代码的重复量,提高代码的可维护性。

 6.2函数指针数组实现计算器

//函数指针数组实现计算器
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    4
	//NULL的作用是为了占用0下标,然后让函数指针数组的元素下标对应上菜单的功能
	do {
		//菜单
		menu();
		//进行选择
		printf("请选择:>");
		scanf("%d", &input);
		//用函数指针数组代替switch
		if (input == 0)
		{
			printf("退出计算器\n");
			return 0;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d",&x,&y);
			ret = (***pfArr[input])(x, y);//其实比函数指针的用法就多了一个[input]
			 //跟函数指针一样,调用函数的时候 * 是没有任何意义的
			printf("%d\n",ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

函数指针数组是一个数组,其中的元素是函数指针。每个函数指针指向一个特定的函数,可以通过函数指针调用相应的函数。 函数指针数组可以用来解决代码冗余的问题,可以方便地遍历、调用不同的函数,从而简化代码的结构和逻辑,提高代码的可维护性和可扩展性。

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

7. 指向函数指针数组的指针(仅作了解即可)

//指向函数指针数组的指针
int Add(int a, int b)
{
	return a + b;
}
int Sub(int a, int b)
{
	return a - b;
}
int main()
{   //函数指针
	int(*pf)(int, int) = Add;
	//函数指针数组
	int (*pfArr[4])(int, int) = { Add,Sub };
	//指向函数指针数组的指针
	 int (*(*ppfArr)[4])(int,int) = &pfArr;

	 return 0;
}

如何理解指向函数指针数组的指针:

8.回调函数

概念

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

存在着较多逻辑相同,代码冗余的部分。

 这时候就需要用到回调函数解决问题了:

8.1关于回调函数的理解

8.1.1用回调函数改良简单计算器

#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;
}
//将冗余重复部分用Calc函数封装(只用了一个函数),通过这个函数里面传入的函数指针调用计算器函数
void Calc(int(*pf)(int, int))//pf函数指针,传过来哪个函数地址就用哪种类型计算
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
    ret = pf(x, y);
	printf("%d\n", ret);
}
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 ret = 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);
	return 0;
}

总结:

回调函数:被作为参数传递的函数,Add、Sub、Mul、Div四个函数就是回调函数,

而Calc函数则是工具人

8.2qsort库函数的使用

8.2.1冒泡排序

回顾一下冒泡排序的过程:

冒泡排序的思想:两两相邻的元素进行比较,假设要排成升序

怎么去写冒泡排序的代码:
①由元素个数确定趟数

②由趟数确定比较对数         

③两个元素两两交换排成升序

//冒泡排序 
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]);
	}
	printf("\n");
}
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);

	print_arr(arr, sz);
	return 0;
}

不足:

但是这个冒泡排序的缺点也很明显,就是只能排整型int

如果遇到浮点型、结构体等类型的数据,就排不了,那么怎么可以解决呢?

这时候就要用到qsort了。

8.2.2qsort的概念

qsort-- quicksort

是一个库函数,是用来排序的库函数使用快速排序的方法

qsort的好处是

1.现成的

2.可以排序任意类型的数据

 

qsort是可以排序任意类型的数据

1.比较2个整数的大小,> < ==

//qsort函数的使用者提供这个函数
//qsort 默认是升序
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
    //排成倒序的话,什么都不用动,只需要return *(int*)p2 - *(int*)p1;//调换顺序即可
}
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
test1()
{
	int arr[] = { 3,1,5,2,4,9,8,6,5,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//使用qsort来排序整型数组,这里就要提供一个比较函数,这个比较函数能够比较2个整数的大小
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}
int main()
{
   test1();
}

 

2.比较2个字符串,strcmp -->回顾字符串知识:http://t.csdn.cn/V7E9a

3.比较2个结构体数据(学生:张三、李四)指定比较的标准

回顾结构体对象访问成员的知识:http://t.csdn.cn/DVEVj

//测试qsort 排序结构体数据
struct Stu {
	char name[20];
	int age;
};
void print_arr1(struct Stu* arr, int sz)//打印年龄
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		//printf("%d ", (*(arr + i)).age);
		//printf("%d ", (arr+i)->age);
		printf("%d ", arr[i].age);

	}
	printf("\n");
}
void print_arr2(struct Stu*arr, int sz)//打印姓名
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", (arr+i)->name);
		//printf("%s ", (*(arr+i)).name);
		//printf("%s ", arr[i].name);

	}
	printf("\n");
}
//按照年龄来比较
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 test2()
{
	struct Stu s[] = { {"zhangsan",30},{"lisi",25},{"wangwu",50} };
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按照年龄来排序
	print_arr1(s, sz);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	print_arr1(s, sz);
	//测试按照名字来排序
	/*print_arr2(s,  sz);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	print_arr2(s, sz);*/
}

打印年龄:

打印性别:

8.3冒泡排序思想实现qsort

使用冒泡排序的思想来实现一个类似于qsort这个功能的冒泡排序函数bubble_sort()

各参数组成:
base,num,width,cmp函数,elem1和elem2

接下来说明一下qsort各参数的设计思路:

①为什么base定义为void*类型?

void*是 C 语言中的一种通用指针类型,可以指向任意类型的数据。在 qsort 函数中,由于需要对不同类型的数据进行排序,因此需要使用void*类型的指针,以便能够接受各种类型的数据,base参数是一个指向要排序数组第一个元素的指针。由于它要指向任意类型的数据,所以使用void*是最通用的。

举例:

int a = 10;

void* p = &a;

优点:

无具体类型的指针,所以它可以接收任何类型的地址

缺点:

*p  //-->  void*的指针不能解引用操作符

p++  //-->  因为不知道是什么数据类型,不能直接++

正确用法:

*(int*)p;//也可以转化成其它类型

②为什么num要设计成size_t?

  • size_t 在 C 语言中是一种无符号整数类型,用于表示大小、长度和索引值,使用它表示数组元素个数很合适。
  • 数组元素个数是一个非负的值,使用无符号类型size_t可以避免出现负数,更加合理。

  • 使用专门的 size_t 类型比简单的 unsigned int 更能表明这个参数的语义 - 它表示一个大小或长度,而不是一个整数。

③为什么width要设计成size_t

在qsort函数中,width参数表示每个数组元素的大小(以字节为单位)

  1. width表示一个"大小"的概念,使用size_t类型可以更清楚地表达这个参数的语义。

  2. width的单位是字节,size_t通常是无符号整数类型,不会有负数,所以更适合表示正的值。

  3. width需要足够大的范围来表示任意数据类型的大小。size_t类型依赖平台,但通常是机器字大小,能满足大多数数据元素的大小需求。

  4. 在访问数组元素时,索引位置需要乘以元素大小才能获得地址偏移量。既然索引是size_t类型,那么两者相乘的结果也应该是size_t类型,以避免溢出问题。

④为什么要设计一个函数指针cmp?

  1. qsort 本身是一个通用的排序函数,不能知道要排序的元素类型,也就无法直接比较两个元素。采用函数指针可以将比较操作的实现留给用户。

  2. 通过函数指针,用户可以自行实现针对自己数据类型的比较函数,将具体的比较逻辑封装起来。这提高了qsort的通用性和灵活性。

  3. 对于不同的数据类型,比较操作的逻辑可能不同。使用函数指针实现可以避免在qsort中写大量的条件分支逻辑。

  4. 函数指针提供了一种扩展机制。如果用户需要改变比较操作的逻辑,只需要传入一个新的函数指针就可以,而不需要改动qsort函数本身。

⑤为什么elem1和elem2类型是const void*类型?

  1. qsort要对任意类型的数据进行排序,所以比较函数需要能处理任意类型的元素。使用void*可以指向任意类型的数据,const用于表示不应修改指针指向的内容。

  2. qsort要对任意类型的数据进行排序,所以比较函数需要能处理任意类型的元素。使用void*可以指向任意类型的数据,const用于表示不应修改指针指向的内容。

  3. 在调用qsort时可以直接将数组元素的地址强制转换为const void* 类型传给比较函数,简化调用。

  4. qsort函数本身不需要了解元素具体类型,只要把void*指针传给比较函数,由比较函数转换并解释指针内容即可。

开始模拟实现:冒泡排序大题思路不需要改变,只需要对排序方式进行即可

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}
//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
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++;
	}
}

//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
void bubble_sort(void* base, size_t num, size_t width,
	int (*cmp)(const void* p1, const void* p2))
{
    //冒泡排序大题思路不需要改变,只需要对排序方式进行即可
	//要确定趟数
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int flag = 0;//假设已经有序了
		//一趟冒泡排序的过程
		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);
			}
		}
	}


}

void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test3()
{
	int arr[] = { 3,1,5,2,4,9,8,6,5,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("排序前:");
	print_arr(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	printf("排序后:");
	print_arr(arr, sz);
}

qsort对于代码的解析:

如何交换数据:

为什么这个地方写成char*?

在bubble_sort函数中,由于不知道base指向的数据类型,因此需要将其强制转换为char*类型,从而让指针的步长为1。这样,在进行指针的加减运算时,就可以根据width参数来控制指针的步长,从而实现对任意数据类型的排序。

注意:width对于char来说是1个字节的意思,但是对于其它类型来说就不是了,width是根据参数不同来设置不同的数据类型,控制指针步长的。

交换的流程:1,2,3,4表示顺序 

 代码的整体流程:

排序:

通过新定义的冒泡排序排序年龄以及姓名

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, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
void bubble_sort(void* base, size_t num, size_t width,
	int (*cmp)(const void* p1, const void* p2))
{
	//要确定趟数
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int flag = 0;//假设已经有序了
		//一趟冒泡排序的过程
		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);
			}
		}
	}


}
void print_arr3(struct Stu* s, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", (s + i)->age);
		//printf("%s ", (*(arr+i)).name);
		//printf("%s ", arr[i].name);

	}
	printf("\n");

}
void print_arr4(struct Stu* s, int sz)
{
	int i = 0;
		for (i = 0; i < sz; i++)
		{
			printf("%s ", (s+i)->name);
			//printf("%s ", (*(arr+i)).name);
			//printf("%s ", arr[i].name);
	
		}
		printf("\n");

}
void test4()
{
	struct Stu s[] = { {"zhangsan",30} ,{"lisi",25},{"wangwu",50}};
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按年龄来排序
	/*print_arr3(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	print_arr3(s, sz);*/
	//测试按照名字来排序
	print_arr4(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	print_arr4(s, sz);

}

按姓名排序:

 按年龄排序

这篇文章到这里就结束了,如有错误欢迎大家指正,然后下来就是这篇关于sizeof和strlen的详细总结:,指针和数组笔试题解析总结,传送门--> http://t.csdn.cn/aKVsj

欢迎大家来访。

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

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

相关文章

数据结构--基础知识

数据结构是什么&#xff1f; 数据结构是计算机科学中研究数据组织、存储和管理的方法和原则。它涉及存储和操作数据的方式&#xff0c;以便能够高效地使用和访问数据。 相关内容 基本组成 数组&#xff08;Array&#xff09;&#xff1a;数组是一种线性数据结构&#xff0c;…

MySql005——使用SQL创建数据库和表

在《MySql000——MySql数据库的下载、安装以及使用图形化工具创建数据库和表》中&#xff0c;我们使用图形化工具MySQL Workbench创建数据库和表&#xff0c;下面我们将使用SQL来实现这一过程 一、数据库操作 1.1、创建数据库 1.1.1、创建MySQL数据库通用写法 使用 create 命…

ts一些常用符号

非空断言操作符(!) 具体是指在上下文中当类型检查器无法断定类型时&#xff0c;一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。具体而言&#xff0c;x! 将从 x 值域中排除 null 和 undefined 。 1. 赋值时忽略 undefined 和 null function…

Simulink仿真模块 - Saturation Dynamic

Saturation Dynamic将输入信号限制在动态饱和上界和下界值之间 在仿真库中的位置为&#xff1a;Simulink / Discontinuities 模型为&#xff1a; 说明 Saturation Dynamic 模块产生输出信号&#xff0c;该信号是以来自输入端口 up 和 lo 的饱和值为界的输入信号的值。 输入输…

【已解决】windows7添加打印机报错:加载Tcp Mib库时的错误,无法加载标准TCP/IP端口的向导页

windows7 添加打印机的时候&#xff0c;输入完打印机的IP地址后&#xff0c;点击下一步&#xff0c;报错&#xff1a; 加载Tcp Mib库时的错误&#xff0c;无法加载标准TCP/IP端口的向导页 解决办法&#xff1a; 复制以下的代码到新建文本文档.txt中&#xff0c;然后修改文本文…

PHP+mysql鲜花销售商城网站html5在线鲜花花店购物订购系统

花店订购管理系统&#xff0c;是基于php编程语言&#xff0c;mysql数据库开发&#xff0c;本系统分为用户和管理员两个角色&#xff0c;其中用户可以注册登陆系统&#xff0c;查看分类&#xff0c;搜索鲜花&#xff0c;查看鲜花详情&#xff0c;加入购物车&#xff0c;生成订单…

【MySQL】模具数据转移处理

系列文章 C#底层库–MySQLBuilder脚本构建类&#xff08;select、insert、update、in、带条件的SQL自动生成&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129179216 C#底层库–MySQL数据库操作辅助类&#xff08;推荐阅读&#xff0…

【phaser微信抖音小游戏开发002】hello world!

执行效果&#xff1a; 将以下代码文本内容&#xff0c;放入到game.js中即可。目录结构如下图 import ./js/libs/weapp-adapter import ./js/libs/symbolGameGlobal.window.scrollTo () > { };//防止真机出错 import Phaser from ./js/phaser//引入Phaservar {windowWidth, …

9条建议告诉你如何正确处理PCB设计布线

一、关于PCB布线线宽 1、布线首先应满足工厂加工能力&#xff0c;首先向客户确认生产厂家&#xff0c;确认其生产能力&#xff0c;如图1所示。如客户无要求&#xff0c;线宽参考阻抗设计模板。 图1 PCB板厂线宽要求 2、阻抗模板&#xff0c;根据客户提供的板厚及层数要求&…

vue中使用axios发送请求时,后端同一个session获取不到值

问题描述&#xff1a; 在登录页面加载完成后通过axios请求后端验证码接口&#xff08;这时后端会生成一个session用于保存验证码数值&#xff09;&#xff0c;当输入完用户名、密码、验证码后请求登录接口&#xff0c;报错验证码输入错误&#xff0c;打印后端保存验证码的sessi…

C1. Dual (Easy Version)(正负号转换)

题目&#xff1a;Problem - C1 - Codeforces 总结&#xff1a; 首先&#xff1a;对于全正数和全负数的数组求非减序列构造很是简单 紧接着&#xff1a;对于有正有负的数组可以将其转化为统一符号 最后符号统一方案&#xff1a; 找出绝对值最大的数并将每个数都将加上该绝对…

SSM电影推荐系统【纯干货分享,免费领源码03073】

目 录 摘 要 Abstract 第1章 前 言 1.1 研究背景 1.2 研究现状 1.3 系统开发目标 第2章 技术与原理 2.1 开发技术 2 2.2 ssm框架介绍 2 2.3 MySQL数据库 2 2.4 B/S结构 2 第3章 需求分析 3.1 需求分析 3.2 系统可行性分析 3.3 项目设计目标与原则 3.4…

添加IP白名单的方法,你知道几个?

在各种网络操作中&#xff0c;不同网站和服务可能会对来源IP地址进行限制&#xff0c;为了正常访问&#xff0c;我们需要将自己的IP地址添加到白名单中。本文将为您详细介绍添加IP白名单的几种方法以及它们之间的优劣比较&#xff0c;同时分享可能遇到的问题和解决方案。 方法…

mac 下用brew快速安装CommandLineTools

有时候用git 就会提示安装CommandLineTools &#xff0c;xcode太大又不想安装&#xff0c;怎么办呢我们可以试下下面的方式 什么是Brew&#xff1a; Brew是Mac OS X下的一个包管理器&#xff0c;可以方便地安装、升级和卸载很多常用的软件包 在mac下如何安装呢&#xff1a; …

linux网卡命名规则

Consistent Network Device Naming Linux provides methods for consistent(一致) and predictable(可预测) network device naming for network interfaces. These features change the name of network interfaces on a system in order to make locating and different…

Java8实战-总结9

Java8实战-总结9 Lambda表达式把Lambda付诸实践&#xff1a;环绕执行模式第1步&#xff1a;记得行为参数化第2步&#xff1a;使用函数式接口来传递行为第3步&#xff1a;执行一个行为第4步&#xff1a;传递Lambda 使用函数式接口PredicateConsumerFunction原始类型特化 Lambda表…

mybatis plus 的一些使用

简介 官网&#xff1a;http://mp.baomidou.com/ 参考教程&#xff1a;https://baomidou.com/pages/24112f/ MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 …

web前端框架Javascript之JavaScript 异步编程史

早期的 Web 应用中&#xff0c;与后台进行交互时&#xff0c;需要进行 form 表单的提交&#xff0c;然后在页面刷新后给用户反馈结果。在页面刷新过程中&#xff0c;后台会重新返回一段 HTML 代码&#xff0c;这段 HTML 中的大部分内容与之前页面基本相同&#xff0c;这势必造成…

同样是跨端框架,React会不会被VUE取代?

看到知乎上有比较多的类似问题&#xff0c;正好这两个框架在以往的一些项目中都有实践过&#xff0c;就借着本篇文章说说我个人的看法。 先摆个结论&#xff1a;不会&#xff0c;毕竟各有千秋&#xff0c;除非跨端框架有被更好的概念所替代&#xff0c;又或者App已经彻底过气了…

PoseiSwap:通过 RWA 的全新叙事,反哺 Nautilus Chain 生态

PoseiSwap 是 Nautilus Chain 上的首个 DEX&#xff0c;作为目前行业内模块化区块链叙事的早期奉行者&#xff0c;PoseiSwap 也得到了较高的市场关注。基于 Nautilus Chain&#xff0c;PoseiSwap 打造了一个全新的 Rollup 应用层&#xff0c;并通过零知识证明来建立全新的订单簿…