【C语言】深入理解指针

目录

1.字符指针

2.指针数组

3.数组指针

4.数组传参与指针传参

一维数组传参

二维数组传参

一级指针传参

二级指针传参

5.函数指针

6.函数指针数组

7.指向函数指针数组的指针(了解即可)

8.回调函数

回调函数的应用:库函数qsort

模拟实现库函数qsort


1.字符指针

允许用字符串来初始化字符指针

char*p="abcdef"这个语句是正确的,他表示把后面字符串首元素地址放到指针变量p里面去。其中abcdef是一个常量字符串

看下面这个代码

刚上来的两句是创建了两个字符数组,并用hello bit来初始化他们,这两个数组的创建必然要申请两块不同的内存空间,而打印的条件是str1==str2,而数组名是数组首元素地址,所以不满足if的条件,因此会打印str1 and str2 are not same。

然后是用常量字符串初始化字符指针的操作,对于常量字符串hello bit来讲,他存放在内存的代码段,他的地址是固定的,用他的地址来初始化str3和str4,因此str3和str4里面存放的都是常量字符串hello bit的首元素地址,因此打印的是str3 and str4 are same。

2.指针数组

指针数组顾名思义就是用来存放指针的数组,本质上还是一个数组,用来存放一组类型相同的指针。

比如这样一个代码

他们在内存中存放的形式如图

如果拿到了arr[0],也就是arr数组的第一个元素,里面存的是arr1,他指向了arr1数组,也就是说arr[0]就是arr1,arr1[0]表示arr1数组的首元素,又因为arr1就是arr[0]所以arr[0][0]就是arr1的首元素,这看起来像一个二维数组,同理arr[1][0]就是arr2数组的首元素。

虽然形式上跟二维数组一样,但是指针数组跟二维数组还是有很大区别的,二维数组在创建的时候在内存中被分配的空间是连续的,而这个指针数组再创建的时候就是存了三个指针,这个指针指向了三个数组,这三个数组并不一定连续。也就是说对于一个三行五列的二维数组(假设数组名为arr)来说,arr[0][4],arr[1][0]的地址是连续的,但是对于上面的指针数组来说arr[0][4]是arr1的第五个元素,而arr[1][0]是arr2的首元素,由于arr1和arr2在向内存申请空间的时候都是随机申请的,大概率不连续,因此这个指针数组arr的arr[0][4]和arr[1][0]也是大概率不连续的。

3.数组指针

类比一下我们熟悉的其他类型指针,比如int *p,*代表p是一个指针,int代表p指向的数据是int类型的,数组指针也类似,比如int *p[10],*代表p是一个指针,去掉*p之后剩下的int [10]是其指向数据的类型,这是一个数组类型。因此p就叫做数组指针变量,里面存放的是一个数组的地址。p的类型我们写作int(*)[10]。

在对数组指针变量进行初始化的时候,应该写成 int(*p)[10]=&arr;

可以这样理解,首先得有个指针变量p,然后他前面得有个*表示他是一个指针,此时如果没有(),p就会和[]结合,因此我们加上括号,让p和*结合代表他是一个指针。指向的元素是int [10]类型的,存放的内容是arr的地址。同时印证了这个数组指针的类型是int(*)[10],因为去掉指针变量的名字,就是他的类型。

数组指针的使用

先来看一个不合适的使用方法

说我们要打印这个数组的每个元素,这里代码使用了数组指针的方式,首先&arr代表取出整个数组的地址(arr在一般情况下表示数组首元素地址,但是有两个例外,一个是arr单独放在sizeof内部,另一个数&arr,这两种情况下arr代表的是整个数组),既然是一个数组的地址,我们就要放在数组指针里面,因此我们创建了一个int(*)[10]类型的指针变量p,用来存放arr的地址,然后我们要打印arr中的每个元素,肯定要对p进行解引用,*p拿到了arr(可以理解成&和*抵消了),要想打印arr中的每个元素,我们现在知道了arr的首元素地址(因为这里拿到arr是通过解引用,并不属于上面的两种特殊情况,因此这里arr就是数组首元素地址,是int*类型的),想要打印,直接就*(arr+i)即可。也就是说打印的时候应该*((*p)+i),非常的别扭。因为我们想要打印整个数组的内容,直接使用一个int*类型的指针即可,这里却使用了数组指针显然是不合理的,那么数组指针到底应该怎么用?

这里直接给出答案:数组指针在访问二维数组的时候比较方便。

假如有这样一个二维数组arr

当然这只是为了方便理解才画成这样,实际上因为二维数组中每个元素在内存中是连续存放的,他们应该排成一条线。

二维数组可以理解成里面有三个一维数组,也就是说二维数组是一维数组的数组。而二维数组的数组名也代表首元素的地址,首元素又是一个一维数组,因此二维数组的数组名是一个一维数组的地址,我们如果想要把他存起来,应该使用一个数组指针。那么下面这个代码就可以实现二维数组元素的打印

因为arr包含三个一维数组,每个一维数组是四个元素,因此arr的数组名也就是首元素地址是一个int [4]类型的数组的地址,类型是int(*)[4],因此在形参接收的时候应该写int(*p)[4]。

首先p是int(*)[4]类型的数组指针,+1就会跳过一个int [4]类型的数组,p里面存放的是arr首元素的地址,也就是第一个一维数组的地址,因此*p就拿到了第一个数组,相当于拿到了第一个数组的数组名,也就是说*p是int*类型的,+1就会跳过一个int类型。

*p+i用来访问所有行,*((*p+i)+j)就能访问所有元素了,或者说(*p+i)[j]也可以,他们是等效的。

当然也可以直接形参直接使用二维数组来接收

实际上形参写成数组形式只是语法允许,让我们能够直观的知道接收的是一个数组,本质上地址就是要用指针来接收,即使写成第二种写法,编译器在编译的时候也会编译成我们第一种写法,在为形参开辟空间的时候也是为数组指针开辟一块空间。

4.数组传参与指针传参

一维数组传参

下面哪些传参方式是正确的?

先看test函数的传参,实参是arr,也就是一个int类型数组的数组名,表示首元素的地址,应该用一个int*类型的指针来接收,因此形参写成int*arr是对的,又因为C语言语法允许数组传参的时候用数组接收,因此int arr[]和int arr[10]作为形参也是可以的,因为实参传的只是一个地址,形参部分是不会真正去创建一个数组的,但是语法规定可以用数组来作为形参接收一个数组名,我们甚至可以把形参写成int arr[1000],也是对的,编译器都会按照形参是int *类型的来理解。

再来看test2的传参,arr2是一个指针数组,他的数组名是这个指针数组首元素的地址,也就是一个指针的地址,即二级指针,实参传了二级指针,形参要拿一个二级指针变量来接收,因此形参应该是int **类型的,当然语法允许用和实参代表的数组同类型的数组作为形参来接收,但是本质上形参还是一个指针。

因此上面所有传参均正确。

二维数组传参

test函数实参是一个二维数组的数组名,他代表二维数组arr首元素的地址,又因为二维数组arr是由3个一维数组构成的,每个一维数组又有五个int类型的元素,因此arr的首元素是第一个一维数组的地址,存放他应该用一个数组指针,因此实参应该写成int(*arr)[5],表示arr是一个指针,指向的数组类型是int[5]类型的数组,也就是我们所谓的二维数组里面的一维数组,当然数组名作为实参传递,语法上允许用与arr代表的数组类型相同的数组来作为形参接收,这是为了方便理解,但是系统是不会为形参这个数组去开辟内存空间的,编译的时候仍然会按照数组指针的形式开辟空间。

二维数组在创建的时候可以省略行但是不能省略列,因此正确的形参创建形式有int arr[3][5],int arr[][5],int(*arr)[5]。

注:除去数组名单独放在sizeof括号里还有&数组名这两种情况,二维数组的数组名一定是一个数组指针。

一级指针传参

对于指针传参来说,如果实参是一个一级指针,形参就用同类型的指针来接收即可,二级指针同理,这非常的简单,因此对于指针传参,我们来讨论一下如果我们知道了形参是一个一级指针,实参可能传递的是什么。

可以接收

1.char a=0; test2(&a) 一个char类型变量的地址

2.char ch="abc"; test2(ch);一个char[]类型数组的数组名

3.char a=0;char* p=&a; test2(p);一个char*类型的指针变量

二级指针传参

如果形参已经确定是char**p,可能接收的是什么实参?

可以接收

1.char a=0;char *p=&a;test(&p);一个char*类型指针的地址

2.char a=0;char *p=&a;int**pp=&p;test(pp);一个二级指针

3.char* arr[10];test(arr);一个char*[10]类型数组的数组名

注:数组传参,形参可以用数组形式接收,也可以用指针形式接收,但是指针传参,形参指针写成指针形式。

5.函数指针

函数指针顾名思义就是指向函数的指针,里面存放的是一个函数的地址

来看一段代码

居然是一样的,这说明函数名和&函数名代表的意思都是函数的地址

既然函数有地址,我们就可以把他存到指针变量里面去,这个指针变量就是函数指针,在初始化的时候我们写作void (*p)(),也就是函数的返回值类型以及形参类型决定了他的函数指针是什么样的类型。比如有一个函数

int add(int x,int y),用来存放他的函数指针在初始化的时候就写作

int(*p)(int,int),去掉指针的名字,就是他的类型,因此这个函数指针的类型就是int(*)(int,int)

再比如有一个函数void test(charpc,int arr[10]),初始化他的函数指针就写作void (*p)(char*,int [10]),p的类型就是void(*)(char*,int [10]),10可以省略,也可以写成int *

有了函数指针,我们就可以对他进行解引用来调用函数。如果pf里存放了函数add的地址,那么add(3,5)和(*p)(3,5)就是等效的。实际上*都多余了,因为我们函数名和&函数名其实是一回事,我们可以测试一下

因此我们既然把&add赋给了pf,那就相当于把add赋给了pf,也就是说pf就是add,因此我们直接pf(3,5)也是可以正常调用add函数的

来看两个有趣的代码

首先我们看到最后有个小括号,这肯定代表函数调用,然后左边最外层的括号应该就是就是为了分割开,这样我们就从内部的0开始入手,0前面有一个括号,括号里面是一个类型,这表示把0强制类型转换成函数指针的类型,那么0作为函数指针就代表了一个地址,再对他进行解引用就能调用0地址处的函数。之所以最右边调用的小括号里面啥也没有,是因为这个函数不需要任何参数。这个代码应用场景可能就是我们知道了0地址处有一个函数,想要调用他。

这个代码看起来无从下手,因为他好像也并没有函数调用,也没有强制类型转换,如果我提前告诉你,这是一个函数名为signal函数的声明,你有没有头绪了呢?

解释一下,我们就从signal开始入手,因为这肯定是一个函数名,那么他后面的括号就是他的参数类型,分别是int和void(*)(int),也即一个int类型和一个函数指针类型,那么知道了函数名,函数的参数类型,把他们都去掉,不就是他的返回类型吗?故signal的返回类型是void(*)(int),他的返回值也是函数指针类型。因此上面这段代码是signal的声明。

这个代码之所以难以理解,就是因为这个函数指针的类型写起来非常具有迷惑性,如果把这个函数指针类型进行类型重定义成一个简单的类型,这个代码阅读起来就会非常的轻松。那首先来介绍一下typedef的使用方法,如果是把unsigned int类型重命名为uint,就写作typedef unsigned int uint,如果是把int 类型重命名为ptr_t,就写作typedef int ptr_t

但是如果要把函数指针 int(*)(int)重命名为pint,就不能写tepedef int(*)(int) pint,而应该写成typedef int(*pint)(int)

要把函数指针void(*)(int)重命名为pf_t,就写作typedef void(*pf_t)(int),这样就可以把上面的代码简化为pf_t signal(int,pf_t),这样读起来就舒服多了,一眼就看出这是一个函数的声明。

6.函数指针数组

顾名思义就是这个数组中的每个元素都是函数指针类型。

假如我们要写一个计算器,于是我们就写了下面四个函数

我们发现这些函数的参数和返回值都已相同类型的,那我们就可以把这些函数放到一个数组里面去,怎么放函数?只能放他的函数名吧,那我们又说函数名其实是函数的地址,因此我们放的其实是一些函数的地址,也就是函数指针,当然要用一个函数指针数组来存放,那么如何初始化这个数组?类比int arr[4]={0};这表示arr数组里面能放4个int类型的元素

那么我们想要一个能够放四个int(*)(int,int)类型变量的数组,就初始化成int(*)(int,int) arr[4]是不是?这样理解可以,但是语法规定我们要把arr[4]写到*的旁边,也就是说我们应该这样初始化函数指针数组

注:指针变量在创建的时候,指针变量一定是在*的旁边的。

int(*p)(int ,int),*表示p是个指针,去掉*和p就是指针指向的元素类型是

int(int,int),说明指向的元素是函数类型,单独去掉变量名就是p的类型,也就是说p是int(*)(int,int)类型。

int(*arr[4])(int,int),那么对于简单的计算器这个代码的实现,就可以这样写(计算器只针对整数)

7.指向函数指针数组的指针(了解即可)

假如arr是一个函数指针数组,我们现在&arr,放到指针p里面,则p就是指向函数指针数组的指针,他的类型是什么呢?

首先来看函数指针数组的类型应该怎么写。int(*[4])(int,int),是函数指针数组的类型,现在p要指向他,应该写成int(*(*p)[4])(int,int)=&arr,其中p旁边的那个*表示p是一个指针,去掉p,就是p的类型,所以p的类型是int(**arr[4])(int,int),类型去掉p和他旁边的*,就是p指向的元素的类型,因此p指向的元素类型是int(*[4])(int,int),也即一个函数指针类型。

8.回调函数

函数指针有一个非常重要的用途就是实现回调函数。回调函数就是通过函数指针调用的函数。比如前面的计算器,除了使用函数指针数组来实现,还可以使用函数指针来实现。

其中calc函数就通过函数指针回调了加减乘除的函数,此时的add,sub,mul,div就是回调函数,通过函数指针调用谁,谁就是回调函数

回调函数的应用:库函数qsort

qsort的头文件是stdlib.h

他的四个参数分别是要进行排序的数组base的首地址,base数组的元素个数,每个元素的大小,以及一个函数指针,这个函数指针指向了一个函数,这个函数的参数是两个void*类型的指针,返回类型是int,要求这个函数能够比较参数(这个函数的参数是两个指针)指向的两个元素的大小,规定如果elem1指向的元素比elem2指向的元素大,那这个函数就返回一个大于零的数,反之就返回一个小于零的数,如果elem1和elem2指向的元素一样大,就返回0。

void*类型的指针可以接收任何类型的地址,但是不能直接解引用,由于我们不知道未来要比较的两个函数是什么类型的,那就只能写成void*类型,就可以接收任何类型了,在解引用之前强制类型转换即可。

qsort默认是以升序排列的(如果要降序排列,只需要比较大小的函数返回值上用相反的逻辑即可,比如elem1指向的元素如果比elem2大,就返回一个小于零的数),并且可以排列任何类型的数组。来看一个例子

模拟实现库函数qsort

下面我们将使用回调函数模拟实现qsort,由于目前没有学习快速排序,因此使用冒泡排序代替。

模拟实现,我们就把参数设置成和库函数qsort一样,最后一个函数指针指向的是一个能比较两个元素大小的函数,需要使用者自己编写。

我们排序使用的是冒泡排序的思想,就是比较相邻的两个元素,如果前面比后面大,就交换,那现在的问题是当时冒泡排序是针对整形数组的,比较就是直接base[j]和base[j+1]进行比较即可,但是现在我们想要这个my_sort类型能够对任意类型的数据进行排序,就不能简单的写成base[j]和base[j+1],因为我们连base的类型都不知道,当然也不知道+1会跳过几个字节,也就不知道+1是不是拿到了和base[j]相邻的元素。那么我们干脆一个字节一个字节的操作,因为不管什么类型,大小至少也是一个字节,那如何操作一个字节?很简单,使用char类型的指针即可。因此不管base代表什么类型的数组,我们都先把base强制类型转换成char*类型,那么base[j]其实就是(char*)base+j*size,与他相邻的元素就是(char*)base+(j+1)*size。

这样我们就拿到了要比较的两个元素,但是这两个元素如何比较?直接使用大于号显然是不合理的,这时候就用到了我们的第四个参数,cmp函数,我们需要自己写一个能够比较两个元素大小的函数,并且规定如果前者大,就返回一个大于0的数,反之就返回一个小于0的数,如果二者相等,就返回0。

通过判断cmp函数的返回值,我们就知道了需不需要交换这两个元素。那问题又来了,如何交换?直接创建临时变量吗?那临时变量是什么类型的呢?但是要交换的数据是什么类型,他们最小也有一个字节,那么我们直接一个字节一个字节的交换就行,什么时候才算交换结束呢?当然是交换完size个字节后,就结束了。如图是swap函数的交换

这样我们的my_sort函数就可以正常使用了,比如我们要排序的是一个整形数组,我们就要去写一个能够比较两个整形数据的函数,比如这样写

如果我们要比较两个结构体类型的数据呢?

假如结构体类型是这样的

如何比较两个struct stu类型变量的大小?

由于上面的结构体类型含有两个成员变量,那么要比较struct sut类型变量的大小,就可以按照name的大小来进行比较,也可以按照age的大小进行比较,当然如果按照name的大小来进行比较,就不能直接相减了,因为字符串的比较是用库函数strcmp,这个库函数的返回值逻辑与我们的cmp函数一致,也是前者大就返回大于零的数,后者大就返回小于零的数,一样大就返回0,因此我们如果按照name的大小来比较的话,直接返回strcmp的值就可以,如图

注:由于空指针是无法进行解引用操作的,因此我们在写cmp函数的时候都需要进行强制类型转换,要比较的是什么类型的,就强制类型转换成对应的指针。

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

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

相关文章

Adb显示第3方应用的包名原理

Android早期版本实现原理请看 Android源码分析-pm命令的实现,列出包名pm list package,列出系统库pm list libraries_pm list packages-CSDN博客 Android12 对adb shell pm 实现原理做了重构:改成了template模式PackageManagerShellCommand …

【C生万物】数组

📚博客主页:爱敲代码的小杨. ✨专栏:《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 ❤️感谢大家点赞👍🏻收藏⭐评论✍🏻,您的三连就是我持续更新的动力❤️ 🙏小杨水平有…

OJ_浮点数加法(高精度运算)

题干 C实现 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<map> #include<string> using namespace std;string GetInteger(string a) {return a.substr(0, a.find(.)); }string GetFraction(string a) {return a.substr(a.find(.) 1 ,a.siz…

【webpack】技巧使用

webpack和TypeScript 安装webpack相关内容安装TS相关内容配置初始化数据初始化运行展示和目录展示报错解决&#xff08;缺失文件配置&#xff09; 安装前端必备神奇lodash测试一下entry配置index.html模板配置修改打包出来的index.html的titleinject注入chunks 属性多页面配置 …

2.7日学习打卡----初学RabbitMQ(二)

2.7日学习打卡 JMS 由于MQ产品很多&#xff0c;操作方式各有不同&#xff0c;于是JAVA提供了一套规则 ——JMS&#xff0c;用于操作消息中间件。JMS即Java消息服务 &#xff08;JavaMessage Service&#xff09;应用程序接口&#xff0c;是一个Java平台中关于面 向消息中间件的…

springboot174基于springboot的疾病防控综合系统的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

vscode wsl远程连接 权限问题

问题描述&#xff1a;执行命令时遇到Operation not permitted 和 Permission denied问题&#xff0c;是有关ip地址和创建文件的权限问题&#xff0c;参考网络上更改wsl.conf文件等方法均无法解决&#xff0c;只能加sudo来解决

【MySQL进阶之路】磁盘随机读写和顺序读写对MySQL性能的影响

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

前后端通讯:前端调用后端接口的五种方式,优劣势和场景

Hi&#xff0c;我是贝格前端工场&#xff0c;专注前端开发8年了&#xff0c;前端始终绕不开的一个话题就是如何和后端交换数据&#xff08;通讯&#xff09;&#xff0c;本文先从最基础的通讯方式讲起。 一、什么是前后端通讯 前后端通讯&#xff08;Frontend-Backend Commun…

解析十六进制雷达数据格式:解析雷达数据长度。

以Cat62格式雷达数据为例&#xff0c;十六进制雷达数据部分代码&#xff1a; 3e0120bf7da4ffee0085 雷达数据长度使用4个字符&#xff08;2个字节&#xff09;标识&#xff0c;在这里是“0120”&#xff0c;转换为十进制数为288。 雷达数据长度父类&#xff1a; base_length_…

python 基础知识点(蓝桥杯python科目个人复习计划35)

今日复习计划&#xff1a;阶段总结&#xff08;新年贺礼&#xff09; 1.python简介&#xff08;定义&#xff0c;优点&#xff0c;缺点&#xff0c;应用领域&#xff09; python&#xff1a;一种广泛使用的解释型&#xff0c;高级和通用的编程语言 python极简&#xff0c;生…

Xray 工具笔记

Xray 官方文档 扫描单个url&#xff08;非爬虫&#xff09; 并输出文件&#xff08;不同文件类型&#xff09; .\xray.exe webscan --url 10.0.0.6:8080 --text-output result.txt --json-output result.json --html-output report.html默认启动所以内置插件 &#xff0c;指定…

前端JavaScript篇之实现call、apply 及 bind 函数

目录 实现call、apply 及 bind 函数1. 实现call函数2. 实现apply函数3. 实现bind函数 实现call、apply 及 bind 函数 call、apply和bind函数都是用于改变函数中this指向的方法。它们的作用都是使函数能够在不同的对象上下文中运行。call方法和apply方法的作用类似&#xff0c;…

多元回归分析:理论与应用

多元回归分析是一种统计方法&#xff0c;用于研究两个或多个自变量&#xff08;解释变量&#xff09;与一个因变量&#xff08;响应变量&#xff09;之间的关系。这种分析允许研究者评估多个因素对结果变量的影响&#xff0c;是社会科学、经济学、生物医学和工程等多个领域中常…

【doghead】uv_loop_t的创建及线程执行

worker测试程序,类似mediasoup对uv的使用,是one loop per thread 。创建一个UVLoop 就可以创建一个uv_loop_t Transport 创建一个: 试验配置创建一个: UvLoop 封装了libuv的uv_loop_t ,作为共享指针提供 对uv_loop_t 创建并初始化

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Toggle组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Toggle组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Toggle组件 组件提供勾选框样式、状态按钮样式及开关样式。 子组件 仅当Toggl…

【MySQL】数据库基础 -- 详解

一、什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 一般的文件确实提供了数据的存储功能&#xff0c;但是文件并没有提供非常好的数据&#xff08;内容&#xff09;的管理能力&#xff08;用户角度&#xff09;。 文件保存数据有以下几个缺点&…

无心剑中译佚名《春回大地》

The Coming of Spring 春回大地 I am coming, little maiden, With the pleasant sunshine laden, With the honey for the bee, With the blossom for the tree. 我来啦&#xff0c;小姑娘 满载着欣悦的阳光 蜂儿有蜜酿 树儿有花绽放 Every little stream is bright, All …

Windows 安装 Linux子系统,并为子系统设置图形化界面

安装WSL 在控制面板中打开下面的选项&#xff1a; 执行下面的命令&#xff0c;更新到WSL2版本&#xff0c;并在以后创建子系统的时候默认采用WSL2的版本&#xff1a; wsl --update wsl --set-default-version 2在Window上安装连接工具&#xff1a; 在Window上下载VcXsrv&…

备战蓝桥杯---动态规划之经典背包问题

看题&#xff1a; 我们令f[i][j]为前i个物品放满容量为j的背包的最大价值。 f[i][j]max(f[i-1][j],f[i-1][j-c[i]]w[i]); 我们开始全副成负无穷。f[0][0]0;最后循环最后一行求max; 负无穷&#xff1a;0xc0c0c0c0;正无穷&#xff1a;0x3f3f3f3f 下面是v12,n6的图示&#xff…