02.指针的进阶

news2024/11/26 2:49:57

1.字符指针

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

int main()
{
  char ch = 'w';
  char *pc = &ch;
  *pc = 'w';
  return 0;
}

char * p---const char * p(因为常量字符串不能被修改) 

#include<stdio.h>
int main()
{
	const char * pstr = "hello bit.";
	printf("%s\n",pstr);//不用解引用,因为这是打印字符串,遇到\0就结束。
    //而pstr收到的是首地址,遇到\0结束
    //如果直接解引用,只能接收到首地址的字符。
    printf("%c",*pstr);
	return 0;
}

//本质是把字符串 hello bit. 首字符的地址放到了pstr中

const char* p = "abcdef"//首字符的地址传向p

//常量字符串不能被修改

char * p="abc";---改:const char * p="abc"(因为左侧为常量字符不能被修改)

EG1:

#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这些都是首地址 

2.指针数组

指针数组:存放指针的数组

//整型数组 - 存放整型的数组

//字符数组 - 存放字符的数组

//指针数组 - 存放指针(地址)的数组

2.1 字符指针数组(存放字符指针)

const char* arr[4] = { "abcdef","qwer", "hello bit", "hehe" };

//数组连续,内部的字符串不连续,字符串是连续的

int main()
{
	//存放字符指针的数组
	const char* arr[4] = { "abcdef","qwer", "hello bit", "hehe" };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%s\n", arr[i]);
	}

	return 0;
}

2.2 整形指针数组 

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int arr4[5] = { 0,0,0,0,0 };
	//指针数组
	int* arr[4] = {arr1, arr2, arr3, arr4};
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", *(arr[i] + j));//arr[i][j]
		}
		printf("\n");
	}

	return 0;
}

//取到arr1,拿到的就是aar1的起始地址 

分析:

int* arr1[10]; //整形指针的数组,每个元素是 int *
char *arr2[4]; //一级字符指针的数组,每个元素是char *
char **arr3[5];//二级字符指针的数组 ,每个元素是char **

3.数组指针

字符指针 - 存放字符地址的指针 - 指向字符的指针 char*

整型指针 - 存放整型地址的指针 - 指向整型的指针 int*

浮点型的指针 - 指向浮点型的指针 float* &double*

数组指针-存放数组地址的指针-指向数组的指针(本质是指针)

//数组名为首元素地址

int main()
{
	char ch = 'w';
	char* pc = &ch;

	int num = 10;
	int* pi = &num;

	int arr[10];

	//pa就是一个数组指针
	int (*pa)[10] = &arr;

	return 0;
}
	/*数组名 - 数组首元素的地址
	&数组名 - 是数组的地址
	数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样*/
int main()
{
	int a = 10;
	int *pa = &a;

	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	//&arr取出的是数组的地址,只有数组的地址才需要数组指针来接收
	//int (*p)[10] = &arr;//pa是一个指针
	printf("%p\n", arr);//int* 
	printf("%p\n", arr + 1);//4
	//数组首元素的地址,4
	printf("%p\n", &arr[0]);//int*
	printf("%p\n", &arr[0]+1);//4
	//数组首元素的地址,4
	printf("%p\n", &arr);// int(*)[10]
	printf("%p\n", &arr+1);//40,10*4
	//数组的地址,40
	return 0;
}

//数组指针对一维数组不友好 

int main()
{
	//整个数组
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	int (* p)[10] = &arr;//p为整个数组//p指向整个数组
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", (*p)[i]);//p指向整个数组,要是想指向1,2,3需要加入下标
                   //arr[i]
	}

	//首元素地址
	int*p = arr;//p首元素的地址
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}

	return 0;
}

//数组指针一般用于二维数组 

 ✳二维数组的打印方式

1.方法一

void print1(int arr[3][4], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

2.方法二

加一跳过一行

void print2(int(*p)[4], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{			
			//printf("%d ", (*(p + i))[j]);//p+i找到某一行,然后解引用找到这一行的元素,后找到这一行的某个元素
			printf("%d ", p[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][4] = { {1,2,3,4}, {2,3,4,5} , {3,4,5,6} };
	//print1(arr, 3, 4);
	print2(arr, 3, 4);//arr是首行的地址,所以自建的方程可以写为指针形式
    //二维数组的首元素是第一行

	return 0;
}

//二维数组转向一维数组

//数组指针存一个一维四项数组的地址 

//int (* p)[10]=&arr

p=&arr

* p=*&arr=arr

*p[i]=arr[i]

int arr[5];//整型数组,数组是五个元素
int *parr1[10];//指针数组,数组10个元素。每个元素是int *(就是把int * parr1[10]的parr1[10]拿走)
int (*parr2)[10];//parr2是数组指针,该指针指向一个数组,数组是10个元素,每个元素都是int类型的
int (*parr3[10])[5];//数组指针

//parr3是数组,数组有10个元素,数组的每个元素的类型是:int (*)[5],int (*)[5]的数组指针类型

4. 数组参数、指针参数


 辨析【数组】或者【指针】传给函数,了解如何设计函数

4.1 一维数组传参

数组传参,数组接受

数组传参,指针接受

#include <stdio.h>
void test(int arr[])//ok?yes
{}
void test(int arr[10])//ok?yes//传数组
{}
void test(int* arr)//ok?yes//传指针
{}
void test2(int* arr[20])//ok?yes
{}
void test2(int** arr)//ok?yes//int *的地址//int * arr是不对的
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

4.2 二维数组传参
 

int main()
{
	int arr[3][5] = { 0 };
	test(arr);
}
void test(int arr[3][5])//ok?yes
{}
void test(int arr[][])//ok?no//行可以省,列不可以省
{}
void test(int arr[][5])//ok?yes
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//ok?//主函数是三行五裂,数组名是首元素地址,即就是第一行的地址
{}
void test(int* arr[5])//ok?//no
{}
void test(int(*arr)[5])//ok?yes将第一行指针传递
{}
void test(int** arr)//ok?no二级指针接受一级指针的地址变量
{}

4.3 一级指针传参

#include <stdio.h>
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;//1的地址交给p
int sz = sizeof(arr)/sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);//指针变量传参,指针变量接收
return 0;
}

当函数为一级指针时,函数可以接收的参数:

void test(* p)
{}

int a=10;
int * p=&a;
int arr[10];
test(arr)//yes
test(p)//yes
test(&a)//yes

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;
}

当函数的参数为二级指针的时候,可以接收的参数
 

void test(char **p)
{ }
int main()
{
char c = 'b';
char*pc = &c;
char**ppc = &pc;
char * arr[10];
test(&pc);//yes
test(ppc);//yes
test(arr);//yes
return 0;
}

//二维数组和二维指针不可以等同 

//* arr==*(arr+0)=arr[0]

//**arr==*(arr[0]+0)==arr[0][0]

5. 函数指针

//数组指针-指向数组的指针

int (*p)[10]=&arr//数组的地址

//函数指针-指向函数的指针

int Add(int x,int y)

{ return x+y;}

int main()
{
	//printf("%p\n", &Add);
	//int arr[10];
	//int (*pa)[10] = &arr;

	//int (*pf)(int, int) = &Add;//函数的地址
	int (* pf)(int, int) = Add;//函数指的是pf,不是*pf,*只是告诉pf是指针
	int ret = (*pf)(2, 3);//解引用,但其实不用解引用
    //分析:原来://int ret = Add(2, 3);
    //但是由int (* pf)(int, int) = Add;可知,Add=pf
    //所以可以直接:int ret = pf(2, 3);

	printf("%d\n", ret);

	//int (*pf)(int, int) = Add;
	//&函数名和函数名都是函数的地址//ADD=&ADD
	
	//pf 是一个存放函数地址的指针变量 -  函数指针
	return 0;
}
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}

输出的是两个地址,这两个地址是 test 函数的地址

//保存函数地址的方式:

void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。 

EG1:

//代码1:此代码是一次函数调用
(*( void (*)()) 0 )();

//解释:

//把0强制转化为函数指针类型,0被当作一个地址

//该代码是一次函数调用

//调用0地址处的一个函数

//首先代码中将0强制类型转换为类型为void (*)()的函数指针

//然后去调用0地址处的函数


//代码2
void (*signal(int , void(*)(int)))(int);

//函数的返回类型,去掉signal(int , void(*)(int)

//signal没有和*括在一起,所以它是一个指针

// 该代码是一次函数的声明

// 声明的函数名字叫signal

// signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针能够指向的那个函数的参数是int

// 返回类型是void

// signal函数的返回类型是一个函数指针,该函数指针能够指向的那个函数的参数是int,返回类型是void

-----

代码2:简化版本

typedef void(*)(int) pfun_t//意思是你把前面的重命名,这只是分析,真实写法为:

typedef void(*pfun_t)(int);

-----改造
pfun_t signal(int, pfun_t); 

总结:

//数组的地址,放在数组指针中 

6. 函数指针数组

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

int main()
{
	//指针数组
	char* ch[5];

	int arr[10] = {0};
	//pa是数组指针
	int (*pa)[10] = &arr;
	
	//pf是函数指针
	int (*pf)(const char*) = &my_strlen;

	//函数指针数组
	int (*pfA[5])(const char*) = { &my_strlen};
    //[]优先于*,故pfA和[]结合,它为数组,去掉pfA[5]为函数指针
	return 0;
}

指针数组
//存放的是字符指针char* arr1[107.
//存放整型指针int* arr2[5].
函数指针数组 - 存放函数指针的数组

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

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

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

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];


答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。
函数指针数组的用途:转移表


EG:(计算器)

#include <stdio.h>
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;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		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;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

//扩展,所以法一需要改进

//函数指针数组存放上述函数的地址

//函数指针数组存放上述函数的地址
//转移表
int (*pf[5])(int, int) = { NULL, Add, Sub, Mul, Div };

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器\n");
			break;
		}
		else if (input>=1 &&input<=4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pf[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);

	return 0;
}

 //上述代码,只需要改变函数,和函数指针数组

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

int main()
{
	int arr[10];
	int (*pA)[10] = &arr;

	//函数指针数组
	int (* pf[5])(int, int);

	//ppf是指向函数指针数组的指针
    int * ppf=&pf
	int (*(* ppf)[5])(int, int) = &pf;//希望ppf是指针
    //ppf的类型就是去掉ppf

	return 0;
}


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

定义方式:

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[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}

8. 回调函数

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

改进:法一的计算器代码,存在冗余,代码重复出现四份

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 (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;
}

【经典】

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;
			}
		}
	}
}

//只能排序整数 

2.qsort函数的使用:

算法:快速排序

void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));

//第一个参数:数组的起始地址

//第二个参数:数组的大小,单位是元素

//元素的大小,一个元素是几个字节

//第四个参数:比较函数,void *指针无具体类型的指针,用具体的可以导致类型不匹配,可认为是通用指针

void *不能直接解引用,也不可以++,但指针类型可以转换

//size_t是标准C库中定义的,它是一个基本的与机器相关的无符号整数的C/C + +类型, 它是sizeof操作符返回的结果类型,该类型的大小可选择。其大小足以保证存储内存中对象的大小(简单理解为 unsigned int就可以了,64位系统中为 long unsigned int)。通常用sizeof(XX)操作,这个操作所得到的结果就是size_t类型。

//冒泡改变,需要改变比较大小的部分:

if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }//因为比较结构体、字符串不可以直接用大于号小于号

1.比较整型的数据

✳<0,e1<e2

✳>0,e1>e2

//实现一个比较整型的函数
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;//e1强制转换为int *后解引用
}

//使用qsort对数组进行排序,升序

void test1()
{
	
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//bubble_sort(arr, sz);
	//库函数中一个排序函数:qsort
	//qsort(arr, sz, sizeof(arr[0]), cmp_int);
	//0 1 2 3 4 5 6 7 8 9
	//打印
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

2.使用qsort排序结构体

//使用qsort 排序结构体
struct Stu
{
	char name[20];
	int age;
};
//按照学生年龄排序
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);//字符串的比较用strcmp
}

void test2()
{
	struct Stu s[3] = { {"zhangsan",20}, {"lisi", 50}, {"wangwu", 33} };
	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);
}

//qsort可以使用任何类型排序

改造冒泡排序,使得这个函数可以排序任何指定的数组(运用冒泡排序思想)

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

注意:这里第一次使用 void* 的指针,讲解 void* 的作用。

//第一个参数是void *,是为了能够接收任意地址

//第二个参数:元素个数,排序需要知道元素个数

//第三个参数:宽度,因为不知道是什么类型的

//第四个参数:不知道什么类型,const 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 sz, size_t width, int (*cmp)(const void*e1,
 const void*e2))
{
	//趟数
	size_t i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base+j*width, (char*)base+(j+1)* width)>0)//char *指针
			{
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
void test3()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);//自己写的函数,类比qsort
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
//使用我们自己写的bubble_sort函数排序结构体数组
void test4()
{
	struct Stu s[3] = { {"zhangsan",20}, {"lisi", 50}, {"wangwu", 33} };
	int sz = sizeof(s) / sizeof(s[0]);
	//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}


int main()
{
	//test1();
	//test2();
	//test3();
	test4();
	return 0;
}

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

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

相关文章

从汇编的角度了解C++原理——虚函数

文章目录1、虚函数1.1、虚函数储存结构1.2、子类重写虚函数1.3、在栈上调用虚函数1.4、在堆上调用虚函数(通过指针调用&#xff0c;多态)本文用到的反汇编工具是objconv&#xff0c;使用方法可以看我另一篇文章https://blog.csdn.net/weixin_45001971/article/details/12866064…

编译基于armV8架构的opencv,并在rock3a开发板上运行

近期在基于arm开发板做图像识别任务开发时&#xff0c;需要用到Opencv库 之前在做rknpu开发时&#xff0c;开发sdk里面已经集成了opencv 但是该opencv开发包不能实现imshow/VideoCapture等函数&#xff0c;经过调研&#xff0c;决定对opencv源代码进行编译&#xff0c;生成arm…

安科瑞能耗监测系统在新疆昌吉市政务中心项目的研究与应用

安科瑞 华楠摘要&#xff1a;大型公共建筑总面积不足城镇建筑总面积的4%&#xff0c;但总能耗却占全国城镇总耗电量的22%&#xff0c;大型公共建筑单位面积年耗电量达到70&#xff5e;300KWh&#xff0c;为普通居民住宅的10&#xff5e;20倍。公共建筑是节能大户和节能核心&…

Delphi 11.2 安装 CnWizards 组件包

官方网址&#xff1a;https://www.cnpack.org/showdetail.php?id900&langzh-cn 开源免费的组件包&#xff0c;大大提高了开发效率&#xff0c;再次感谢大佬们的无私奉献 这个组件包主要是为了实现一些delphi没有的便捷设置&#xff0c;以及能给delphi增加了一些好用的辅助…

惊艳的产品背后,是锐利的设计思维

缘起 几年前&#xff0c;我偶然用一个 叫 Zine 的小app 写了两篇文章&#xff0c;感觉非常好。 后来在网上认识 了Zine 团队的创始人 Louis&#xff0c;也喜欢上了他们的另一个 App&#xff1a;Varlens&#xff0c; 最近他们推出了记笔记的 App Lattics&#xff0c;一些功能也…

《 Unity Shader 入门精要》 第3章 Unity Shader 基础

第3章 Unity Shader 基础 3.1 Unity Shader 概述 材质与 Unity Shader 在 Unity 中我们通常需要将材质&#xff08;Material&#xff09; 和 Unity Shader 配合使用&#xff0c;常见流程如下&#xff1a; 创建一个材质创建一个 Unity Shader&#xff0c; 并将它赋给材质将材…

Android View类

布局定义了应用中的界面结构&#xff08;例如 Activity 的界面结构&#xff09;。布局中的所有元素均使用 View 和 ViewGroup 对象的层次结构进行构建。View 通常用于绘制用户可看到并与之交互的内容。ViewGroup 则是不可见的容器&#xff0c;用于定义 View 和其他 ViewGroup 对…

AppScan自定义扫描策略,扫描针对性漏洞

系列文章 AppScan介绍和安装 AppScan 扫描web应用程序 AppScan被动手动探索扫描 AppScan绕过登录验证码深入扫描 第五节-AppScan自定义扫描策略&#xff0c;扫描针对性漏洞 AppScan安全扫描往往速度是很慢的&#xff0c;有些场景下他的扫描项目又不是我们需要的&#xff0c;…

如何实现六轴机械臂的逆解计算?

1. 机械臂运动学介绍 机械臂运动学 机器人运动学就是根据末端执行器与所选参考坐标系之间的几何关系&#xff0c;确定末端执行器的空间位置和姿态与各关节变量之间的数学关系。包括正运动学&#xff08;Forward Kinematics&#xff09;和逆运动学&#xff08;Inverse Kinemati…

渔业养殖远程监控系统解决方案

传统的水产养殖依靠养殖者的经验进行观察&#xff0c;信息不准确&#xff0c;调控不及时&#xff0c;养殖规模扩大难&#xff0c;人工成本高。除此之外传统水产养殖以个户居多&#xff0c;生产管理方式粗放&#xff0c;个体生产能力不足&#xff0c;养殖产品的品质难以保障。 …

AppScan扫描报告

系列文章 AppScan介绍和安装 AppScan 扫描web应用程序 AppScan被动手动探索扫描 AppScan绕过登录验证码深入扫描 AppScan自定义扫描策略&#xff0c;扫描针对性漏洞 第六节-AppScan扫描报告 1.加载扫描结果 1.点击【打开】 2.选择之前保存过的扫描结果 3.等待加载完成 …

RK35XX(3568) Android WSL ubuntu22.04 编译环境配置

前言&#xff1a;装Ubuntu真机操作是很流畅但是没什么软件&#xff0c;装Vmware虚拟机操作卡顿配置也麻烦。那不如试一试wsl吧&#xff0c;命令行操作&#xff0c;流程又快捷wsl简介&#xff1a;适用于 Linux 的 Windows 子系统可让开发人员按原样运行 GNU/Linux 环境 - 包括大…

JAVA面试(如何进行有效面试)

1、什么是面试它是一种面试人与求职者之间相互交流信息的有目的的会谈。它使招聘方和受聘方都能得到充分的信息&#xff0c;以在招聘中作出正确的决定。面试是一个双方彼此考量和认知的过程。2、面试的目标从求职者那里获取与个人行为、工作有关的信息&#xff0c;以确定求职者…

c语言数组复习

1、一维数组 ----------&#xff08;1&#xff09;、键盘输入 10 个数&#xff0c;求最大值和最小值&#xff08;最简单的方法&#xff09; ----------&#xff08;2&#xff09;、数组的逆置 #include<stdio.h> void test01() {int arr[10] { 0 };int n sizeof(arr)…

【IoT】硬件选型:如何正确区分电子线的端子型号?

问题提出 笔者最近负责的一款重力传感器由于没有端子&#xff0c;需要在生产时自己压端子&#xff0c;这个时候就会涉及端子的选择。 端子介绍 一般来说&#xff0c;端子有多种不同的型号&#xff0c;在使用的时候&#xff0c;你必须要注意到每种型号之间的差别。 端子一般有XH…

权限管理---尚硅谷

1.项目基础 2.定义统一返回结果对象 3.Nodejs 4.前端内容编写 5.菜单详情 6.SpringSecurity权限管理 7.添加登录日志 8.操作日志 9.后端打包 10.前端打包 11.动态sql日期的判断 项目基础 定义统一返回结果对象定义全局统一返回结果类 import lombok.Data;/*** 全局统一返回结果…

小程序容器技术助力突破智能汽车瓶颈

作为一种综合系统&#xff0c;智能车辆集环境感知、规划决策、多等级辅助驾驶等功能于一体。近年来&#xff0c;智能车辆已经成为世界车辆工程领域研究的热点和汽车工业增长的新动力&#xff0c;很多发达国家都将其纳入到各自重点发展的智能交通系统当中。在共享经济兴起和汽车…

如何写好JS

本节课从实践维度解读在实际编码过程中何种类型的 JavaScript 代码称之为“好代码”&#xff0c;并从 JS 出发&#xff0c;总结其他语言编码可遵循的共性原则&#xff0c;由浅入深&#xff0c;其三大原则是&#xff1a; 各司其职——html&#xff0c;css&#xff0c;js分离组件…

通达信接口QQ是什么端口?

可以将通达信接口QQ视为使用通达信市场软件作为数据库&#xff0c;然后将信息整合为策略的前提&#xff0c;所有行为都是自动化的。通达信接口的优势在于交易策略是事先制定的。是否基于市场波动&#xff0c;不受个人情绪的影响&#xff0c;可以大大降低个人原因造成的错误。 …

[ACTF2020 新生赛]BackupFile

目录 信息收集 思路 构造payload 知识补充 信息收集 从题目来看应该是让扫描备份文件(backupfile) 进入页面就一句话 Try to find out source file! 先用dirbuster模糊扫描一下目录 常见的如下 index.phps index.php.swp index.php.swo index.php.php~ index.php.bak ind…