深度刨析程序中的指针

前面我们已经学习过了指针的一下性质:

  • 指针就是个变量,用来存放地址,地址唯一标识的一块内存空间
  • 指针的大小是固定的4/8个字节(32位平台/64位平台)
  • 指针是有类型,指针的类型决定了指针的加减整数的步长,指针解引用操作时的权限。
  • 两个指针相减返回的是两指针间元素的个数(同类型指针)

文章目录

  • 1.字符指针
  • 2.指针数组
  • 3.数组指针
    • 3.1 数组指针的定义
    • 3.2 &数组名与数组名
    • 3.3 数组指针的使用
  • 4.数组传参、指针参数
    • 4.1 一维数组传参
    • 4.2 二维数组传参
    • 4.3 一级指针传参
    • 4.4 二级指针传参
  • 5.函数指针
  • 6.函数指针数组
  • 7.指向函数指针数组的指针
  • 8.回调函数

1.字符指针

在指针的类型中我们知道有一种指针类型为字符指针char*
一般使用情况

#include <stdio.h>
int main()
{
	char c = 'a';
	char* pc = &c;
	*pc = 'y';
	return 0;
}

还有一种情况

#include <stdio.h>
int main()
{
	const char* str = "hello world";//把"hello world"的首元素的地址给了str
	//但是不能单纯的理解为数组,这里的"hello world"是存放代码区中的不可修改,是常量字符串,所以我们在前面加了const修饰
	printf("%s\n",str);
	return 0;
}

指针

本质就是把常量字符串hello world的首元素的地址放到了str当中,也就是将常量字符串的首元素h的地址放到str中
练习

#include <stdio.h>
int main()
{
	char str1[] = "hello world";
	char str2[] = "hello world";

	const char* str3 = "hello bit";
	const char* str4 = "hello bit";

	if(str1==str2)
		printf("same\n");
	else
		printf("not same\n");
	if(str3==str4)
		printf("same\n");
	else
		printf("not same\n");
	return 0;
}

//打印结果:
/*
not same
same
*/

这里比较的都是地址。
str1和str2都是数组,当用相同的常量字符串去初始化不同的数组的时候就会开辟不同的空间。而str3和str4指向的同一个常量字符串。在c/c++中会把常量字符串单独存储在一个内存区域(代码段),当我们用几个指针去指向同一个字符串时,它们实际会指向同一块内存的。
可以这么理解:str1和str2是可以修改数组中的元素的,如果不同数组间的修改会相互影响,那岂不是乱遭了。而str3和str4是不可以被修改的,那么让它们两指向同一块空间也是完全没有问题的。

2.指针数组

指针数组就是存放指针的数组。
我们可以进行类比:
整型数组是存放整型的数组,字符数组是存放字符的数组。那么指针数组肯定就是存放指针的数组咯。

int* arr1[10];//整型指针的数组
char* arr2[4];//一级字符指针数组
char** arr3[10];//二级字符指针数组

3.数组指针

3.1 数组指针的定义

我们知道整型指针是指向整型的指针(存放整型变量的地址的指针变量)
还有字符指针是指向字符的指针(存放字符变量的地址的指针变量)
如此类比的话
数组指针就是指向数组的指针(存放数组变量的地址的指针变量)
数组指针的正确写法

int *p1[10];//错
int (*p2)[10];//对
int (*p)[10];
//p与*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
//加()的原因是因为,根据操作符的优先性,[]的优先级是要高于*的,为了保证*与p的结合需要添加括号

3.2 &数组名与数组名

int arr[10];

arr&arr分别是什么呢?
arr是数组名,数组名又表示数组首元素的地址。
&arr表示的整个数组的地址。

#include <stdio.h>
int main()
{
	int arr[10];
	printf("%p\n",arr);
	printf("%p\n",&arr);
	return 0;
}
//打印结果:
/*
00DCFBDC
00DCFBDC
*/

打印它们的地址可以发现是一样的。但其实又不完全一样。

#include <stdio.h>
int main()
{
	int arr[10];
	printf("%p\n",arr);
	printf("%p\n",arr+1);
	
	printf("%p\n",&arr);
	printf("%p\n",&arr+1);
	return 0;
}
//打印结果:
/*
004FF970
004FF974
004FF970
004FF998
*/

arr+1跳过的4个字节的地址。而&arr跳过的是40个字节的地址。
正如前面所说&arr是整个数组的地址,整个数组大小就是40个字节。
本例中&arr的类型就是int(*)[10],是一种数组指针类型。
数组地址+1,跳过整个数组的大小,所以&arr+1相对于&arr的差值就是40.

3.3 数组指针的使用

了解到数组指针指向的数组,那么数组指针中存放的就是数组的地址。

#include <stdio.h>
int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,0};
	int(*p)[10] = &arr;
	//把整个数组的地址存放在数组指针变量当中
	//但是很少这么写
	return 0;
}

数组指针的使用

#include <stdio.h>
void print1(int arr[3][5],int row,int col)
{
	for(int i = 0;i<row;++i)
	{
		for(int j = 0;j<col;++j)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
}

void print2(int (*arr)[5],int row,int col)
{
	for(int i = 0;i<row;++i)
	{
		for(int j = 0;j<col;++j)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = {1,2,3,4,5,6,7,8,9,0};
	print1(arr,3,5);
	//arr是数组的数组名,表示数组首元素的地址。而这又是一个二维数组,二维数组的首元素地址就是第一行的地址,所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址,可以利用数组指针接收。
	print2(arr,3,5);
	
	return 0;
}

区分

int arr[5];//整型数组
int* parr1[10];//整型指针数组
int (*parr2)[10];//数组指针
int (*parr3[10])[5];//数组指针数组

4.数组传参、指针参数

在写代码时不可避免的要把【数组】或者【指针】传递给函数,那么函数的参数设计要怎么做呢?

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//可行,最容易理解的写法([]内的数字可以随便写,不影响系统的判断)该传参的本质就是int* arr
{}
void test(int arr[10])//可行,最容易理解的写法。([]内的数字可以随便写,不影响系统的判断)该传参的本质就是int* arr
{}
void test(int* arr)//可行,实参传递的是arr代表数组首元素的地址,利用整型指针接收合情合理
{}
void test2(int *arr[20])//可行,本质是int** arr
{}
void test2(int **arr)//可行,实参传递的是arr2也代表首元素的地址,因为arr2是指针数组,一维指针的地址要有二级指针接收,合情合理
{}

int main()
{
	int arr[10] = {0};
	int* arr2[20] = {0};
	test(arr);
	test2(arr2);
	return 0;
}

4.2 二维数组传参

#include <stdio.h>

void test(int arr[3][5])//可行,最容易理解的写法
{}
void test(int arr[][])//不可行,
{}
void test(int arr[][5])//可行
{}
//二维数组传参,函数形参的设计只能省略第一个[]的数字。
//对一个二维数组,可以不知道又多少行,但是必须知道要有多少列。
//因为在内存的二维数组的存放也是线性的,全存一行。知道列数才能知道有多少行。
void test(int* arr)//不可行,二维数组的数组名代表的是数组第一行的地址,是数组指针。要存放这个数组指针是无法用整型指针存放
{}
void test(int* arr[5])//不可行,二维数组的数组名代表的是数组第一行的地址,是数组指针。而这个表示的是指针数组
{}
void test(int (*arr)[5])//可行
{}
void test(int** arr)//不可行,二维数组的数组名代表的是数组第一行的地址,是数组指针.这里是二级指针,不一致。
{}
int main()
{
	int arr[3][5] = {0};
	test(arr);
	return 0;
}

4.3 一级指针传参

#include <stdio.h>
void print(int* arr,int sz)
{
	for(int i = 0;i<sz;++i)
	{
		printf("%d ",*(arr+i));
	}
}
int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,0};
	int sz = 10;
	int* p = arr;
	print(arr,sz);
	return 0;
}
//打印结果:1 2 3 4 5 6 7 8 9 0

当函数的参数部分为1级指针的时候,函数能接受的的参数为该一级指针对应类型的地址。

4.4 二级指针传参

#include <stdio.h>
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;
}

当函数的参数部分为二级指针的时候,函数能接受的的参数为该二级指针对应一级指针类型的地址。

5.函数指针

其实函数也是有地址的。

#include <stdio.h>
void test()
{
	printf("hello\n");
}
int main()
{
	printf("%p\n",test);
	printf("%p\n",&test);
	return 0;
}
//打印结果
/*
00A41267
00A41267
*/

从这里可以看出,函数不仅有地址,而且函数的函数名就代表了函数的地址,&函数名同样也表示函数的地址。
既然函数有地址,那么也就说明可以利用变量来存储。这个存储函数地址的变量就是函数指针。
函数指针的正确写法

#include <stdio.h>
void test()
{
	printf("hello\n");
}
int main()
{
	void (*pf1)() = test;//正确写法
	void *pf2() = test;//错误写法
	return 0;
}

pf1可以存储,和数组指针类似,这里的*要先和pf1结合,确定pf1是一个指针,()的优先级有比较高。因此需要用()将*pf1括起来。pf1指向的是一个函数,指向函数无参数,返回类型为void。

#include <stdio.h>
int Add(int x,int y)
{
	return x+y;
}
int main()
{
	int (*pf)(int int) = Add//指向有参数的函数指针
	return 0;
}

练习

//代码1
(*(void(*)())0)();
/*
解释:
先看void(*)()这是一个函数指针类型。再往外看,这个函数指针类型被括号括住了(void(*)())
一个类型被()住就是表示强制类型转换的意思。也是说明0被强制类型转换成了函数指针类型。然后*表示对一个函数指针类型进行解引用取出指向的函数*(void(*)())0,最后再调用这个函数。
总结:调用0地址处的函数(实际是无法调用的)
*/

//代码2
void (*signal(int,void(*)(int)))(int);//signal为函数名
/*
解释:signal是函数名,那signal()中的就是函数的参数类型,类型分别为整型和函数指针类型,现在一个函数有了函数名和函数的参数,就差函数的返回类型,如果我们把signal(int,void(*)(int))删除就得到了void (*)(int)这不就是函数指针类型吗,那也就是说signal的函数的返回类型就是void(*)(int)
总结:这是一个函数的声明,找到其函数参数和函数返回类型就可以了。
*/

简化代码2

typedef void (*pf)(int);
pf signal (int,pf);//利用typedef将类型重命名,来简化代码

6.函数指针数组

数组是存放相同类型数据的存储空间
前面我们已经学习了指针数组

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

同样的我们也可以把函数指针存放进数组,就叫做函数指针数组,那函数指针数组的是如何定义的呢?

int (*pf)();//这是一个函数指针
//我们将[]添加到变量名后面就可以了
int (*pf[10])();//这就是函数指针数组

pf[]结合说明pf是一个数组,然后数组存放的类型就是int(*)()
函数指针数组的运用
下面以实现一个简单的计算器为例

#include <stdio.h>
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 a = 0,b = 0;
	int ret = 0;
	do
	{
		menu();
		printf("选择你所要用到的功能>\n");
		scanf("%d",&input);
		switch(input)
		{
			case 1:
				printf("请输入两数>");
				scanf("%d %d",&a,&b);
				ret = Add(a,b);
				printf("ret = %d\n",ret);
				break;
			case 2:
				printf("请输入两数>");
				scanf("%d %d",&a,&b);
				ret = Sub(a,b);
				printf("ret = %d\n",ret);
				break;
			case 3:
				printf("请输入两数>");
				scanf("%d %d",&a,&b);
				ret = Mul(a,b);
				printf("ret = %d\n",ret);
				break;
			case 4:
				printf("请输入两数>");
				scanf("%d %d",&a,&b);
				ret = Div(a,b);
				printf("ret = %d\n",ret);
				break;
			case 0:
				printf("退出\n");
				break;
			default:
				printf("输入错误\n");
				break;
		}
	}while(input);
	return 0;
}

这样写的话其实是很繁琐的。实现这种简单的功能都写了这么长的代码,而且如果后续在添加什么函数功能的话,代码又要增加好的。所以我们要简化。
通过观察,这4个函数的参数和返回类型都是相同的,那么不就说明了可以写成函数指针数组吗。数组中存放这个函数指针类型就可以了。

#include <stdio.h>
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 a = 0,b = 0;
	int ret = 0;
	do
	{
		menu();
		printf("选择你所要用到的功能>\n");
		scanf("%d",&input);
		int(*pf[5])(int,int) = {NULL,Add,Sub,Mul,Div};//存入NULL是为了可以和菜单对应
		if(input>0&&input<5)
		{
			printf("请输入两数>");
			scanf("%d %d",&a,&b);
			ret = pf[input](a,b);
			printf("ret = %d\n",ret);
		}
		else if(input == 0)
			printf("退出\n");
		else
			printf("输入有误\n");
	}while(input);
	return 0;
}

利用函数指针数组我们将该程序充分简化,而且如果后续还要添加类似的函数功能的话,我们只需要将新写的函数添加进数组,在改变一下判断条件即可。

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

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

void test(const char* str)
{
	printf("%s\n",str);
}
int main()
{
	//函数指针pfun
	void(*pfun)(const char*) = test;
	//函数指针的数组pfunarr
	void(*pfunarr[5])(const char* str);
	//指向函数指针数组pfunarr的指针ppfunarr
	void(*(*ppfunarr)[5])(const char*) = &pfunarr;
}

还可以再绕下去的

8.回调函数

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

首先演示一下qsort函数的使用:

//对整型数组进行排序
#include<stdio.h>
int int_cmp(const void* a,const void* b)
{
	return (*(int*)a) - (*(int*)b);
}
int main()
{
	int arr[10] = {1,3,5,7,9,2,4,6,8,0};
	qsort(arr,10,sizeof(int),int_cmp);
	for(int i = 0;i<10;++i)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
}
//打印结果:
//0 1 2 3 4 5 6 7 8 9

//对结构体数组进行排序
#include <stdio.h>
#include <string.h>
struct stu
{
	int age;
	char name[10];
};
int struct_cmp_age(const void* a, const void* b)//利用年龄排序
{
	return ((struct stu*)a)->age - ((struct stu*)b)->age;
}
int struct_cmp_name(const void* a, const void* b)//利用名字排序,因为字符串无法相减
//所以这里利用了strcmp进行字符串的比较
{
	return strcmp(((struct stu*)a)->name,((struct stu*)b)->name);
}
int main()
{
	struct stu s[3] = { {17,"yui"},{14,"anna"},{20,"hua"} };
	qsort(s, 3, sizeof(s[0]), struct_cmp_age);
	printf("调用struct_cmp_age\n");
	for (int i = 0; i < 3; ++i)
	{
		printf("%d %s\n", s[i].age, s[i].name);
	}
	qsort(s, 3, sizeof(s[0]), struct_cmp_name);
	printf("调用struct_cmp_name\n");
	for (int i = 0; i < 3; ++i)
	{
		printf("%d %s\n", s[i].age, s[i].name);
	}
	return 0;
}
//打印结果:
/*
调用struct_cmp_age
14 anna
17 yui
20 hua
调用struct_cmp_name
14 anna
20 hua
17 yui

*/

qsort
打开cplusplus网站->qsort
qsort

void qsort(void* base,//需要排序的数组首元素地址
		  size_t num,//需要排序的数组的元素个数
		  size_t size,//需要排序数组的单个元素的大小
		  int (*compar)(const void*,const void*)//传递函数指针,需要自己写
		  }

可以看到的时,这里接受数组首元素的地址是用void*来接收。
提问:为什么呢?
回答:Void*指针 是无具体类型的指针。Void* 类型的指针可以接任意类型的地址(这种类型的指针是不能直接解引用操作的,也不能直接进行指针运算的)。
所以用void*接收是没问题的。然后,这个qsort函数不仅可以对整型数组排序,还可以对字符数组,浮点型数组,甚至是结构体数组。这也就造成了不能使用特定类型指针来接收的情况,如果使用了特定的类型,那其它类型就不能被接收了,所以才会选择使用void*来接收。
模拟实现qsort,但是因为目前还没有学快速排序,所以这里我们利用冒泡排序替代。

//主要逻辑
void swap(char* a, char* b, int size)
{
	char tmp = 0;
	for (int i = 0; i < size; ++i)//交换的实质其实就是指针所指向内容的交换.
	//因为char只能指向一个字节,所以我们需要传递size了解到要交换的字节大小,然后一个字节一个字节的交换。
	{
		tmp = *a;
		*a = *b;
		*b = tmp;
		a += 1;
		b += 1;
	}
}
void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	for (int i = 0; i < num - 1; ++i)
	{
		for (int j = 0; j < num - i - 1; ++j)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)//因为void类型的指针是不能直接解引用操作的,也不
			//能直接进行指针运算的。为了拿到比较位置的地址,我们需要将base强转为(char*),因为char*的加减整数时只会跳过一个字节,
			//这是最小的位移距离了。所以我们可以通过强转后的base拿到base[j]和base[j+1]的地址进行比较。
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);//开始交换
			}
		}
	}
}

测试

#include <stdio.h>
#include <string.h>
struct stu
{
	int age;
	char name[10];
};
int int_cmp(const void* a, const void* b)
{
	return (*(int*)a) - (*(int*)b);
}
int struct_cmp_age(const void* a, const void* b)//利用年龄排序
{
	return ((struct stu*)a)->age - ((struct stu*)b)->age;
}
int struct_cmp_name(const void* a, const void* b)//利用名字排序,因为字符串无法相减
//所以这里利用了strcmp进行字符串的比较
{
	return strcmp(((struct stu*)a)->name,((struct stu*)b)->name);
}
void swap(char* a, char* b, int size)
{
	char tmp = 0;
	for (int i = 0; i < size; ++i)//交换的实质其实就是指针所指向内容的交换,因为char只能指向一个字节,所以我们需要传递size了解到要交换的字节大小,然后一个字节一个字节的交换。
	{
		tmp = *a;
		*a = *b;
		*b = tmp;
		a += 1;
		b += 1;
	}
}
void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	for (int i = 0; i < num - 1; ++i)
	{
		for (int j = 0; j < num - i - 1; ++j)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)//因为void类型的指针是不能直接解引用操作的,也不能直接进行指针运算的。为了拿到比较位置的地址,我们需要将base强转为(char*),因为char*的加减整数时只会跳过一个字节,这是最小的位移距离了。所以我们可以通过强转后的base拿到base[j]和base[j+1]的地址进行比较。
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}
int main()
{
	int arr[10] = { 1,3,5,7,9,2,4,6,8,0 };
	bubble_sort(arr, 10, sizeof(int), int_cmp);//冒泡排序
	printf("对arr排序:\n");
	for (int i = 0; i < 10; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	struct stu s[3] = { {17,"yui"},{14,"anna"},{20,"hua"} };
	bubble_sort(s, 3, sizeof(s[0]), struct_cmp_age);
	printf("调用struct_cmp_age:\n");
	for (int i = 0; i < 3; ++i)
	{
		printf("%d %s\n", s[i].age, s[i].name);
	}
	bubble_sort(s, 3, sizeof(s[0]), struct_cmp_name);
	printf("调用struct_cmp_name:\n");
	for (int i = 0; i < 3; ++i)
	{
		printf("%d %s\n", s[i].age, s[i].name);
	}
	return 0;
}
//打印结果:
/*
对arr排序:
0 1 2 3 4 5 6 7 8 9
调用struct_cmp_age:
14 anna
17 yui
20 hua
调用struct_cmp_name:
14 anna
20 hua
17 yui
*/

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pr 2024下载安装,Adobe Premiere pro2024剪辑软件下载合集获取

Premiere Pro 2023中文版简称Pr&#xff0c;pr2023是一款视频编辑软件。 pr 2023不仅可以帮助用户对各种视频进行剪辑、旋转、分割、合并、字幕添加、背景音乐等基础的处理&#xff0c;还能帮助用户进行视频颜色校正、颜色分级、稳定镜头、调整层、更改片段的持续时间和速度、效…

进程通信(3): System V IPC

System IPC包括System V消息队列&#xff0c;System V信号量&#xff0c;System V共享内存区。 System V IPC使用一个key&#xff08;key_t&#xff09;作为他们名字&#xff0c;这个值由ftok函数通过路径名和一个id生成。 客户和服务器通过在路径名和id达成一致&#xff0c;双…

SpringCloud教程 | 第十篇: 读取Nacos的配置(失败情况2)

参考&#xff1a;SpringCloud搭建Nacos配置中心_springcloud nacos maven-CSDN博客 解决了pom.xml的依赖冲突后&#xff0c;在启动服务时&#xff0c;报错了&#xff0c;并且自动停止了。 2024-07-17 11:13:03.597 ERROR [com.alibaba.nacos.client.remote.worker] com.alibab…

在 Windows 平台搭建 MQTT 服务

一、引言 出于稳定性、可靠性、成本等多方面的考虑&#xff0c;众多 MQTT 服务实现更倾向于选择拥有丰富开源生态系统的 Linux 环境&#xff0c;Windows 平台上可选的 MQTT 服务相对有限。NanoMQ 是用于物联网边缘的超轻量级 MQTT 消息服务器&#xff0c;有着强大的跨平台和可…

超详细信息收集篇

1 域名信息收集 1.1 域名是什么 域名&#xff08;英语&#xff1a;Domain Name&#xff09;&#xff0c;又称网域&#xff0c;是由一串用点分隔的名字组成的 Internet 上某一台 计算机 或计算机组的名称&#xff0c;用于在数据传输时对计算机的定位标识&#xff08;有时也指地…

监控系统怎样做?

监控类型自底向上分为资源监控、服务监控和业务监控。希望打造公司级的监控系统最好的时机是系统规划时&#xff0c;如果把监控设计往后放&#xff0c;将会面临一个巨大的难题&#xff1a;推行和现有不兼容的规范。 三种监控类型 资源监控 这个相对简单&#xff0c;随着k8s的兴…

【深度学习】BeautyGAN: 美妆,化妆,人脸美妆

https://www.sysu-hcp.net/userfiles/files/2021/03/01/3327b564380f20c9.pdf 【深度学习】BeautyGAN: Instance-level Facial Makeup Transfer with Deep Generative Adversarial Network BeautyGAN: Instance-level Facial Makeup Transfer with Deep Generative Adversaria…

嵌入式物联网在教育行业的应用——案例分析

作者主页: 知孤云出岫 嵌入式物联网在教育行业的应用——案例分析 目录 作者主页:嵌入式物联网在教育行业的应用——案例分析一、引言二、智能教室&#xff1a;环境监测系统1. 硬件需求2. 电路连接3. 代码实现 三、个性化学习&#xff1a;智能学习平台1. 数据处理与分析2. 代…

下载仓颉sdk安装时遇到“无法运行”问题

图1. 社区地址&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 在GitCode社区中下载Cangjie-0.53.4-windows_x64的sdk后&#xff0c;双击安装时遇到“此应用无法在你的电脑上运行的问题” 经过反复排查后&#xff0c;确定是sdk直接下载有问题&#xff1b;‘需要…

Android Studio 汉化

下载地址&#xff1a;https://plugins.jetbrains.com/plugin/13710-chinese-simplified-language-pack----/versions/stable 下载完zpi包之后&#xff0c;进入&#xff1a;设置 -> 插件 导入即可。 注&#xff1a; 汉化包需要与 Android Studio 版本对应。

Matlab|基于蒙特卡洛法的电动汽车充电负荷计算

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序方法复现《V2G 模式下含分布式能源的配电网优化运行研究》第二章电动汽车无序充电模型&#xff0c;按照文章《V2G 模式下基于复杂网络的电动汽车有序充电策略》分析思路研究了不同数量电动汽车接入情况…

Jetpack Compose学习记录(一)

目录 前言控件实时预览Modifierremember状态提升 前言 学了一段时间的Compose&#xff0c;不得不说声明式UI比原生的开发效率快很多&#xff0c;而且Compose也是Google现在主推的开发模式&#xff0c;可以动态化地更改ui&#xff0c;相比于databinding对数据和布局进行绑定。C…

FreeCAD源码分析:Serialization

序列化(Serialization)是在程序运行时将对象转换成字节流的过程&#xff0c;进而将二进制字节流罗盘到文件系统或者通过网络发送到远端。而反序列化(Deserialization)则恰恰相反&#xff0c;是由二进制字节流重新构建对象的过程。 Ref. from Boost Serialization Here, we use …

电子标书怎么盖章?

电子标书怎么盖章&#xff1f;下面是利用e-章宝&#xff08;##### 与在线盖章的平台不同&#xff0c;专业的盖章软件在盖章时无需要上传你的文档&#xff0c;甚至在无网络下都可以使用&#xff0c;且保密性非常强&#xff0c;如e-章宝。如果你的文件保密性强&#xff0c;用网上…

CSS综合案例(快报模块头部制作)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 一、前述 二、案例分析 1.样例参看 2.拆分分析 三、案例实施 一、前述 案例&#xff1a;快报模块头部制…

open3d:随机采样一致性分割平面

1、背景介绍 随机采样一致性算法&#xff08;RANSAC Random Sample Consensus&#xff09;是一种迭代的参数估计算法&#xff0c;主要用于从包含大量噪声数据的样本中估计模型参数。其核心思想是通过随机采样和模型验证来找到数据中最符合模型假设的点。因此&#xff0c;只要事…

spring框架(ioc控制反转 aop面向切面编程)

目录 服务端三层开发&#xff1a; spring框架&#xff08;ioc控制反转 aop面向切面编程&#xff09; 1、Spring框架的优点 什么是事物&#xff1f; 2、Spring的IOC核心技术 什么是ioc&#xff1f; 代码举例&#xff1a;&#xff08;详细流程见笔记&#xff09; **applic…

NSSCTF-Web题目26(PHP弱比较)

目录 [SWPUCTF 2022 新生赛]funny_php 1、题目 2、知识点 3、思路 [ASIS 2019]Unicorn shop 4、题目 5、知识点 6、思路 [SWPUCTF 2022 新生赛]funny_php 1、题目 2、知识点 弱比较、双写绕过 3、思路 出现源代码&#xff0c;我们进行审计 第一个if 这里要我们GET方…

AI PC创造新商机,ISP与HPD集成单芯片方案受欢迎

今年以来&#xff0c;AI PC逐渐成为市场的焦点&#xff0c;因为AI PC给多年一成不变的PC市场带来了新的看点&#xff0c;也给了消费者升级的理由。今年是AI PC的元年&#xff0c;上半年不论是芯片厂商&#xff0c;还是PC厂商都在AI PC市场快速布局。AI PC相关的大模型、生态&am…

CRMEB 多商户Java版 v1.7更新预告!积分商城要来了

&#x1f469;‍&#x1f4bb;&#xff1a;下个版本有积分商城吗&#xff1f; &#x1f468;‍&#x1f4bb;&#xff1a;积分商城什么时候可以出呢&#xff1f; &#x1f64b;&#xff1a;什么时候能上拼团呢&#xff1f; &#x1f64b;‍♂️&#xff1a;图片热区&#xf…