C语言进阶之笔试题详解(2)

前言

这里的内容包括二维数组笔试题和指针笔试题,供给读者对这部分知识进行加深和巩固。

158c3f50b199454985017a51dbef9841.png               ✨ 猪巴戒:个人主页✨

               所属专栏:《C语言进阶》

        🎈跟着猪巴戒,一起学习C语言🎈

目录

前言

笔试题

二维数组

题目

解析:

一维数组

二维数组

总结:

指针笔试题

题目1

解析:

题目2

解析:

总结:

题目3

解析

题目4

解析:

题目5

解析: 

总结:

题目6:

解析:

题目7: 

解析:

总结:

题目8:

解析:

最后


笔试题

二维数组

题目

以下代码的打印结果是什么?

int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0][0]));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a[0]+1));
	printf("%d\n", sizeof(*(a[0] + 1)));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(*(a + 1)));
	printf("%d\n", sizeof(&a[0] + 1));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a[3]));

	return 0;
}

解析:

一维数组

一维数组的数组名表示的是首元素的地址,通过数组名加下标的形式可以访问数组的每个元素。

arr[1]这里的1就是元素的下标,arr[1]访问的是数组的第二个元素。

二维数组

arr[3][4]是一个二维数组,二维数组可以看作是3个一维数组组成,每个一维数组就是二维数组的一个元素,因此二维数组的首元素其实是第一行的所有元素。

二维数组的数组名arr,表示的是第一行的地址,也就是第一个一维数组的地址。一共有多少行就有多少个一维数组。

arr[0]其实就是第一个一维数组的数组名。

arr[1]就是第二个一维数组的数组名。

arr[2]就是第三个一维数组的数组名。

一共是3行,就是3个一维数组。

想要访问二维数组的每个元素:一维数组的数组名加下标,arr[0][0],访问的是第一行第一列的元素。

449142bd904342adb372616a9323ed0a.png

1|printf (" %d\n ", sizeof ( a ) )  48

这里a是数组名,sizeof(数组名),就代表整个数组的大小,数组一共12个元素,每个元素4个字节,整个数组的大小就是48个字节。

2|printf (" %d\n ", sizeof ( a[0][0] ) )  4

a[0][0]就是第一行第一列的元素,一个整形元素,就是4个字节的大小。

3|printf (" %d\n ", sizeof ( a[0] ) )   16

a[0]就是第一个一维数组,a[0]就是第一个一维数组的数组名,sizeof(数组名)表示的就是整个数组的大小,这个一维数组一共有4个元素,每个元素的大小是4个字节。

4|printf (" %d\n ", sizeof ( a[0]+1 ) )  4 / 8

a[0]就是第一个一维数组的数组名,因为a[0]并不是单独放在sizeof内部的,所以这里的a[0]表示的就是首元素的地址,a[0]是一维数组,首元素地址就是第一行第一列的元素的地址。那a[0]+1表示的就是第一行第二个的元素的地址,地址为4个字节或者8个字节。

5|printf (" %d\n ", sizeof ( *( a[0] + 1) ) )  4

在4|中,我们就分析了a[0]+1是第一行第二个元素的地址,现在把它解引用。*(a[0]+1)得到的就是第一行第二个元素,一个整形元素的大小是4个字节。

6|printf (" %d\n ", sizeof ( a + 1 ) )  4 / 8

a是一个二维数组,a不是单独放在sizeof内部,a表示的就是首元素的地址,而二维数组首元素的地址表示的是第一个一维数组的地址,也就是第一行的地址,a+1表示的就是第二个一维数组的地址,也就是第二行的地址,即使是一维数组的地址,大小也是4个字节或者8个字节。

7|printf (" %d\n ", sizeof ( *(a + 1) ) )  16

在6|中,我们分析到a+1表示的就是第二个一维数组的地址,也就是第二行的地址,这里对第二个一维数组进行解引用操作,*(a+1)得到的就是第二个一维数组的元素,一共有4个元素,每个元素的大小是4个字节。

8|printf (" %d\n ", sizeof ( &a[0] + 1 ) )  4 / 8

&a[0],&数组名表示就是取整个数组的地址,a[0]就是第一个一维数组,取的就是第一行的地址,,因为这个地址的类型是一行的地址,&a[0]+1跳过一行,得到的就是第二行的地址。地址的大小是4个字节或者8个字节。

9|printf (" %d\n ", sizeof ( *a ) )  16

a是一个二维数组,a并没有单独放在sizeof的内部,a表示的就是首元素的地址,二维数组首元素的地址,就是第一行的地址。*a对第一行的地址解引用得到的就是第一行的所有元素,一个4个元素,每个元素的大小是4个字节。

10|printf (" %d\n ", sizeof ( a[3] ) )   16

sizeof()只关心内部类型的大小,如果有这个二维数组第4行,同样是是4个元素,sizeof(数组名),依旧是向后访问4个元素,每个元素是整形的大小,所以是16个字节,sizeof不会真的去访问,程序会返回16。


总结:

数组名的意义:

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

2.&数组名,这里的数组名表示整个数组,去除掉是整个数组的地址。

3.除此之外所以的数组名都表示首元素的地址。

4.二维数组可以看作有多个一维数组组成,一维数组可以看作二维数组的每个元素,行数代表制二维数组的意思个数,列数代表一维数组的元素个数。


指针笔试题

题目1

int main()
{
    int a[5] = {1,2,3,4,5};
    int* ptr = (int*)(&a+1);
    
    printf("%d\n",*(a+1),*(ptr-1));
    return 0;
}

解析:

*(a+1):  2

a是数组名,表示的是首元素的地址,一个元素的地址,+1就跳过一个元素,所以(a+1)表示就是第二个元素的地址,解引用得到的就是第二个元素,等于2.

db48ce2b09304072b2f0f6f0df08f191.png

 *(ptr-1):  5

&a,a是数组名,&a取的是整个数组的地址,+1跳过整个数组,指向的位置:

735a314caa0a42739dfe248ea6169ab3.png

将(&a+1)强制类型转化为(int*),就是由数组指针转成整形指针的类型, 原来是数组的地址,所以是数组指针,现在是整形指针的地址,就意味着,+1或-1的操作只能跳过一个整形元素的大小。

ptr-1跳过4个字节,位置如下图所示,对ptr-1解引用就得到5.

495143f5b89d4a63a9682e247a4c872e.png


题目2

假设p的值为0x100000.如下表表达式的值分别为多少?

已知,结构体Test类型的变量大小是20个字节

struct Test
{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}*p;

int main()
{
    printf("%p\n",p+0x1);
    printf("%p\n",(unsigned long)p +0x1);
    printf("%p\n",(unsigned int*)p +0x1);
    return 0;
}

解析:

1.    printf (" %p\n ", p+0x1 )

p是结构体指针,结构体变量的大小是20个字节,

指针+1,跳过一个指针变量的大小,0x100000这是16进制,我们加上20个字节

十进制中的20转化为十六进制就是14.

结果为0x100014.

2.    printf (" %p\n ", (unsigned long)p +0x1 )

将p一个结构体指针强制类型转化为unsigned long类型,也就是整数,那结果就为

0x100001

因为指针+1-1需要考虑指针类型,整数直接加减就可以了

3.    printf (" %p\n ", (unsigned int*)p + 0x1)

p是结构体指针,将p强制类型转化为unsigned int* 类型,无符号整形指针,整形指针+1就会跳过一个整形的大小,也就是4个字节。结果:

0x100004


总结:

这道题考察的是

指针+1-1的操作,什么类型的指针就会跳过一个多大的元素。

比如整形指针+1就会跳过4个字节,字符指针+1就会跳过1个字节。

-1也类比如此。


题目3

int main()
{
	int a[4] = { 1,2,3,4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

解析

1.    int* ptr1 = (int*)(&a + 1)

&a+1,&数组名,取的是整个数组的地址,所以加1就会跳过整个数组。

(int*)(&a+1),将(&a+1)强制类型转化成整形指针,ptr1就是一个整形指针

ptr1[-1],ptr1[-1]其实就等同于*(ptr1-1),ptr1是整形指针,所以-1跳过一个整形元素,4个字节。

ptr1[-1]的打印结果:

0x00 00 00 04 打印会省略前面的0,所以结果为4

%x是打印十六进制的数字

7af11eabfee04ea49ef60eb75d1fc6f3.png

5544c9edcc2144958cd1c9ce7fdcc884.png

 2.    int* ptr2 = (int*) ( ( int ) a + 1)  0x02 00 00 00

a是数组名,表示首元素的地址

(int) a 对首元素的地址强制类型转化为(int)

(int)a+1,a已经强制类型转化为整数,+1直接在a的地址加1就可以了。

(int*) ( ( int ) a + 1),强制类型转化会整形指针,那整形指针ptr2指向的就是a+1指向的后面4个字节。

msvc采用的是小端存储模式,所以读出来的要再次进行倒置,结果:

0x02 00 00 00

%x是打印十六进制

c5c7ff19ead143ab89b4b64ea8d0fd7c.png


题目4

#include <stdio.h>
int main()
{
	int arr[3][2] = { (0,1),(2,3),(4,5) };
	int* p;
	p = arr[0];
	printf("%d", p[0]);

	return 0;
}

解析:

如果我们想要对一个三行二列的数组进行初始化,我们可以用这种形式。

int arr[3][2] = { {0,1}, {2,3}, {4,5} };

07b08596205643e38deef172d55ace25.png

但是用()就不是这个初始化了,这是逗号表达式,逗号表达式取最后的一位,依次取,剩余不够就会初始化为0。

int arr[3][2] = { (0,1),(2,3),(4,5) };
//实际上
int arr[3][2] = {   1  ,  3  ,  5   };

2fd41f3a0b174644a6ba2b8b0cff041f.png

arr[0]是第一行的数组名,arr[0]表示首元素的地址,a[0][0]的地址,&a[0][0],第一行的地址和第一个元素的地址表示形式是一样的

int*p,p是整形指针, 所以存放的是arr[0][0]的地址

p[0] -> *(p+0) -> *p

结果:1


题目5

int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}

解析: 

a是每行有5个元素,一共5行的二维数组。

p是数组指针,一个指向4个整形的数组。

p = a, 数组名a表示首元素的地址,即第一行的地址,地址的表示形式就是第一个元素的地址,p只有4个元素,但是地址的位置传给了p。

&p[4][2]-->*(*(p+4)+2),p是一个4个整形元素的数组指针,所以p+1就跳过4个字节,

*(p+4)就是一个4个元素的整形数组的数组名,表示这个数组首元素的地址,即整形指针,整形指针+2,跳过2个整形元素,实际上是8个字节。

&a[4][2],找到数组a第5行第3列的元素地址位置

指针-指针,指针之间相减,等于指针之间的元素个数。

&p[4][2] - &a[4][2],由于是低地址-高地址,结果为:-4 

2df109d7d46e43b1b9d45a0dc6b08718.png

%p的打印结果

%p是打印地址,地址是没有负数概念的,所以这里的-4是将其存放在内存中的补码打印出来,-4的补码是:11111111111111111111111111111100 

将它变成十六进制,0xFFFF FFFC

10eb87872aa94816bb8e4609063a6cee.png

%d的打印结果

-4


总结:

1.指针相减得到的是指针之间的元素个数。

题目6:

int main()
{
	int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	int* ptr1 = (int*)(&aa + 1);
	int* ptr2 = (int*)(*(aa + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

解析:

*(ptr1-1):  10

&aa取的是二维数组的起始地址,&aa+1就会跳过整个二维数组。

然后传给整形指针ptr1,整形指针ptr1-1,向前跳过一个元素。

ptr1-1指向的数字就是10

5b03b17c2e8146c4b31ce55ced6ff20e.png

 *(ptr2-1):  5

aa为首元素的地址,即第一行的地址,aa+1跳过一个一维数组,就是第二行的地址

*(aa+1)对第二行的地址解引用得到的就是第二行的数组名,数组名表示首元素的地址,ptr2表示的是6的地址。

ptr2-1,ptr2是整形指针,-1向前跳过1个元素,得到的就是5的地址。

*(ptr2-1),解引用得到5.

题目7: 

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

解析:

pa是一个char**类型,一个二级字符指针,指向的是a,a表示数字首元素的地址,就是“work”的地址。

因为pa是char**类型,所以pa+1,跳过一个char*元素,pa++也就是跳到“at”。

对pa解引用,得到的结果:

at

247a4779f3df4897bb011eccb80515c1.png


总结:

 1.整形指针int*,+1-1跳过一个整形元素

2.二级字符指针char**,+1-1跳过一个char*类型的元素。


题目8:

int main()
{
	char* c[] = { "ENTER","NEW","POITER","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;

	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);

	return 0;
}

解析:

e01456759e90464f9ca84dcf59ab6aa4.png

1.     printf (" %s\n ", **++cpp)

cpp = cp,cpp指向的是数组cp的首元素,就是c+3.

++cpp,cpp指向的位置发生了改变,cpp指向c+2

*++cpp,解引用后,得到的就是c+2.

**++cpp,再次解引用,得到的就是c+2所指向的"POINT".

  打印结果:POINT

65d62babadf84cd68b5db0ef38d4044f.png

2.    printf (" %s\n ", *-- * ++cpp + 3)

上一轮的操作中,cpp所指向的位置已经发生了改变,cpp指向的对象是c+2

++cpp,cpp所指向的位置改变,指向c+1.

*++cpp,解引用,就是c+1

--*++cpp,前置减减,得到的c.

*--*++cpp,得到c所指向的对象,“ENTER”

*--*++cpp+3,这个*--*++cpp得到的是c所指向的对象,即字符串首个字符的地址,就是字符指针。

字符指针+3,跳过3个字符,得到的结果是:

“ER”

在字符数组中的字符串存放的其实是这个常量字符串首个字符的地址,

47ab00d2559e47e68518fe53dd075d89.png

3.    printf (" %s\n ", *cpp[-2] + 3)

经过上一轮,cpp所指向的对象变成了cp数组的第三个元素,第三个元素本来是c+1,但是--之后变成了c。

cpp[-2],等同于*(cpp-2),cpp向前跳过2个元素,指向的对象变成了c+3,然后解引用得到c+3.

*cpp[-2],对c+2进行解引用,c+3所指向的对象是“FIRST”,得到“FIRST”.得到的“FIRST”其实是F的地址,是一个字符指针

*cpp[-2]+3,字符指针+3,跳过3个字符,得到的结果就是“ST”.

7e45326ee7734f04979dbe33b0841efc.png

4.    printf (" %s\n ", cpp[-1][-1] + 1)

cpp指向cp的第三个元素,第三个元素已经变成了c。

cpp[-1][-1],等同于*(*(cpp-1)-1)

cpp-1,cpp指向的是cp第三个元素,向前移动一位,得到c+2的地址.

*(cpp-1),对c+2的地址解引用,就会得到c+2.

*(cpp-1)-1,c+2-1,得到的就是c+1。

*(*(cpp-1)-1),对c+1进行解引用,得到的就是c所指向的对象,“NEW”.“NEW”得到的是N的地址,是一个字符指针。

以上对cpp[-1][-1]的解析,

cpp[-1][-1] + 1,我们已经得到“ENTER”,也就是字符指针,字符指针+1-1跳过一个字符,+1跳过N,得到的结果是“EW”.

e9c7a741f6f640efbc142f91fc7da7d5.png

最后

 如果你能够走到这里,那么恭喜你,请给自己比个大拇指吧!

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

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

相关文章

借助文档控件Aspose.Words,在 C# 中比较两个 PDF 文件

在当今的数字世界中&#xff0c;管理和比较文档是一项至关重要的任务&#xff0c;尤其是在商业和法律领域。在 C# 中处理 PDF 文档时&#xff0c;Aspose.Words for .NET 提供了用于比较 PDF 文档的强大解决方案。在这篇博文中&#xff0c;我们将探讨如何在 C# 应用程序中比较 P…

MySQL进阶-读写分离

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、语雀 、Github &#x1f389;公众号&#xff1a;猫十二懿 一、MySQL 读写分离介绍 读写分离,简单地说是把对数据库的读和写操作分开&#xff0c;以对应不同的数据库服务器。主数据库提供写操作&…

从零开始的c语言日记day38——数组参数,指针参数

一维数组传参 要把数组或者指针传给函数&#xff0c;那函数参数如何设计&#xff1f; 上面各写法有问题嘛&#xff1f; 第一个没问题 第二个没问题 第三个没问题 第四个没问题 第五个解析&#xff1a;定义int*arr2[20]为20个int*类型的数组&#xff0c;test2之后用的是ar…

Kubernetes(K8s)资源管理-03

资源管理 资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 kubernetes的本质上就是一个集群系统&#xff0c;用户可以在集群中部署各种服务&#xff0c;所谓的部署服务&#xff0c;其实就是在kubernetes集…

NoSQL大数据存储技术思考题及参考答案

思考题及参考答案 第1章 绪论 1. NoSQL和关系型数据库在设计目标上有何主要区别&#xff1f; (1)关系数据库 优势&#xff1a;以完善的关系代数理论作为基础&#xff0c;具有数据模型、完整性约束和事务的强一致性等特点&#xff0c;借助索引机制可以实现高效的查询&#xf…

Clickhouse Join

ClickHouse中的Hash Join, Parallel Hash Join, Grace Hash Join https://www.cnblogs.com/abclife/p/17579883.html 总结 本文描述并比较了ClickHouse中基于内存哈希表的3种连接算法。 哈希连接算法速度快&#xff0c;是最通用的算法&#xff0c;支持所有连接类型和严格性设…

TCP/IP封装

数据如何通过网络发送&#xff1f;为什么 OSI 模型需要这么多层&#xff1f; 下图显示了数据在网络传输时如何封装和解封装。 步骤1&#xff1a;当设备A通过HTTP协议通过网络向设备B发送数据时&#xff0c;首先在应用层添加HTTP头。 步骤2&#xff1a;然后将TCP或UDP标头添加…

Hadoop入门学习笔记

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 这里写目录标题 一、VMware准备Linux虚拟机1.1. VMware安装Linux虚拟机1.1.1. 修改虚拟机子网IP和网关1.1.2. 安装…

Modbus TCP工业RFID读写器的选型要点

Modbus TCP工业RFID读写器是一种采用Modbus TCP通信协议的RFID读写器。它可以通过TCP/IP网络与计算机或其它设备进行通信&#xff0c;实现远程读取和写入RFID标签数据的目的。 与传统的RFID读写器相比&#xff0c;Modbus TCP工业RFID读写器具有更远的读写距离、更高的读写灵敏度…

uniapp如何与原生应用进行混合开发?

目录 前言 1.集成Uniapp 2.与原生应用进行通信 3.实现原生功能 4.使用原生UI组件 结论: 前言 随着移动应用市场的不断发展&#xff0c;使用原生开发的应用已经不能满足用户的需求&#xff0c;而混合开发成为了越来越流行的选择。其中&#xff0c;Uniapp作为一种跨平台的开…

系统设计概念:生产 Web 应用的架构

在你使用的每个完美应用程序背后&#xff0c;都有一整套的架构、测试、监控和安全措施。今天&#xff0c;让我们来看看一个生产就绪应用程序的非常高层次的架构。 CI/CD 管道 我们的第一个关键领域是持续集成和持续部署——CI/CD 管道。 这确保我们的代码从存储库经过一系列测试…

开发知识点-Maven包管理工具

Maven包管理工具 SpringBootSpringSecuritydubbo图书电商后台实战-环境设置&#xff08;JDK8, STS, Maven, Spring IO, Springboot&#xff09;点餐小程序Java版本的选择和maven仓库的配置视频管理系统&&使用maven-tomcat7插件运行web工程SpringTool suite——maven项目…

promis.all的异步使用

基础 参考 https://blog.csdn.net/qq_52855464/article/details/125376557 简单来说 Promise.all是处理接口返回方法异步的&#xff0c;能够使得接口的获取顺序得到控制&#xff0c;不会出现数据为空的情况 使用 先执行jianshigetGroups->groupIds-> const promises2 …

RNN-T Training,RNN-T模型训练详解——语音信号处理学习(三)(选修三)

参考文献&#xff1a; Speech Recognition (option) - RNN-T Training哔哩哔哩bilibili 2020 年 3月 新番 李宏毅 人类语言处理 独家笔记 Alignment Train - 8 - 知乎 (zhihu.com) 本次省略所有引用论文 目录 一、如何将 Alignment 概率加和 对齐方式概率如何计算 概率加和计…

什么是PDM图纸管理系统?PDM图纸管理系统主要功能有哪些?

PDM (Product Data Management) 图纸管理系统 是用于管理企业内部图纸和相关文件的软件系统。它提供了一个集中存储、组织和跟踪图纸和文件的平台&#xff0c;以确保团队成员能够轻松访问、共享和更新所需的工程设计和制造数据。 彩虹PDM系统|PDM产品数据管理系统|BOM管理|工艺…

代码浅析DLIO(三)---子图构建

0. 简介 我们刚刚了解过DLIO的整个流程&#xff0c;我们发现相比于Point-LIO而言&#xff0c;这个方法更适合我们去学习理解&#xff0c;同时官方给出的结果来看DLIO的结果明显好于现在的主流方法&#xff0c;当然指的一提的是&#xff0c;这个DLIO是必须需要六轴IMU的&#x…

问题记录-maven依赖升级或替换(简单版)

问题背景 项目被检测到有高危漏洞&#xff0c;需要对部分jar进行升级。以一个jar为例记录一下升级过程。 1 找到高危漏洞的包 如果装了maven helper插件则可以在下面查看当前模块依赖包 2 使用maven命令 执行下面这个命令&#xff0c;会将当前项目的信息打印出来&#xff0c;…

基于MYSQL+Tomcat+Eclipse开发的超市订单管理系统

基于MYSQLTomcatEclipse开发的超市订单管理系统 项目介绍&#x1f481;&#x1f3fb; 该系统运行需要基于JDK7来进行运行 超市订单管理系统是一款针对超市订单进行管理的软件&#xff0c;旨在提高订单处理效率&#xff0c;降低管理成本。该系统包括以下功能模块&#xff1a; 系…

Java后端开发——SpringMVC商品管理程序

Java后端开发——SpringMVC商品管理程序 今日目标 Spring MVC框架介绍掌握SpringMVC的核心类的原理及配置掌握SpringMVC的常用注解掌握SpringMVC的增删改查编程 Spring MVC框架介绍 Spring MVC&#xff08;Model-View-Controller&#xff09;是一个基于Java的开源框架&#x…

记一次:Python的学习笔记一

前言&#xff1a;之前学习的Python笔记&#xff0c;已经过去很多年了&#xff0c;不久前重新翻了出来练习练习。不完善的地方在缝缝补补 一、环境搭建 1.1、Python的win环境安装 0、python-3.12.0软件安装&#xff1a;Win11环境搭建python-3.12.0-amd64 这里小小注意一下&a…