【C语言】带你手把手拿捏指针(3)(含转移表)

news2024/9/20 19:59:34

在这里插入图片描述

文章目录

  • 一、字符指针变量
  • 二、数组指针变量
    • 1.数组指针变量是什么
    • 2.数组指针变量的初始化
  • 三、二维数组传参的本质
  • 四、函数指针变量
    • 1. 函数指针变量的创建
    • 2.函数指针的使用
    • 3.案例解析:
  • 五、typedof关键字
  • 六、函数指针数组和转移表
    • 1.函数指针数组
    • 2.转移表

一、字符指针变量

   在指针的类型中我们知道有⼀种指针类型为字符指针 char* ,⼀般使⽤的方式如下:
在这里插入图片描述
   这里我们将字符变量a的地址交给指针变量p,然后进行使用,但还有一种方式如下:

#include <stdio.h>
int main()
{
 const char* pstr = "hello bit.";
 //这⾥是把⼀个字符串放到pstr指针变量⾥了吗?
 printf("%s\n", pstr);
 return 0;
}

   代码 const char* pstr = “hello bit.”,特别容易让同学以为是把字符串 hello bit. 放到字符指针 pstr ⾥了,但是本质是把字符串 hello bit. ⾸字符的地址放到了pstr中,如图:
在这里插入图片描述
   上⾯代码的意思是把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 pstr 中。《剑指offer》中收录了⼀道和字符串相关的笔试题,我们⼀起来学习⼀下:

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const char *str4 = "hello bit.";
 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;
}

   请问最后代码会输出什么信息呢?我们来看看运行结果,看看与你的想法是否一致:
在这里插入图片描述
   为什么呢?我们可以思考一下,如果是两个变量,那么它们会开辟两个空间存放相应的数据,可能数据不同,所以本质上它们并不相同,而一个常量字符串
   本身就是常量,不会被修改,所以不会像变量一样开辟两个空间存放,而是会将一个常量字符串存在同一个空间,所以str1和str2不同,str3和str4相同

二、数组指针变量

1.数组指针变量是什么

   之前我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)
   数组指针变量是指针变量?还是数组?答案是:指针变量
我们已经熟悉:

  • 整形指针变量: int * pt; 存放的是整形变量的地址,能够指向整形数据的指针。
  • 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

   那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量

我们来看下面两个,哪个是数组指针变量:

1.int *p1[10];
2.int (*p2)[10];

思考一下p1、p2分别是什么?
   我们先来看看第一个,这里的 * 号会和前面的int结合,为什么呢?因为[]操作符的优先级是比 * 号高的,也就导致了p1和[10]结合, * 和int结合, 变成了一个类型为int*,数组名为p1,元素个数为10的指针数组,如图:
在这里插入图片描述
所以这里p1并不是一个数组指针,而是一个指针数组

   而第二段代码中,我们将p2和 * 用小括号()括起来了,也就改变了优先级,p先和 * 结合,说明p是⼀个指针变量,然后指针指向的是⼀个大小为10个整型的数组。所以p是⼀个指针,指向⼀个数组,叫数组指针
这⾥要注意:[]的优先级要⾼于 * 号的,所以必须加上()来保证p先和*结合

2.数组指针变量的初始化

   数组指针变量是⽤来存放数组地址的,那怎么获得数组的地址呢?就是我们之前学习的 &数组名,如下:

int arr[10] = {0};
int (*p)[10] = &arr;//得到的就是数组的地址

数组指针类型解析:

int (*p) [10] = &arr;
 |    |   |
 |    |   |
 |    |   p指向数组的元素个数
 |    p是数组指针变量名
 p指向的数组的元素类型

   我们之前也做过比较,我们发现数组名,也就是首元素地址和数组地址看起来是一样的,如下:
在这里插入图片描述
它们三个的关系是什么呢?
   我们知道这里数组名arr就是首元素地址,也就是&arr[0],但是这里的&arr是取出的数组的地址,它和另外两个的区别就是,前两个是元素的地址,±整数是跳过相应的元素个数,而&arr±整数时,则是跳过相应的数组个数
   为了加深理解,也同时为我们的二维数组传参本质讲解做铺垫,这里我们举一个例子:
   如何使用数组指针访问一维数组?我们来看一个代码,看看它是否正确,如下:

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*p)[5] = &arr;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

   这个代码正确吗?根据我们前面学的知识,很容易判断出它是错误的,那么为什么呢?就是因为这里p实际上是&arr,也就是整个数组的地址,对它解引用应该是拿到整个数组,如下代码:
在这里插入图片描述
   可以看到,我们对p解引用后拿到的是整个数组,那么p与这个一维数组的联系到底是什么呢?如下:

p = &arr
*p = *&arr
*p=arr 

   通过上文比较我们可以看到, * p本质上就是数组的数组名,那么我们就把 * p当作数组名来使用,如下:

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*p)[5] = &arr;
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", (*p)[i]);
	}
	return 0;
}

   我们来看看运行结果:
在这里插入图片描述
   可以看到确实实现了用数组指针访问一维数组,但是总感觉怪怪的,感觉就是为了完成任务硬拼起来的,那数组指针到底会在什么场景出现呢?我们就在讲解二维数组传参的本质时介绍

三、二维数组传参的本质

   有了数组指针的理解,我们就能够讲⼀下⼆维数组传参的本质了
   过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:

#include <stdio.h>
void print(int a[3][5], int r, int c)
{
 int i = 0;
 int j = 0;
for(i=0; i<r; i++)
 {
 for(j=0; j<c; j++)
 {
 printf("%d ", a[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;
}

   这⾥实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?
   ⾸先我们再次理解⼀下⼆维数组,⼆维数组其实可以看做是每个元素是⼀维数组的数组,我们在传参时会传这个二维数组的数组名,我们也都知道一个数组的数组名是首元素的地址,那么二维数组的首元素地址是什么呢?
   由于二维数组可以看做是一维数组的数组,那么第一行就是它的第一个元素,第二行就是它的第二个元素,依此类推,所以⼆维数组的首元素就是第⼀⾏,是个⼀维数组,如下图:
在这里插入图片描述
   所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀维数组的地址。根据上⾯的例⼦,第一行的⼀维数组的类型就是 int [5] ,所以第一行的地址的类型就是数组指针类型 int(*)[5]
   那就意味着⼆维数组传参本质上也是传递了地址,传递的是二维数组第一行这个⼀维数组的地址,是个数组,那么形参也是可以写成数组指针形式的,如下:

void print(int (*p)[5}, int x, int y)

   经过上一节的分析,我们也就知道了p其实就是二维数组第一行的地址,也就是二维数组的首元素的地址,也就相当于二维数组的数组名arr,那么我们此时就可以使用p访问二维数组,如下代码:

void print(int(*p)[5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		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};
	print(arr, 3, 5);
    
	return 0;
}

运行结果如图:
在这里插入图片描述
   可以看到确实使用这种方式可以完美访问二维数组了,那么还有其它方法吗?我们这里再讲一个更加深入,更加贴近指针用法的方法
   首先我们来再深入一点了解二维数组,我们说二维数组的每一行都是一个一维数组,那么这个一维数组有数组名吗?当然有,如下图:
在这里插入图片描述
   然后我们继续深入理解,p是二维数组中第一个一维数组的地址,那么p+1就应该是第二个一维数组的地址,因为p是数组指针,±整数可以跳过对应的数组个数,p+2就是第三个一维数组的地址,如图:
在这里插入图片描述
   那么* (p+0),* (p+1),* (p+2),之前通过学习对一维数组的地址进行解引用就会得到该一维数组的数组名,也就是首元素的地址,如下例:

int arr[3][5] = { 1,2,3,4,5 ,2,3,4,5,6 ,3,4,5,6,7};
p+0 = &arr[0];
p+1 = &arr[1];
p+3 = &arr[2];
那么:
*(p+0) = *&arr[0]=arr[0];
*(p+1) = *&arr[1]=arr[1];
*(p+3) = *&arr[2]=arr[2];

   所以我们可以总结为下图:
在这里插入图片描述
   现在有了每个一维数组的数组名,也就是首元素地址,我们再对首元素地址进行±就可以得到其它元素的地址,然后解引用就可以得到对应的元素,如下图:
在这里插入图片描述
   于是最终我们可以利用指针数组写出如下代码:

void print(int(*p)[5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		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};
	print(arr, 3, 5);
	return 0;
}

   运行结果如下:
在这里插入图片描述
   总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式,它的本质就是使用数组指针,来访问二维数组的元素

四、函数指针变量

1. 函数指针变量的创建

什么是函数指针变量呢?
   根据前⾯学习整型指针,数组指针的时候,我们的类⽐关系,我们不难得出结论:
   函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
那么函数是否有地址呢?我们做个测试:

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

   我们来看看运行结果:
在这里插入图片描述
   确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的方式获得函数的地址
   如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针非常类似
   它也需要把变量名和*用()括在一起,最前面写上函数的返回类型,不同的是,数组后面跟的是元素的个数,而函数指针变量后面是一对小括号,里面写上函数的参数,参数的类型必须写出来,但是参数名可以省略,现在举两个例子演示:
举例1:函数无参数时:

void test()
{
	printf("hehe\n");
}
int main()
{
	void (*P)() = test;
	       //或写成&test;
	return 0;
}

举例2:函数有参数时:

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*p)(int x, int y) = Add;
//或写成:int (*p)(int, int) = Add;
	return 0;
}

函数指针类型解析:

int (*p) (int x, int y)
 |    |   ------------ 
 |    |        |
 |    |    p指向函数的参数类型和个数的交代
 |   函数指针变量名
 p指向函数的返回类型
int (*) (int x, int y) //p函数指针变量的类型

2.函数指针的使用

   可以通过函数指针调用指针指向的函数,由于函数名就是函数的地址,所以我们在使用时,可以直接用函数指针变量名替换函数名,如下例:

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*p)(int x, int y) = Add;
	printf("%d\n", p(2, 3));
	return 0;
}

输出结果:
在这里插入图片描述
   或者我们也可以对p进行解引用,也可以拿到函数的地址,如下:

printf("%d\n", (*p)(2, 3));

输出结果:
在这里插入图片描述

3.案例解析:

   我们来看两段代码,猜测它们的含义:

  1. 代码1
(*(void (*)())0)();

这段代码中比较特殊的就是数字0,随后我们来看它的左边是什么:

(void (*)())

   很明显在括号里面的是一种函数指针类型,它的返回类型是void,参数为空,没有变量名,说明它只是一种函数指针类型,那么把它放在0的前面有什么用呢?我们举一个简单的例子:

(int)3.14

   在一个数据前加上一个括号,里面写上类型,很明显就是我们的强制类型转换,这里是把浮点型3.14强制转换为整型,上述例子也是如此,将0强制转换为了一个函数指针类型,现在相当于0是一个指针
然后前面的*对它进行解引用,相当于就是这个函数本身,这个函数就是前面一堆:

(*(void (*)())0)

   最后的一个小阔号就是函数本身的括号,如函数Add(int x,int y),函数名Add后面的括号,只是这个函数没有参数,而Add函数有参数

  1. 代码2
void (*signal(int , void(*)(int)))(int);

   这个代码更加不可思议,我们首先可以从signal下手,它看起来很像一个函数声明,它的参数分别是int类型,以及一个返回类型为void,参数为int类型的函数指针类型,如下:

signal(int , void(*)(int))

   那么剩下的那些是什么呢?既然我们猜测它是一个函数,那么现在函数名有了,函数参数也有了,是不是还差一个返回类型,我们把中间这一段去掉,如下:

void (*)(int);

   很明显它变成了一个函数指针类型,小伙伴们肯定也可以猜到,这就是刚刚那个函数的返回类型,可是为什么这么奇怪,要把除了返回类型的东西塞进返回类型,不像传统这样写:

void (*)(int)  signal(int , void(*)(int))

   这样似乎更好理解,但是这是C中未定义的,还是要使用上面的写法,否则会出错
   最后总结一下:这段代码是函数signal的声明,它的一个参数是int,一个参数是一种函数指针类型,返回类型也是一个函数指针类型

五、typedof关键字

   typedef 是⽤来类型重命名的,可以将复杂的类型,简单化
   比如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使用:

typedef unsigned int uint;
//将unsigned int 重命名为uint

   如果是指针类型,能否重命名呢?其实也是可以的,比如,将 int* 重命名为 ptr_t ,这样写:

typedef int* ptr_t

   但是对于数组指针和函数指针稍微有点区别,比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef int(*parr_t)[5]; 
//新的类型名必须在*的右边

   函数指针类型的重命名也是⼀样的,比如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:

typedef void (*pf_t)(int)
//新的类型名必须在*的右边

那么要简化上面的第四点中的代码2,可以这样写:

typedef void(*pf_t)(int);
pf_t signal(int, pfun_t);

六、函数指针数组和转移表

1.函数指针数组

   数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组,比如:

int * arr[10];
//数组的每个元素是int*

   那要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?我们现在写出多种可能,来猜一下是哪一个:

1.int (*parr1[3])();

2.int *parr2[3]();

3.int (*)() parr3[3];

   是哪一种呢?一般来说跟函数指针有关的时候,我们一般会把一些声明写进函数类型中,所以在上面的三个例子中,正确的写法是1
   parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?是 int (*)() 类型的函数指针
   那么函数指针数组有什么作用呢?就要涉及到下一个内容:转移表

2.转移表

   函数指针数组的⽤途:转移表
   举例:计算器的实现,要求可以根据菜单使用加减乘除
我们最初写过一个最简单的加法函数,如下:

int add(int x, int y)
{
	return x + y;
}

那么另外的函数也只需要一个一个实现,如下:

int add(int a, int b)
{
 return a + b;
}
int sub(int a, int b)
{
 return a - b;
}
int mul(int a, int b)
{
 return a * b;
}
int div(int a, int b)
{
 return a / b;
}

   到现在这一步还算简单,随后我们开始写主函数中的内容,我们可以设计一个菜单menu,用来打印选择输入0关闭计算器,输入1,2,3,4就对应加减乘除,具体的实现可以使用Switch语句,如下:
menu函数:

void menu()
{
	printf("*************************\n");
	printf(" 1:add              2:sub \n");
	printf(" 3:mul              4:div \n");
	printf("          0:exit \n");
	printf("*************************\n\n");
	printf("请选择:");
}

主函数:

int main()
{
	int x, y;
	int input = 0;
	int ret = 0;
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("已退出计算器!");
			break;
		case 1:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		}
	} while (input);
	return 0;
}

   这样我们的计算器就完成了,但是我们发现了严重的问题,这样写代码虽然简单,但是实在有点笨,有很多内容我们都没有必要重复,那么我们该怎么避免呢?这时我们发现这些函数的参数和返回类型都是一致的,我们就可以尝试使用函数指针数组:
   我们现在创建一个函数指针数组,如下:

int (*pf[5])(int , int);

   接下来我们对它们进行初始化,分别将每个函数的地址放进去,由于函数名就是函数地址,我们只需要填上函数名:

int (*pf[5])(int , int)={ add,sub,mul,div };

   但是这时候我们就会发现一个小问题,add在数组中的下标是0,但是实际上在Switch语句中,输入1应该才是add,这时候有个小技巧就是,在最前面加上一个元素0,就可以让它们到对应的位置上,如:

int (*pf[5])(int , int)={ 0,add,sub,mul,div };

   随后我们就可以利用这个数组方便的访问函数,pf就是数组名,input就是要访问的对应的下标,所以pf[input]相当于拿到对应函数的地址,也就是函数名,随后将其正常使用即可,如下:

pf[input](x,y);

   知道了这个,我们代码就好写了,可以简化许多内容,所以完整的计算器代码如下:

int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}

void menu()
{
	printf("*************************\n");
	printf(" 1:add              2:sub \n");
	printf(" 3:mul              4:div \n");
	printf("          0:exit \n");
	printf("*************************\n\n");
	printf("请选择:");
}

int main()
{
	int x, y;
	int input = 0;
	int ret = 0;
	int (*pf[5]) (int, int) = { 0,add,sub,mul,div };
	do
	{
		menu();
		scanf("%d", &input);
		if (input>=1 && input<=4)
		{
			printf("请输入两个整型数据:");
			scanf("%d %d", &x, &y);
			ret = *pf[input](x, y);
			printf("结果为:%d\n\n", ret);
		}
		else if(input==0)
		{
			printf("已退出计算器\n"); \
				break;
		}
		else
		{
			printf("选择错误,请重新输入!\n\n");
		}
	} while (input);
	return 0;
}

   那这个跟转移表有什么关系呢?其实它所指的就是运用函数指针数组以数组方式去调用里面的函数,从而在某些情况下替代冗长的switch函数,所以简单的说函数指针数组就叫转移表

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

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

相关文章

Linux基础---11优化系统

一.优化SSH连接速度 1&#xff09;修改配置文件 cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak#备份vi /etc/ssh/sshd_config将79行和115行的yes修改为no,最后:wq保存退出&#xff08;79gg和115gg可直接跳至本行&#xff09; 79 行&#xff1a;GSSAPIAuthentication no…

二叉树OJ题——对称二叉树

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 对称二叉树 二、解题思路 三、解题代码

基础算法(3)——二分

1. 二分查找 题目描述&#xff1a; 解法一&#xff1a;暴力解法&#xff08;时间复杂度) 循环遍历数组&#xff0c;找到相等的值返回下标即可 解法二&#xff1a;二分查找 “有序数组可使用二分查找”&#xff0c;这句话是不严谨的&#xff0c;应该是具有 “二段性” 的数组…

nodejs+express+vue教辅课程辅助教学系统 43x2u前后端分离项目

目录 技术栈具体实现截图系统设计思路技术可行性nodejs类核心代码部分展示可行性论证研究方法解决的思路Express框架介绍源码获取/联系我 技术栈 该系统将采用B/S结构模式&#xff0c;开发软件有很多种可以用&#xff0c;本次开发用到的软件是vscode&#xff0c;用到的数据库是…

蚂蚁在 RAG 与向量检索上的实践:技术应用与创新分析

引言 在AI技术迅猛发展的背景下&#xff0c;如何有效地处理海量数据成为了技术创新的关键问题。向量数据库和RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术结合&#xff0c;为提升生成式AI应用的准确性和实时性提供了有效的解决方案。本文结合蚂蚁集团在向量…

宠物空气净化器该怎么选?希喂、352、霍尼韦尔哪款对吸附浮毛有效

明明我都成年很久了&#xff0c;我爸妈还把我当小孩一样&#xff0c;我干什么前都要和他们说一声。前段时间去朋友家玩&#xff0c;本来对宠物无感的我一下子就被她家可爱的猫咪萌化了。猫咪好可爱呀&#xff0c;毛茸茸的摸起来很舒服&#xff0c;眨巴的大眼睛看着你真的心软软…

荣誉 | 分贝通入选2024「Cloud 100 China」

近日,2024 Cloud 100 China 榜单于美高梅酒店正式发布,这是靖亚资本和崔牛会联合推出的第三届榜单。 全球商旅管理、企业支出全流程管控、数据BI全方位降本、AI赋能高效出行体验.......近年来,分贝通不断精进产品能力及BI&AI能力,再次上榜。 本届评选,组委会基于过去一年融…

【MRI数据】LEMON MRI 数据集下载

本文介绍使用cyberduck软件下载 LEMON MRI 数据。 数据简介 LEMON MRI 数据官网&#xff1a;https://fcon_1000.projects.nitrc.org/indi/retro/MPI_LEMON.html 提供了 228 名健康参与者的公开数据集&#xff0c;包括年轻人&#xff08;N154&#xff0c;25.13.1 岁&#xff0…

ModbusTCP报文详解

Modbus TCP与Modbus Rtu(ASCI)数据帧的区别 总结&#xff1a;Modbus TCP就是在Modbus Rtu(ASCI)基础上去掉CRC&#xff0c;再加上六个0一个6 Modbus TCP MBAP报文头 域长度描述客户机服务器事务处理标识符2字节Modbus请求/响应事务处理的识别客户机启动服务器从接收的请求中重…

vue3中如何拿到vue2中的this

vue3中常用api vue3中常用响应式数据类型&#xff1a;

【计算机网络】详解TCP/IP分层模型局域网和跨网络通信的原理

一、网络协议 两个概念&#xff1a;交换机&#xff1a;实现位于同一个子网中的主机数据交换。路由器&#xff1a;实现数据包的跨网络转发。 两台主机的距离变远了&#xff0c;会引发出一系列问题&#xff1a; 1、如何使用数据的问题 2、数据的可靠性问题 3、主机定位问题 4、…

<<编码>> 第 14 章 反馈与触发器(2)--或非门反馈 示例电路

或非门反馈电路 info::操作说明 先闭合上面的开关(置位 Set), 此时输出高电平 再断开上面的开关, 因反馈的存在, 输出保持为高电平 闭合下面的开关(复位 Reset), 输出重新回到低电平 断开下面的开关, 输出继续保持低电平 primary::在线交互操作链接 https://cc.xiaogd.net/…

产品经理有必要学习大模型技术吗?

第一&#xff0c;大模型正在成为各类产品的核心组件&#xff0c;颠覆了传统产品和应用生态&#xff0c;进入AI大模型应用的新阶段。 例如&#xff0c;NewBing、Perplexity等AI搜索产品已经颠覆了传统搜索引擎的搜索模式&#xff0c;用户不用搜索后再点开排序靠前的网页链接&am…

Linux常见查看文件命令

目录 一、cat 1.1. 查看文件内容 1.2. 创建文件 1.3. 追加内容到文件 1.4. 连接文件 1.5. 显示多个文件的内容 1.6. 使用管道 1.7. 查看文件的最后几行 1.8. 使用 -n 选项显示行号 1.9. 使用 -b 选项仅显示非空行的行号 二、tac 三、less 四、more 五、head 六、…

十八,Spring Boot 整合 MyBatis-Plus 的详细配置

十八&#xff0c;Spring Boot 整合 MyBatis-Plus 的详细配置 文章目录 十八&#xff0c;Spring Boot 整合 MyBatis-Plus 的详细配置1. MyBatis-Plus 的基本介绍2. Spring Boot 整合 MyBatis Plus 的详细配置3. Spring Boot 整合 MyBatis plus 注意事项和细节4. MyBatisx 插件的…

《微处理器系统原理与应用设计第十三讲》通用同/异步收发器USART轮询模式应用设计

USART提供两设备之间的串行双工通信&#xff0c;并支持中断和DMA工作。采用轮询、中断和DMA三种方式进行数据收发。 一、功能需求 实现远程串行通信数据的回传确认。微处理器系统构成的测控设备通过USART&#xff08;串口&#xff09;与用户设备&#xff08;上位机&#xff0…

学习使用SQL Server Management Studio (SSMS)

SQL Server Management Studio (SSMS) 是一个集成环境&#xff0c;用于管理任何SQL基础设施&#xff0c;从SQL Server到Azure SQL数据库。SSMS提供了各种工具来配置、监控和管理SQL Server的实体和组件。以下是一篇详细的使用指南&#xff0c;涵盖了SSMS的主要功能和操作。 1.…

感谢问界M9一打二十,让我们买到这么便宜的BBA

文 | AUTO芯球 作者 | 雷慢 国产豪华车&#xff0c;终于扬眉吐气了&#xff0c; 你敢信吗&#xff1f;在50万以上豪华车中&#xff0c; 现在问界M9一款车的月销量&#xff0c; 是其他前20名销量的总和&#xff01; 要知道&#xff0c;它的对手是各种宝马、奔驰、雷克萨斯的…

基于python+django+vue的医院预约挂号系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于协同过滤pythondjangovue…

APP测试基本流程与APP测试要点总结

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 APP测试实际上依然属于软件测试的范畴&#xff0c;是软件测试的一个真子集&#xff0c;所以经典软件测试理论&#xff0c;依然是在APP测试中有效的&#xff0c;只…