【C进阶】指针进阶(1)_二次复习版

news2024/11/28 12:29:52

        

目录

1. 字符指针

1.1常量字符串的修改

加上const解决问题

打印常量字符串

1.2数组存放的字符串

1.3例题:数组创建与常量池的区别

2. 指针数组 

2.1字符指针数组

2.2整型指针数组

2.3使用3个一维数组,模拟实现一个二维数组

2.4例题:

3.数组指针

3.1 数组指针的定义

3.2arr 和 &arr 有什么区别?

3.3 数组指针的使用

使用数组指针访问并打印一维数组

在自定义函数内打印二维数组

二维数组接收

 数组指针接收

4. 数组参数、指针参数

4.1 一维数组传参

总结:

4.2 二维数组传参

总结:

4.3 一级指针传参  

4.4 二级指针传参  

5.函数指针

创建和使用一个函数指针变量

阅读两段有趣的代码:

例题1. 0地址函数调用

例题2 函数声明


         这篇文章我重新又总结了一遍,所以说总结的可能比第一次写的更深刻,欢迎博主们的来访与指点!

指针的基础概念知识: 

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。

3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

4. 指针的运算。(导航到这去 --> http://t.csdn.cn/95Veg )

1. 字符指针

#include<stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;//pc是字符指针
	return 0;

}

这段代码定义了一个字符变量 ch 并将其初始化为 ‘w’。然后定义了一个字符指针 pc,并将其初始化为 &ch,即 pc 指向 ch 的地址。

因为 ch是一个字符变量,其在内存中占据1个字节的空间。&ch 是 ch 的地址,即它指向 ch 在内存中的位置。pc变量的类型是字符指针,即它存储的是一个地址,该地址指向一个字符类型的变量。在这种情况下,pc 存储的是 ch 的地址,因此它指向 ch 变量的位置。

通过将字符指针指向字符变量的地址,可以使用指针来访问和修改该变量的值。例如,可以使用 *pc 来访问 ch 的值,即 *pc 的值为 w。可以使用 *pc = x 来修改 ch 的值,即将 ch 的值改为 x。

 代码执行:

1.1常量字符串的修改

char* p = "abcdef"中,常量字符串"abcdef"这个表达式的值是首字符'a'的地址,本质是把字符串首字符‘a’的地址存储到p中去。

#include<stdio.h>
int main()
{
	char* p = "abcdef";//常量字符串,意思不是把字符串的值赋值给p,"abcdef\0"是7个字符
//所以说,占据7个字节的空间,不可能放到占用4个字节的指针变量p的空间里面去
//本质是把常量字符串首元素的地址--'a'的地址给了p
	
	*p = 'w';//程序报错的原因在于"abcdef"是常量字符串不能更改

	return 0;
}

 执行:

在 C 语言中,把字符串常量赋值给一个字符指针是合法的,但是对于指向字符串常量的指针,我们不能通过指针来修改其所指向的字符串的值,因为字符串常量是只读的,试图修改其值会导致未定义的行为。 

如果将 p 声明为 char* 类型,那么指针指向的是一个字符串常量。因为字符串常量是只读的,所以试图通过 *p='w' 来修改字符串常量的值是非法的,这会导致编译错误

加上const解决问题

寻求解决方法: 

为了避免这种情况,可以使用 const 关键字修饰指针变量p,将其声明为指向常量的指针,即const char* 类型。这样一来,我们就不能通过指针来修改其所指向的字符串的值,这就保证了程序的正确性。

因此,将 p 声明为 const char*  类型是为了避免试图修改字符串常量的值,从而保证程序的正确性。

这样它就会直接显示错误,提醒使用者这是个常量值不能被改掉。

打印常量字符串

实际上,只要拿到常量字符串"abcdef"的首字符地址也可以打印出整个字符串。

我们定义了一个字符指针 p,并将其初始化为指向字符串 "abcdef" 的首字符‘a’地址。然后,我们使用 %s 格式化字符来打印这个字符串。由于 p 指向字符串的首字符地址,因此当我们打印 p 时,它会从该地址开始输出,直到遇到终止字符 '\0' 为止,因此整个字符串 "abcdef" 都会被打印出来。

#include<stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%s\n", p);

	return 0;
}

1.2数组存放的字符串

 与常量字符串不同的是数组,数组的空间是可以修改的。

#include<stdio.h>
int main()
{
	char arr[] = "abcdef";
	char* p = arr;
	*p = 'w';
	printf("%s\n", arr);
}

p指向的是数组的首元素,arr数组是可以修改的,数组在内存中是连续存放的,只要拿到首字符的地址,就会顺藤摸瓜一直打印到‘\0’停止。

执行:

1.3例题:数组创建与常量池的区别

图解:

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

思路:

创建arr1和arr2两个数组,他们的地址肯定是不相同的,是两个变量。(以现在的知识储备,可以仔细想想,如果定义了两个数组,里面放的东西是一样的,修改其中一个arr1,另一个arr2也会跟着变吗,这样的设计肯定是不合理的,所以arr1和arr2的地址是不相同的。)
 

但是下面str3和str4是字符串常量  str3在创建的时候,会先去字符串常量池中找,看是不是有hello bit.  如果没有就创建,str4也是一样,但是它找到了,所以它就指向常量池中的这个字符串。

执行:

2. 指针数组 

传送门-->指针和数组知识--http://t.csdn.cn/V3VqG

字符数组–存放字符的数组   char arr1[10];
整型数组–存放整型的数组   int arr2[5];

 

指针数组–存放的就是指针
存放字符指针的数组–字符指针数组   char* arr3[5];
存放整型指针的数组–整型指针数组   int*arr[6];

2.1字符指针数组

这样的代码表达的意思是:

int main()
{
    char* arr[] = { "abcdef","hehe","qwer" };
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%s\n", arr[i]);
    }
    return 0;
}

对上述代码解析:

数组的每个元素是char*,能够存放的元素为字符指针,那每个元素只要存放一个字符的地址即可。数组的第一、二、三个元素都是char*,第一个元素它里面存放的是'abcdef\0 'a'的地址,第二个元素存放的是'hehe\0'里面第一个'h'的地址,第三个里面存放的是'qwer\0'这个'q'的地址。

它其实最终形成的这样一个形象是这样子:

arr[0]是‘a’的地址,arr[1]是‘h’的地址,arr[2]是‘q’的地址。那现在直接遍历这个数组,就能拿到这些字符串首字符的地址,就能够打印出来这些字符串。

2.2整型指针数组

int main() {
	
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	int e = 50;
	int* arr[5] = { &a,&b,&c,&d,&e };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(arr[i]));
	}
	return 0;
}

abcde五个变量不一定连续,把它们的首元素地址放在arr数组里面,数组在内存中是连续存放的

执行: 

2.3使用3个一维数组,模拟实现一个二维数组

定义了三个整型数组arr1、arr2和arr3,分别存储了一组连续的整数。然后定义了一个整型指针数组arr,它包含了arr1、arr2和arr3三个数组的首元素地址。

for循环用于遍历arr数组中的每一个元素,即每个数组的首地址,然后在内层循环中,遍历了每个数组中的所有元素,并使用指针算术运算访问了每个元素的值并打印输出。

#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5};
	int arr2[] = { 2,3,4,5,6};
	int arr3[] = { 3,4,5,6,7};
	int* arr[] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i <3 ; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			//printf("%d ", arr[i][j]);
			printf("%d ", *(arr[i] + j));
           printf("%d ", *(*(arr+i) + j));
 		}
		printf("\n");
	}
	return 0;
}

 *(*(arr+i) + j) == *(arr[i] + j) == arr[i][j] 三种写法等价

首元素地址加偏移量找到下标是这个元素的地址了,再解引用就找到此元素。

执行: 

按自己的理解再写一个

图解:

用一个数组名表示首元素地址,把a,b,c放到arr中说明是把首元素地址存进去了,因为是个整型地址,所以是个整型指针,所以是int*arr[3]每个元素的类型是int*,本质是用一个指针数组,来管理3个int数组

#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	int b[] = { 2,3,4,5 };
	int c[] = { 3,4,5,6 };
	int* arr[3] = { a,b,c };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
 
}

执行: 

 arr[i][j]可写成*(arr[i]+j),一个整型指针(地址)加j向后偏移j个元素

2.4例题:

int main()
{
    int* arr[3][3] = { 1 };

    int p = **arr;
    printf("%p", p);

}

 

3.数组指针

3.1 数组指针的定义

数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整形指针: int * pint ; 能够指向整形数据的指针。
浮点型指针: float * pf ; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。

类比:

整型指针 -- 指向整型的指针int a = 10;

int* p = &a;

字符指针-指向字符的指针char ch = 'w';

char* pc = &ch;

数组指针–指向数组的指针

int arr[10] ;//数组

int (*pa)[10] = &arr;//取出的是数组的地址

char arr[10] --  char (*pc)[10] = &arr;

int*arr[5];--       int*  (*pc ) [5]  =&arr

下面代码哪个是数组指针?
int * p1 [ 10 ];
int ( * p2 )[ 10 ];
//p1, p2 分别是什么?

答:int (*p2)[10];

p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针,[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

易错点:


int main()
{
	int a = 10;
	int* pa = &a;//1
 
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
	int(*p)[10] = &arr;//2
 
	int(*p2)[10] = arr;//3
 
}

对上述代码总结:

1.*说明pa是指针,int说明pa指向的a变量是整型
2.&arr取出的是数组的地址,只有数组的地址才需要数组来接收,类型是int[10]

3.arr是不能放进左边指针(类型是int[10])里面的,arr本身的类型是int*

3.2arr 和 &arr 有什么区别?

数组名绝大部分情况下是数组首元素的地址

但是有2个例外:

1. sizeof(数组名) - sizeof内部单独放一个数组名的时候,数组名表示的整个数组,计算得到的是数组的总大小

2. &arr - 这里的数组名表示整个数组,取出的是整个数组的地址,从地址值的角度来讲和数组首元素的地址是一样的,但是意义不一样

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	//printf("%d\n", sizeof(arr));
	printf("%p\n", arr);//int * 
	printf("%p\n", arr+1);//4

	printf("%p\n", &arr[0]);//int* 
	printf("%p\n", &arr[0]+1);//4

	printf("%p\n", &arr);//int(*)[10]
	printf("%p\n", &arr+1);//40

	return 0;
}

上图可看出arr==&arr[0](地址值相等),也说明指针类型决定了指针+1到底加的是几,即为数组名是数组首元素地址,而&arr是整个数组的起始地址

我们已知的两种访问数组的方式:


int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;

	//使用指针来访问
	int* p = arr;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	//下标的形式访问数组
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

3.3 数组指针的使用

使用数组指针访问并打印一维数组

理解:p == &arr,*p == *&arr,*p == arr 

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

	int sz = sizeof(arr) / sizeof(arr[0]);
	int (* p)[10] = &arr;//注意这个地方不是arr,&arr是整个数组的地址,所以要用数组指针接收
	int i = 0;
	//p  --- &arr
	//*p --- *&arr
	//*p --- arr
	//虽然对,但是不推荐
	for (i = 0; i < sz; i++)
	{
		printf("%d ", (*p)[i]);
	}
	printf("\n");
	//虽然对,但是不推荐
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *((*p) + i));
	}
	return 0;
}

(*p)+i的意思:下标为i的元素的地址,*((*p)+i)得到下标为i的元素。

以上两种写法均正确,但是不推荐。

在自定义函数内打印二维数组

二维数组接收

打印二维数组传参的时候可以形参用二维数组接收。

void print1(int arr[3][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[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};
	/*二维数组的数组名,也表示首元素的地址
	二维数组的首元素是第一行
	首元素的地址就是第一行的地址,是一个一维数组的地址*/
	//
	print1(arr, 3, 5);
	return 0;
}

 数组指针接收

也可以用数组指针接收,并遍历数组

二维数组的数组名,也表示首元素的地址
二维数组的首元素是第一行
首元素的地址就是第一行的地址,是一个一维数组的地址

void print(int(*arr)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			//printf("%d ", *(*(arr + i) + j));
			printf("%d ", arr[i][j]);
	       //printf("%d ", (*(arr + 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};
	/*二维数组的数组名,也表示首元素的地址
	二维数组的首元素是第一行
	首元素的地址就是第一行的地址,是一个一维数组的地址*/
	//
	print1(arr, 3, 5);
	return 0;
}

*(*(arr + i) + j)--> arr 是二维数组的数组名,数组名是数组首元素地址,也就是二维数组的首元素第一行的地址,arr+i就是二维数组第 i 行地址,*(arr+i)得到二维数组第i行,也就是第i行的数组名,也就是arr[i],arr[i]数组名又相当于数组首元素地址,也就是第i行第一个元素的地址,即为&arr[i][0]

也就是说,     *(arr+i) == arr[i] == &arr[i][0]

也可以这样写:    printf("%d ", (*(arr + i))[j]);
(*(arr+i))[j])意思是访问完第i行之后再访问[j]列,可以写成arr[i][j]

易错点:

1.int arr[5];整型数组,数组有10个元素

2.int *parr1[10];指针数组,数组10个元素,每个元素都是int*类型的

3.int(*parr2)[10];parr2是数组指针,该指针指向一个数组,数组是10个元素,每个元素是int类型

4.int (*p)[10]=arr,如何强转?(int(*)[10])arr

5.int* p=&num,对p解引用的类型int 

6.int(*p)[10]=&arr,&arr赋给p,p--arr,*p=*&arr=arr

7.int(*parr3[10])[5]

parr3是数组,数组中存放的指针,该指针指向的又是数组。

 parr3和[10]结合说明parr3是数组,大小存10个元素,每个元素的类型是int(*)[5],

可以理解为指针数组里面存放的是数组指针。

4. 数组参数、指针参数

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//1 ok
{}
void test(int arr[10])//2 ok
{}
void test(int* arr)//3 ok
{}
void test2(int* arr[20])//4 ok
{}
void test2(int** arr)//5 ok
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);//&arr[0]
	test2(arr2);//&arr2[0]
}

1.数组传参,形参部分写出数组,可以不指定大小,指定类型即可

2.数组传参,数组接收,大小指定10个元素,可以

总结:1和2是数组传参数数组接收

3.arr[10]数组10个元素,每个元素都是int,数组名表示首元素地址(整型的地址),就应该放到int指针里去 3.是数组传参指针接收

4.数组传参可以写成一样的,大小可以不写:int *arr[]

5.数组名表示首元素地址,每个元素都是int*的地址,是一级指针地址,可以用二级指针接收,不可以写成int* arr

总结:

一维数组传参,形参可以是数组,也可以是指针的当时形参是指针的时候,要注意类型

4.2 二维数组传参

void test(int arr[3][5])//1 ok
{}
void test(int arr[][])//2 no
{}
void test(int arr[][5])//3 ok
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。//这样才方便计算
void test(int *arr)//4 no
{}
void test(int* arr[5])//5 no
{}
void test(int (*arr)[5])//6 ok
{}
void test(int **arr)//7 no
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
//二维数组传参,参数可以是指针,也可以是数组

//如果是数组,行可以省略,但是列不能省略
//如果是指针,传过去的是第一行的地址,形参就应该是数组指针
}

1.可以写成一模一样的

2.二维数组传参,函数形参的设计只能省略第一个[]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素,为什么❓

3.正确写法。

4.数组传参,要么写成数组要么写成指针,一级指针,不对,应该是数组指针

5.数组传参,形参要么写成数组,要么指针。但是这个形参是个数组,但又不是二维数组,能存指针但又不是指针。(指针数组)

6.数组前有(*arr),说明是指针,指针指向的[]是数组,数组5个元素,每个元素是int(一行五个元素)

7.看传过来的类型是什么,二级指针是用来接收一级指针的地址的,而arr传的是第一行的地址

总结:

二维数组传参,参数可以是指针,也可以是数组

  1. 如果是数组,行可以省略,但是列不能省略
  2. 如果是指针,传过去的是第一行的地址,形参就应该是数组指针

                                                      对2.的解释

在许多编程语言中,对于二维数组(或者说是矩阵),通常需要知道每一行包含多少元素。这是因为二维数组在内存中通常是连续存储的,所以我们需要知道每行有多少元素来正确地在内存中定位元素。

例如,如果我们有一个二维数组,每行有n个元素,那么第i行第j个元素在内存中的位置就是i * n + j。这种计算方式基于数组的第一个元素位置为0,以及数组的行和列都是从0开始计数。

这就是为什么在声明和使用二维数组时,必须知道一行有多少元素的原因。如果你不知道每行的元素数,你就无法正确地在内存中定位到你要访问的元

素。

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;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0;
}

 p是一级指针传参,一级指针接收即可 

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.函数指针

类比:

整型指针–指向整型的指针int*

字符指针–指向字符的指针char*

数组指针–指向数组的指针int arr[10];

函数指针–指向函数的指针

数组: 数组名和&数组名是有区别的

函数:函数名和&函数名都是函数的地址,没有区别

int Add(int x,int y)
{
	return x + y;
}
int main()
{
 
    printf("%p\n", &Add);
	printf("%p\n", Add);
 
  return 0;
}

 可看到以上代码两种表示方法结果是一样的,函数没有什么首元素的地址,数组才有,所以函数指针用 & 和 不用& 结果都是一样的。

总结:&函数名和函数名都是函数的地址

创建和使用一个函数指针变量

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	//pf就是函数指针
	int (* pf)(int, int) = Add;//函数的地址要存起来,就得放在【函数指针变量】中
	
	int ret1 = (*pf)(3, 5);
	int ret2 = Add(3, 5);
	int ret3 = pf(3, 5);

	printf("%d\n", ret1);
	printf("%d\n", ret2);
	printf("%d\n", ret3);

	return 0;
}

pf是一个存放函数地址的指针变量 - 函数指针 ,和数组指针写法相似,注意:函数指针变量名是pf,不是*pf,*只是告诉我们pf是指针,,要用*pf一定要加(),否则*pf(2,3),求其结果是*5

执行:

总结:pf(3,5) == (*pf)(3,5) ==Add(3,5)

阅读两段有趣的代码:

例题1. 0地址函数调用

int main()
{
	//代码1
	(*(void (*)())0)();
 
	return 0;
 
}

整体看下来,这样分析:

1. 将0强制类型转换为void (*)() 类型的函数指针
2. 这就意味着0地址处放着一个函数,函数没参数,返回类型是void
3. 调用0地址处的这个函数 

细致分析:

从0开始下手,0是整型int,若在(void(*)())放p,为void(*p)()说明是函数指针,(void(*)())0说明是把0强制转换成函数指针类型(0被当成一个地址,0地址,放的参数是无参,返回类型是void的函数),

在前面加*,写成*(void(*)())0,说明对0地址做解引用操作,那个函数是无参的,所以最后面的括号写成()没有传任何参数

总结:该代码是一次函数调用,调用的0地址处的一个函数,首先代码中将0强制类型转换为类型void(*)()函数指针,然后去调用0地址处的函数--------来自《C陷阱和缺陷》

可用上述代码类比一下 

例题2 函数声明

int main(){
 
void(*signal(int, void(*)(int)))(int);
 
return 0;
}

从signal开始,*和signal没()一起,说明不是个指针,而signal括号包含两个参数类型,一个int,另一个是函数指针,说明signal第一个参数是整型,第二个为函数指针,函数除了函数名和参数外,还有返回类型,把函数名和参数类型删去,即为返回类型:void(*)(int)

虽然这样更容易理解,但这样写是错误的:void(*)(int)signal(int,void(*)(int));(只用于方便理解)

总结:
上述的代码是一个函数的声明
函数的名字是signal

signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
该函数指针指向的函数参数是int,返回类型是void
     
signal函数的返回类型也是一个函数指针
该函数指针指向的函数参数是int,返回类型是void

为了看起来方便,可以用typedef重命名类型

typedef int* ptr_t;

typedef void(*pf_t)(int);   //将void(*)(int)类型重新起个别名叫pf_t

所以可以写成这样:

                             pf_t signal(int, pf_t);

区别:

typedef void(*pf_t2)(int); //pf_t2是类型名

void(*pf)(int);//pf是函数指针变量的名字

接下来还有指针进阶(2),上述文章如有错误欢迎大佬指针,感谢你的来访。

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

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

相关文章

快速上手Webpack打包指南:用简单的步骤掌握Webpack的使用技巧

目录 概念&#xff1a;1. webpack 打包简介1.0 多个 JS 文件打包&#xff1a;1.1 webpack 数组形式1.2 webpack 对象形式 总结 Webpack的打包过程可以总结为以下几个步骤&#xff1a; 1.入口点配置&#xff1a;在Webpack的配置文件中&#xff0c;我们需要指定一个或多个入口点…

Android Studio 代码模板插件实现

Android Studio 代码模板插件 背景 可以跳过背景和简述&#xff0c;从模板插件实现开始看. 开发新页面时&#xff0c;原先需要写一堆模板代码。比如用Databinding写列表结构的页面&#xff0c;需要手写以下文件&#xff1a; XxActivity.ktXxFragment.ktXxViewModel.ktXxListA…

从零开始学习CTF

前言 CTF简介 中文一般译作夺旗赛&#xff0c;在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式 CTF起源于1996年DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式 竞赛模式 解题模式&#xff1a; 在解题模式…

如何引导客户进行自助服务,提高员工工作效率?

搭建帮助中心是大多数企业都在尝试做的事情&#xff0c;它的重要性对于企业来说不言而喻。现在对于企业来说&#xff0c;搭建帮助中心或许不是什么难事&#xff0c;但是关于帮助中心&#xff0c;有几个问题需要思考清楚&#xff0c;才能让其发挥最大的价值。 一、如何让用户养成…

Android手机app页面布局方法

app页面布局方法 1. FrameLayout&#xff08;帧布局&#xff09; (1) FrameLayout是最简单的布局了。所有放在布局里的控件&#xff0c;都按照层次堆叠在屏幕的左上角。后加进来的控件覆盖前面的控件。 2. LinearLayout&#xff08;线性布局&#xff09; (1) LinearLayout按…

第九章 多组学简介

第一节 什么是多组学 多组学encode计划是一个大型国际合作计划&#xff0c;旨在研究各种生物体系中的功能元件和基因组功能。该计划的目标是建立一个全面的生物信息学数据库&#xff0c;包括人类和其他生物的基因组和表观基因组、转录组、蛋白质组、代谢组和表型组等。 多组学…

高等数学❤️第一章~第三节~极限❤️间断点及其分类

【精讲】高等数学中的间断点及其分类 博主&#xff1a;命运之光的主页 专栏&#xff1a;高等数学 目录 【精讲】高等数学中的间断点及其分类 导言 一、间断点的概念 二、间断点的分类 必需记忆知识点 知识点1 知识点2 例题&#xff08;用于熟悉高等数学中的间断点及其…

dp算法篇Day11

“哎呀&#xff0c;哎呀&#xff0c;流云开一朵&#xff0c;哟诶嘿哟&#xff0c;哟诶嘿哟~” 51、目标和 (1) 题目解析 包括之后的一些题目&#xff0c;乍一眼看可能你不会发现它与dp问题有何相连&#xff0c;可是按照按照题目又难以想出 思路、写出代码来。因此&#xff0c;…

PostgreSQL——编码“GBK“的字符0x0xa8 0x27在编码“UTF8“没有相对应值`

问题&#xff1a;编码"GBK"的字符0x0xa8 0x27在编码"UTF8"没有相对应值 原因&#xff1a;客户端编码与服务端编码不一致 select name,setting,context from pg_settings where name like %encoding%; 解决方案&#xff1a;修改客户端编码方式和服务端一致…

Jmeter接口测试工具的一些使用小技巧

如何使用英文界面的JMeter Jmeter启动时会自动判断操作系统的locale 并选择合适的语言启动&#xff0c;所以&#xff0c;我们启动jmeter后&#xff0c;其会出现一个倍感亲切的中文界面。但由于jmeter本身的汉化工作做得不好&#xff0c;你会看到有未被汉化的选项及元件的参数。…

脸书营销,跨境电商不能忽视的营销新趋势

Facebook营销就是在Facebook上通过有针对性的广告、商业品牌群组等地方推广您的业务。随着社交媒体继续成为我们日常生活中不可或缺的一部分&#xff0c;Facebook产品在社交商务方面的扩展使该平台成为吸引新客户的重要渠道。 您应该注意到&#xff0c;很多企业都通过Facebook…

3个能免费使用的AI绘画软件,效果精致

通过AI绘画软件&#xff0c;设计小白也能轻松创作出精美的图画创作。本文将为大家介绍3款能免费使用的AI绘画软件&#xff0c;它们能帮助设计小白或者经验丰富的设计师快速设计出精美的图画作品&#xff0c;一起来看看吧&#xff01; 1、即时灵感 即时灵感是国产的AI绘画软件…

【复习45-51题】【每天40分钟,我们一起用50天刷完 (剑指Offer)】第三十七天 37/50

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

VRRP技术和浮动路由(第二十六课)

VRRP技术和浮动路由(第二十六课) 一、浮动路由 1、浮动路由概述 1&#xff09;浮动路由是什么 -浮动路由又称为路由备份&#xff0c;由两条或多条链路组成浮动路由 -浮动路由指配置两条静态路由&#xff0c;这两条静态路由的目的地址相同&#xff0c;但是下一跳地址不同两…

CPM-Bee-5B微调实记

CPM-Bee-5B微调实记 1. 准备工作 &#xff08;1&#xff09;用PyCharm打开CPM-Bee文件夹。 &#xff08;2&#xff09;设置成自己的环境&#xff1a;nlp &#xff08;3&#xff09;将src文件夹在终端打开 &#xff08;4&#xff09;激活自己的环境&#xff1a;nlp 2. 准备…

web自动化测试进阶篇05 ——— 界面交互场景测试

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

数据结构day6(2023.7.20)

一、Xmind整理&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;个栈的入栈次序ABCDE&#xff0c;则栈的不可能的输出序列是&#xff08;D&#xff09; A.ABCDE B.DECBA C.EDCBA D.DCEAB 栈的特点是先进后出&#xff0c;后进先出&#xf…

Electron 系统通知 Notification 实践指南

系统通知是桌面应用的常见功能&#xff0c;用于给用户发送提醒&#xff08;刷下存在感 &#x1f642;&#xff09;&#xff0c;还能帮定点击事件以便后续的操作。 Electron 自带通知模块&#xff0c;下方代码是一个简单的示例 const { Notification } require(electron)cons…

Postman学习之常用断言

什么是断言&#xff1f; 断言——就是结果中的特定属性或值与预期做对比&#xff0c;如果一致&#xff0c;则用例通过&#xff0c;如果不一致&#xff0c;断言失败&#xff0c;用例失败。断言&#xff0c;是一个完整测试用例所不可或缺的一部分&#xff0c;没有断言的测试用例…

SZ:zip/内部函数外部函数/VGG模型/nn

zip&#xff1a; -r recursion-d delete-m move (move隐藏的意思是&#xff0c;原文件会消失&#xff09;想增加文件&#xff0c;不需要加参数什么参数也没有。如果zip压缩文件不存在&#xff0c;执行以上命令将会创建一个新的zip文件并将指定的文件添加进去。如果zip压缩文件…