【C语言】深入理解指针(进阶篇)

一、数组名的理解

数组名就是地址,而且是数组首元素的地址。

任务:运行以下代码,看数组名是否是地址。

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("&arr[0] = %p\n", &arr[0]);
	printf("arr     = %p\n", arr);
	return 0;
}

输出结果:

运行以上代码,我们发现数组名和数组首元素的地址打印的结果一模一样,数组名就是数组首元素(第一个元素)的地址

疑问:数组名如果就是首元素的地址,那么下面的代码证明理解?

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%d\n", sizeof(arr));
	return 0;
}

 输出结果:40,如果arr是数组首元素的地址,那么输出的应该是4/8才对。

其实数组名就是数组首元素(第一个元素)的地址是对的,但是有两个例外:

  • sizeof(数组名):sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
  • &数组名:这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)

除此之外,任何地方使用数组名,数组名都表示首元素的地址。

要了解arr和&arr有什么区别,请看以下代码:

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("&arr[0]   = %p\n", &arr);
	printf("&arr[0]+1 = %p\n", &arr+1);

	printf("arr       = %p\n", arr);
	printf("arr+1     = %p\n", arr+1);

	printf("&arr       = %p\n", &arr);
	printf("&arr+1     = %p\n", &arr+1);
	return 0;
}

 输出结果:

这里我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,这是因为&arr[0]和arr都是首元素的地址,+1就是跳过一个元素。

都是&arr和&arr+1相差40个字节,这里是因为&arr是整个数组的地址,+1操作就是跳过整个数组的。

到这里大家应该搞清楚数组名的意义了吧。

数组名是数组首元素的地址,但是有两个例外( sizeof(arr)和&arr )。

二、使用指针访问数组

有了前面的知识支持,再结合数组的特点,我们可以很方便的使用指针访问数组了。

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = arr;
	//输入
	for (i = 0; i < sz; i++)
	{
		scanf("%d", p + i);//p+i 是地址
	}
	//输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//*(p+1) 解引用操作符(*),是数值
	}
	return 0;
}

 这个代码搞明白了,我们再试一下,如果我们再分析一下,数组名arr是数组首元素的地址,可以赋值给p,其实数组名arr和p在这里是等价的。那我们可以使用arr[i]访问数组元素,那p[i]是否也可以访问数组呢?

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = arr;
	//输入
	for (i = 0; i < sz; i++)
	{
		scanf("%d", p + i);
		//scanf("%d", arr + i);//也可以写成这样
	}
	//输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
	return 0;
}

在第17行的地方,将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i]等价于*(p+i)

因为数组名arr和p是等价的,所以arr[i]等价于*(arr+i)。数组元素的访问在编译器的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。

三、一维数组传参的本质

我们知道数组是可以传递给函数的,这里我们讨论一下数组传参的本质。

首先从一个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给一个函数后,在函数内部求数组的元素个数吗?

#include <stdio.h>
void test(int arr[])
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz2 = %d\n", sz2);
}
int main()
{
	int arr[10] = { 0 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;
}

 输出结果:

 在函数内部求数组的元素个数,输出的结果是1,并没有正确获得元素个数。

这就要学习数组传参的本质了,第一节数组名的理解,我们知道:数组名就是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说数组传参本质上传递的是数组首元素的地址

 所以函数形参的部分理论上应该是用指针变量来接收首元素的地址。那么在函数内部写sizeof(arr)计算的是一个地址的大小(单位字节),而不是数组的大小(单位字节)。正是因为函数的参数部分本质是指针,所以在函数内部是没有办法求数组元素个数的

#include <stdio.h>
void test2(int arr[])
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz2 = %d\n", sz2);
}
void test3(int *p)
{
	int sz3 = sizeof(p) / sizeof(p[0]);
	printf("sz3 = %d\n", sz3);
}
int main()
{
	int arr[10] = { 0 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test2(arr);
	test3(arr);
	return 0;
}

 总结:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式

四、冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较。

方法一:

#include <stdio.h>
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;
			}
		}
	}
}
int main()
{
	int arr[10] = { 3,1,7,5,8,9,0,2,4,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//冒泡排序
	bubble_sort(arr,sz);
	int i = 0;
	//打印输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

方法二:优化

#include <stdio.h>

void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;//假设这⼀趟已经有序了
		int j = 0;
		for (j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				flag = 0;//发⽣交换就说明,⽆序
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
		if (flag == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了
			break;
	}
}
int main()
{
	int arr[10] = { 3,1,7,5,8,9,0,2,4,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//冒泡排序
	bubble_sort(arr, sz);
	int i = 0;
	//打印输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

五、二级指针

5.1 指针的定义

一级指针:是一个指针变量,指向一个普通变量,并保存该普通变量的地址

二级指针:是一个指针变量,指向一个一级指针,并保存该一级指针的地址

 二级指针是一个指向指针的指针变量。它存储了一个指针的地址,该指针又指向另一个变量的地址。

二级指针画图

 5.2 引入二级指针

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int* pa = &a;
	int** ppa = &pa;

	//一次解引用*ppa,此时类型int*
	*ppa = &b;
	//二次解引用**ppa,此时类型int
	**ppa = 200;

	return 0;
}

逻辑关系如下:

a是一个int类型的变量,一级指针pa指向a,并保存a的地址。

二级指针变量ppa指向一级指针pa,并保存pa的地址

二级指针ppa解引用操作:

  • 一次解引用:*ppa的类型变成 int*(代表一级指针pa)间接改变了pa的指向,从a的地址变成了b的地址。
  • 二次解引用:ppa的类型变成了 int (代表变量b),此时ppa = 200;(等价于b=200)。

(1)下面举个例子:

#include <stdio.h>
int main()
{
	//普通变量
	int a1 = 1;
	int a2 = 1;
	int a3 = 1;

	//一级指针
	int* p1 = &a1;
	int* p2 = &a2;
	int* p3 = &a3;

	//二级指针
	int** s = &p1;
}

 (假设a1,a2.a3空间连续,p1,p2,p3空间连续)逻辑图如下:

表达式移动字节数/值的变化类型
s+1sizeof(int*)*1 =  4*1  =  4int**
*s+1sizeof(int)*1  =  4*1  =  4int*
**s+1a1+1  =  1+1  =  2int

分析:

  • s+1 表示二级指针s指向了p2,移动的字节数需要根据指向的数据的空间大小进行计算sizeof(int*)*1 = 4字节,此时s+1还是二级指针,所以类型是int*。
  • *s+1 先对s进行一次解引用为*s,相当于操控一级指针p1,然后*s+1,相当于p1指向了a2的地址,所以移动sizeof(int)*1 = 4字节,此时的类型为int*。
  • **s+1 先对s进行二次解引用为**s,相当于操控变量a1,然后a1加1,所以a1=2;a1的类型是int。

总结:在对二级指针变量s的移动时,s都会将已经保存的一级指针的类型进行解析步长(s+sizeof(p)*n);而一级指针*s(相当于p一级指针变量)会以保存的变量的类型进行解析步长(*s+sizeof(a)*n)。

 六、指针数组

指针数组是多个指针变量,以数组的形式存储在内存中,数组中的每个元素都是一个地址(指针),占有多个指针存储空间。指针数组即存放指针的数组

指针数组的声明方式为:数据类型 *数组名[数组长度]。

 6.1 指针数组模拟二维数组

#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*,可以存放在parr数组中
	int* parr[3] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for(j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
		return 0;
	}
}

parr数组的画图演示

 parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。

上述代码模拟出的二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的

七、数组指针变量

7.1 数组指针变量是什么

上一节我们学习了指针数组,指针数组是一种数组,数组中存放的是地址(指针)。

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

任务: int (*p)[5] = { },如何理解?

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

 这里()和[ ]优先级相同,根据结合律,从左到右运算

()里是*p,p先和*结合,先定义了指针,说明p是一个指针变量,然后指向的是一个大小为5个整数的数组。所以p是一个指针,指向一个数组,叫数组指针。

int (*p)[5]的画图

 7.2 数组指针变量怎么初始化

数组指针变量是依赖存放数组地址的,那么怎么获取数组的地址呢?这就要用到&数组名

 如果要存放数组的地址,就得存放在数组指针变量中,如下:

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

 我们调试可以看到&arr和p的类型是完全一致的。

数组指针类型解析:

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

 7.3 二维数组传参的本质

有了数组指针的理解,我们来讲解一下二维数组传参的本质。

过去我们有一个二维数组,徐亚传参给一个函数的时候,我们是这样写的:

#include <stdio.h>
void test(int arr[3][5],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");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	test(arr, 3, 5);
	return 0;
}

这里实参是二维数组,形参也写成二维数组的形式,那还有什么其他写法吗?

这里我们再次理解一下二维数组,二维数组起始可以看做是每个元素是一维数组的数组,也就是二维数组的每个元素是一个一维数组。那么二维数组的首元素就是第一行,是一维数组。

arr数组

 所以,根据数组名是数组首元素的地址这个规则,二维数组的数组名表示的就是第一行的地址,是一维数组的地址。根据上面的例子,第一行的一维数组的类型就是int[5],所以第一行的地址的类型就是数组指针类型iny(*)[5]。那就意味着二维数组传参本质上也是传递地址,传递的是第一行这个一维数组的地址。那么形参也是可以写成指针形式的。如下:

#include <stdio.h>
void test(int (*p)[5], 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));
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	test(arr, 3, 5);
	return 0;
}

总结:二维数组传参,形参的部分可以写成数组,也可以写成指针形式。

八、函数指针变量

8.1 函数指针变量的创建

函数指针即定义一个指向函数的指针变量。

 定义格式如下:

int (*p)(int x, int  y);  //注意:这里的括号不能掉

 这个函数的类型是有两个整型参数,返回值是整型。

思考:函数是否有地址?

让我们看看以下代码:

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

输出结果如下:

 观察发现,确实打印出来了地址,所以函数是有地址的,函数名技术函数的地址,当然也可以通过&函数名的方法获取函数的地址。

如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针变量非常类似。如下:

void test()
{
	printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;


int Add(int x, int y)
{
	return x + y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以

 函数指针类型解析:

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


int (*) (int x, int y)     //pf3函数指针变量的类型

 8.2 函数指针变量的使用

通过函数指针调用指针指向的函数。

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int, int) = Add;

	printf("%d\n", (*pf)(2, 3));
	printf("%d\n", pf(3,5));
	return 0;
}

输出结果:

九、函数指针数组

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

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

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

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

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

十、转移表

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

举例:计算机的一般实现

#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(" 0:exit \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;
}

使用函数指针数组的实现 

#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;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf(" 0:exit \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输⼊有误\n");
		}
	} while (input);
	return 0;
}

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

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

相关文章

[Java安全入门]三.CC1链

1.前言 Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库&#xff0c;它提供了很多强大的数据结构类型和实现了各种集合工具类。Commons Collections触发反序列化漏洞构造的链叫做cc链&#xff0c;构造方式多种&#xff0c;这里先学习cc1链…

Dubbo-记录

1.概念 Apache Dubbo 是一款 RPC 服务开发框架&#xff0c;用于解决微服务架构下的服务治理与通信问题&#xff0c;官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力&#xff0c; 利用 Dubbo 提供的丰富服务治理…

C语言--函数指针变量和函数指针数组的区别(详解)

函数指针变量 函数指针变量的作用 函数指针变量是指向函数的指针&#xff0c;它可以用来存储函数的地址&#xff0c;并且可以通过该指针调用相应的函数。函数指针变量的作用主要有以下几个方面&#xff1a; 回调函数&#xff1a;函数指针变量可以作为参数传递给其他函数&…

【数仓】通过Flume+kafka采集日志数据存储到Hadoop

相关文章 【数仓】基本概念、知识普及、核心技术【数仓】数据分层概念以及相关逻辑【数仓】Hadoop软件安装及使用&#xff08;集群配置&#xff09;【数仓】Hadoop集群配置常用参数说明【数仓】zookeeper软件安装及集群配置【数仓】kafka软件安装及集群配置【数仓】flume软件安…

【深度学习笔记】6_10 双向循环神经网络bi-rnn

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 6.10 双向循环神经网络 之前介绍的循环神经网络模型都是假设当前时间步是由前面的较早时间步的序列决定的&#xff0c;因此它们都将信…

【Leetcode打卡】递归回溯

【Leetcode打卡】递归回溯 784. 字母大小写全排列 class Solution { public:int find(string s,int pos){int ipos;while(i<s.size()){if(isalpha(s[i])){return i;}i;}return -1;}void turn(string& s,int pos){if(islower(s[pos])){s[pos]toupper(s[pos]);}else{s[po…

ChatGPT提示词工程:prompt和chatbot

ChatGPT Prompt Engineering for Developers 本文是 https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/ 这门课程的学习笔记。 ChatGPT提示词工程&#xff1a;prompt和chatbot 文章目录 ChatGPT Prompt Engineering for DevelopersWhat …

Linux下使用open3d进行点云可视化(.bin文件)

整个场景可视化&#xff1a; import numpy as np import open3d as o3ddef read_kitti_bin_point_cloud(bin_file):# 加载.bin文件point_cloud_np np.fromfile(bin_file, dtypenp.float32).reshape(-1, 4)# 仅使用X, Y, Z坐标&#xff0c;忽略反射率point_cloud_o3d o3d.geo…

代码训练LeetCode(6)编辑距离

代码训练(6)LeetCode之编辑距离 Author: Once Day Date: 2024年3月9日 漫漫长路&#xff0c;才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 72. 编辑距离 - 力扣&#xff08;LeetCode&#xff09;力扣 (LeetCode) 全球极客挚爱的技…

44岁「台偶一哥」成现实版「王子变青蛙」,育一子一女成人生赢家

电影《周处除三害》近日热度极高&#xff0c;男主角阮经天被大赞演技出色&#xff0c;最让人意想不到&#xff0c;因为该片在内地票房报捷&#xff0c;很多人走去恭喜另一位台湾男艺人明道&#xff0c;皆因二人出道时外貌神似&#xff0c;至今仍有不少人将两人搞混。 多年过去&…

RNN预测正弦时间点

import torch.nn as nn import torch import numpy as np import matplotlib matplotlib.use(TkAgg) from matplotlib import pyplot as plt # net nn.RNN(100,10) #100个单词&#xff0c;每个单词10个维度 # print(net._parameters.keys()) #序列时间点预测num_time_steps 50…

什么是RabbitMQ的死信队列

RabbitMQ的死信队列&#xff0c;是一种用于处理消息&#xff0c;处理失败或无法路由的消息的机制。它允许将无法被正常消费的消息重新路由到另一个队列&#xff0c;以便稍后进行进一步的处理&#xff0c;分析或排查问题。 当消息队列里面的消息出现以下几种情况时&#xff0c;就…

ChatGPT 控制机器人的基本框架

过去的一年&#xff0c;OpenAI的chatGPT将自然语言的大型语言模型&#xff08;LLM&#xff09;推向了公众的视野&#xff0c;人工智能AI如一夜春风吹遍了巴黎&#xff0c;全世界都为AI而疯狂。 OpenAI ChatGPT是一个使用人类反馈进行微调的预训练生成文本模型。不像以前的模型主…

每日一练:LeeCode-209、长度最小的子数组【滑动窗口+双指针】

每日一练&#xff1a;LeeCode-209、长度最小的子数组【滑动窗口双指针】 思路暴⼒解法滑动窗口 本文是力扣 每日一练&#xff1a;LeeCode-209、长度最小的子数组【滑动窗口双指针】 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐 L…

《剑指 Offer》专项突破版 - 面试题 76 : 数组中第 k 大的数字(C++ 实现)

目录 详解快速排序 面试题 76 : 数组中第 k 大的数字 详解快速排序 快速排序是一种非常高效的算法&#xff0c;从其名字可以看出这种排序算法最大的特点是快。当表现良好时&#xff0c;快速排序的速度比其他主要对手&#xff08;如归并排序&#xff09;快 2 ~ 3 倍。 快速排…

力扣---腐烂的橘子

题目&#xff1a; bfs思路&#xff1a; 感觉bfs还是很容易想到的&#xff0c;首先定义一个双端队列&#xff08;队列也是可以的~&#xff09;&#xff0c;如果值为2&#xff0c;则入队列&#xff0c;我这里将队列中的元素定义为pair<int,int>。第一个int记录在数组中的位…

嵌入式驱动学习第二周——使用perf进行性能优化

前言 这篇博客来聊一聊如何使用perf进行性能优化。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可以关注本博主并订阅本专栏&#xff0c;一起讨论一起学习。现在关注就是老粉啦&#xff01; 目录 前言…

考研复习C语言初阶(4)+标记和BFS展开的扫雷游戏

目录 1. 一维数组的创建和初始化。 1.1 数组的创建 1.2 数组的初始化 1.3 一维数组的使用 1.4 一维数组在内存中的存储 2. 二维数组的创建和初始化 2.1 二维数组的创建 2.2 二维数组的初始化 2.3 二维数组的使用 2.4 二维数组在内存中的存储 3. 数组越界 4. 冒泡…

HarmonyOS NEXT应用开发之MpChart图表实现案例

介绍 MpChart是一个包含各种类型图表的图表库&#xff0c;主要用于业务数据汇总&#xff0c;例如销售数据走势图&#xff0c;股价走势图等场景中使用&#xff0c;方便开发者快速实现图表UI。本示例主要介绍如何使用三方库MpChart实现柱状图UI效果。如堆叠数据类型显示&#xf…

FPGA的配置状态字寄存器Status Register

目录 简介 状态字定义 Unknown Device/Many Unknow Devices 解决办法 一般原因 简介 Xilinx的FPGA有多种配置接口&#xff0c;如SPI&#xff0c;BPI&#xff0c;SeletMAP&#xff0c;Serial&#xff0c;JTAG等&#xff1b;如果从时钟发送者的角度分&#xff0c;还可以…