linux基础IO——动静态库——实现与应用学习、原理深入详解

news2024/11/12 17:31:39

        前言:本节内容是基础IO部分的动静态库。 本节内容, 我们将站在实现者的角度上自己实现一下动静态库, 并且会站在使用者的角度上使用我们自己实现的库。过程中牵扯到许多新的知识, 最后我们会重谈一下我们的进程。 理解一下有了动静态库的进程到底是怎么样的。现在, 开始我们的学习。

        ps:本节内容了解进程地址空间,页表即可观看。

目录

什么是动静态库?

静态库的实现与应用

静态库的实现

errno

ldd

如何建立默认搜索路径

静态库的使用

动态库的实现与应用

动态库的实现

动态库的使用

动态库是如何被加载的


什么是动静态库?

        要制作无论是静态库还是动态库, 首先我们得知道什么是库?那么, 什么是库呢?

        其实, 库就是由一些.o文件打包而成的一堆二进制文件, 这就叫库。而其中构成库的.o文件其实就是一些各种各样的函数方法。比如我们写的printf, scanf等等都是一些方法, 这些方法是保存在某些.c文件中的, 但是这些.c文件后来被编译成为了.o文件。 由这些.o文件按照某种指令进行打包成为一个库文件, 这就叫做库。 ——也就是说, 库文件里面包含了许多可复用的, 已经写好的代码, 可以被我们使用。

        我们的库有两种, 一种叫做静态库, 一种叫做动态库。 动态库一般以.so为结尾, 静态库一般以.a为结尾。 

什么是库暂且先提这么多, 具体的细节我们下面的内容中再说。      

静态库的实现与应用

静态库的实现

        由上面的知识点我们知道, 库是由.c文件生成.o文件,这些.o文件再打包而成的。

        上面这张图是我们自己实现的加减乘除的函数, 需要注意的是除法函数要判断一下除零错误, 然后我们设计一个myerrno来表示错误码, 用来标记错误信息。 

        把我们的源代码想办法打包成库, 然后我们提供库 + .h。 但是这里有一个问题——我们可不可以不提供.h文件呢?答案是不可以,为什么? 因为我们的.h文件里面是一份我们定义的方法的说明书。而且我们的库里面是一群二进制, 我们直接看是看不懂的!!!

 静态库形成方法

        如上图, 我们先创建makefile, 然后在makefile输入上面的代码——第一行说明要生成叫做libmymath.a的静态库(静态库的后缀为.a), 第三行就是说明我们生成库依赖的文件(也就是mymath.o), 第四行就是我们要将mymath.c生成mymath.o。

        这里我们再重新了解一下什么是静态库? 

        那么假如有下面几个原文件

        其中, main.c是我们自己写的, a.c, b.c, c.c, d.c是别人的。 如果别人直接将源代码给我们, 我们直接将这五个文件编译成.o文件, 然后链接就行了!!

        那么, 了解了这个之后再理解静态库就非常的简单,因为静态库是什么?——上面的a.o, b.o, c.o, d.o打个包, 这个包就是所有的源代码的.o文件的集合,未来我们进行编译的时候, 只需要将main.c变成main.o, 然后再和我们的这个包进行链接, 就是程序了。 而这个包, 就叫做静态库!!!

        然后我们再回到生成静态库的代码中,这里有个打包命令, 叫做ar

        这个ar -rc中, ar是生成静态库的一个命令, 就是说将我们所有的.o打包成一个包。 然后-r就是replace的意思, -c就是creat的意思。 这两个连起来的意思就是说假如有这么一串命令:ar -rc librarymath.a.add.o, sub.o——那么这串命令的意思就变成了将后面两个.o文件放到.a的库里面。 而且, 如果这个.a文件不存在就创建, 如果有内容就进行替换。        

        这里补充好之后就是下图:

        注意将clean要将.o, .a文件全部清理。 

写好makefile之后, 我们make一下, 得到的结果如下图:

        这里就生成了mymath.o、libmymath.a——这里其实就是先生成mymath.o, 然后将mymath.o打包生成libmymath.a

        那么, 现在我的库如果想要给别人使用, 想要发布出去, 怎么发布呢?——这里我们还要使用一下makefile

        首先打开我们的makefile, 在makefile里面创建一个PHONY, 名字叫做output, 同时, 我们在lib下创建一个include, 还有一个mymathlib然后将所有的头文件拷贝到include里面, 将所有的库文件拷贝到mymathlib里面。 未来,我们的所有的自己写的静态库, 就能够将.h文件放到include里, 静态库放到mymathlib里面。

        同时, 我们的clean, 删除也要跟着变一下, 要加上-r选项——目的是为了能够删除目录

        然后执行make;make output后就能发布静态库了:

        但是, 问题来了, 我们今天如果像下面这样写着, 包含了我们自己写的库的头文件mymath.h, 那么我们要如何才能编译这个程序呢?

        我们如果直接编译, 很显然, 会发生错误, 如下图就发生了链接错误:

        所以, 这里正确的编译指令不能直接使用gcc编译程序。 而正确的编译指令就是——gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -lmymath.

        这里现在来讲解这条指令是什么意思。 

        首先-I的意思是指定包含的头文件的搜索路径, 也就是我们的mymath.h会在上图的./lib/inclde中去找。 然后-L的意思就是指定静态库的搜索路径。 但是后面也要跟一个-I + 静态库名称, 来指定使用哪个静态库(这个静态库的名称要去掉前缀和后缀。就比如上图中静态库本来是libmymath.a, 现在变成了mymath)。

        但是我们知道, 我们在写c, c++的时候可没有这么麻烦, 只需要gcc/g++ 后面跟文件名就行了。 而这上面又是-I, 又是-L, 又是-l是为什么呢?

        因为c, c++的头文件, 默认就会处在系统的默认搜索路径中,默认能被找到 c, c++的库, 同样的处在默认的系统的搜索路径中, 并且c, c++默认就能够认识对应的c, c++的动静态库, 所以也就不需要指定

        那么我们也想要像c, c++的库一样, 不想像上面那么麻烦怎么办呢?——那么我们就可以将我们的头文件拷贝到系统的include。 将静态库, 安装到lib64里面。 (/usr/bin/include, 以及lib64分别是头文件和库的默认搜索路径。)但是这两个是系统目录, 需要使用root权限才行!!!

        这一块的内容很多, 所以这里先不谈, 我们先谈两个小知识点——errno与 ldd

errno

        看一下errno——为了区分我们的库函数有没有被正常使用, 一般我们的数据库都会创建一个全局变量, 就如同这里的errno。 

         这个errno怎么用呢?——这里假如我们写一个除零错误的函数:

  

ps:这里博主前面的除零错误函数写错了, 写到这里才看到, 改正一下:

我们运行一下上面的main.c程序:

        我们可以看到错误码的错误标识, 这个错误标识我们还可以标识为1, 2, 3等等。 用来标识不同的错误信息。

ldd

ldd能够查看可执行程序连接的动态库。 

        为什么只能看到动态库, 而看不到我们自己写的mymath静态库呢? ——这是因为gcc默认就是动态链接,但是我们在链接的时候, 我们的lib里面没有动态库, 只有静态库, 所以我们只能静态链接。  

        另外, 未来我们链接库的时候, 如果我们什么都没有带, 默认就是动态链接。 如果我们想要静态链接, 就要加上static。(注意, 如果系统中需要链接多个库的话, gcc也可以链接多个库。只需要将需要连接的库依次往指令后面跟即可)

如何建立默认搜索路径

        谈完上面的两个小知识点之后, 我们就来谈我们上面所提到的——如何像使用c/c++默认库一样不带有冗长的指令选项呢?

        上面已经说过——我们只需要将我们的mymath.h, libmymath.a放到系统的默认路径下面!!!

        注意, 系统的默认目录处于usr还有lib里面, 而这两个是系统级别目录, 需要使用root权限。(下面执行指令时已经变为root账户)

        实行指令之后就可以看到我们已经把文件拷贝进去了!

         上面的这两条指令, 其实就是库的安装!!——可能有点友友会问, 这就安装好了?——是的, 安装其实并没有这么高大上, 我们下载一个库, 一定要有头文件, 一定要有库文件。而库的安装,本质上就是我们将头文件, 库文件安装在系统目录里面。我们一般的情况下, 其实安装的本质就是拷贝, 将程序拷贝到我们的系统目录下(但是不排除有些程序安装需要改变环境变量等等)

静态库的使用

        首先我们知道头文件我们安装到了默认的路径下面。 所以-I后面的头文件搜索路径就可以不要了。 并且,对于静态库来说, 我们将静态库安装到了默认路径下面,那么-L后面的静态库搜索路径就可以不要了,但是-l后面的指定静态库我们仍需要有。这是因为我们必须要知道.o文件要链接那些静态库。如果不指定静态库, 仍旧会发生报错:

        我们指定准确的静态库之后, 就可以编译通过了:

        那么问题来了, 我们自己编写的库一般不要放到系统目录下面, 因为我们写的可能不太好(如果大佬写的很好的话请忽略). 所以我们这个时候我们就不要将库直接放到系统目录下面, 而是使用另一种方法——软链接

        这里先删除之前我们安装的库

        如下图, 红色框框是我们想要连接的头文件所在的目录, 蓝色框框是软链接文件的路径:

        如下图, 红色框框是我们想要链接的静态库, 蓝色框框是将软链接文件放到的路径:

         建立好了上面的东西之后, 注意, 我们的连接的头文件目录连接的是myincl->include,也就是说,我们如果包含的头文件是mymath.h, 那么系统不会触发myincl的软链接也就找不到mymath.h所以我们这里要包含头文件要写成:myincl/mymath.h。

此时编译就又能通过了:

动态库的实现与应用

动态库的实现

动态库我们再新建两组库, 一个mylog.c和头文件, 一个是myprintf.c和头文件。

        在myprintf.h和mylog.h里面都写上#pragma once以及包含stdio头文件。 

然后写上如下内容

         

myprintf.c写上下面第一张图的内容, mylog,c写上下面第二张图的内容

        链接动态库要怎么链接呢?我们知道, 我们的文件只有到.o文件才会链接库 所以, 生成动态库, 一定也是先将文件变成.o文件, 再生成动态库。 只不过, 生成动态库比生成静态库多了一些选项而已。

        这里我们先生成两个.o文件, 要想生成动态库, 必须要生成可以生成动态库的.o文件。 

        然后再生成动态库, 注意:静态库生成要用的打包指令ar, 动态库不需要, 因为动态库算是默认的, 生成库的时候默认生成动态库, 使用库的时候默认使用动态库。 只有当指定静态库或者没有动态库的时候才会使用静态库。 

        动态库的后缀使用.so, 并且生成动态库需要使用-shared标识生成库文件。 

        生成动态库和生成可执行文件一样, 我们要使用-o选项标识生成可执行程序, 生成的文件后缀是.so。 然后我们并不是真的生成可执行程序, 所以加上-shared选项标识生成动态库。  

        然后, 为什么生成的动态库是一个绿色的可执行文件呢? (我们的静态库是不可执行的, 是白色的。)这是因为我们的程序在运行的时候, 静态库不会被加载到内存中, 而动态库联系着我们的程序, 要加载到内存中。 ——也就是说, 我们要如何理解可执行权限?那么什么叫做可执行权限呢?其实就是当前文件是否会以可执行程序的形式加载到内存中这就叫做可执行权限——而动态库, 就使用有着可执行权限(虽然动态库没有main函数, 但是有对应的方法, 也就是说动态库不是不能执行, 而是不能自己单独执行。)

现在来看一下生成两个库的makefile怎么写:

下面是我们的makefile

        保存退出后我们make;make output就能生成如下的目录了, 然后我们以后给别人提供库, 这里面就有三个头文件,可以查看文件的方法。 然后提供两个库, 一个动态, 一个静态。 根据方法的使用说明进行使用这两个库就行了。 

动态库的使用

        现在, 使用一下这两个库,先创建一个文件夹, 然后将我们的mylib文件夹放到mylib文件夹里面。 

        我们这一次包含两个头文件, 其中动态库里面有着myprintf.h的方法, 静态库有着mymath.h的方法。 

        接下来编译它, 编译的方式和之前是一样的, 就是利用-I选择搜索头文件的路径。 ——只用定位到路径, 不需要定位头文件的名称。并且使用-L定位搜索的库路径。——也是只需要定位到路径, 但是后面要跟上-I + 库名(去掉前缀和后缀)

        那么, 只要未来我们需要使用别人的库, 就是用-I,-L以及后面跟-l。

        但是, 又有问题了。 看我们运行我们的a.out, 就会发现, 运行不出结果, 并且下图的红字意思就是说动态库不能分享目标文件。 

        请问这是为什么呢?

我们如果使用a.out就会看到, 下面的我们自己创建的动态库, 它显示没有找到。        

        但是我们不是已经告诉gcc了吗?像什么-I, -L, -l这不就是在告诉gcc要去哪里寻找库吗?——我们也知道, 我们使用的指令是告诉gcc库在哪里, 而gcc是什么呢?gcc是编译器。 也就是说, 我们的指令是告诉编译器库在哪里放着。 

        但是, 我们也要告诉我们的加载器!!!那么我们如何告诉加载器呢?这里有四种方法:

  • 其最简单的方法就是直接将动态库拷贝到lib64里面。 因为我们知道系统的库默认搜索路径就是lib4, 所以我们将库直接拷贝到lib64去是一定可以的。
  • 建立软链接 建立好了之后, 就可以看到我们的加载器也能找到库了!注意实验完成之后要unlink软链接。
  • 第三种方法是调整环境变量——有一个环境变量是专门用来调整用户的库的默认搜索路径的。——这个环境变量是LD——LIBRARY——PATH, 就是下图:现在我们使用export指令添加默认搜索路径就可以了!!!注意实验完成之后需要重启服务器,就可以将LD_LIBRARY_PATH做的修改清空。
  • 第四种方式我们可以先变成root用户, 然后进入etc目录下的ld.so.conf.d目录。 (这个目录文件干啥用的博主也不清楚, 但是可以解决问题)。进入到这个路径我们只需要在这个路径创建一个文件, 这里取一个dynamic-9-4.conf文件名。然后打开文件, 将路径拷贝到里面去:完成之后ldconfig一下, 就饿可以看到a,out的动态库链接好了。所以综上, 第四种方式就是——/etc/ld.so.conf.d建立自己的动态库路径的配置文件, 然后重新ldconfig即可。

综上,就是令加载器知道指定动态库的四种方法, 实际上我们在日常工作中一般使用的都是别人的成熟的库, 都是采用直接安装的方式!!!

动态库是如何被加载的

        通过上面的学习, 我们知道,动态库在运行的时候, 是要被加载的。 但是静态库不会被加载, 而且我们ldd的时候, 我们会看到ldd下面没有所谓的静态库:

        这说明我们的程序默认是动态连接的, 只不过我们使用的mymath方法只提供了静态库, 只能让静态库提供静态链接我们生成可执行程序之后就说明我们的mymath的方法已经拷贝到可执行程序里面了。 已经和静态库没有关系了, 所以我们将静态库删除后, 程序仍可运行。 这就好比静态库卖的是二手电脑,而动态库是网吧。 静态库卖出去的电脑已经是我们自己的了, 而动态库我们只是暂时使用。 

         而且, 常见的动态库是被所有的可执行程序(有动态链接的)共享的, 所以也叫共享库 所以动态库在系统中加载之后, 会被所有的进程所共享!!!——这就能大大的节省内存!!!而对于动态库来说, 性能只有一开始加载动态库的时候会降低效率, 之后就可以被所有人使用了!!!

现在我们来重新回顾一下我们的进程地址空间的内容:

        将来我们运行一个可执行程序, 上图的1.exe。就是先将可执行程序的文件加载到内存中, 然后保存在内存的page里面, 并且创建一个进程和虚拟地址空间, 然后页表映射到这个程序的代码和数据。 

        这里有一个虚拟机的原理小知识点可以补充一下:就是我们以前是知道的, 进程间是独立的, 我们的1.exe挂掉, 不影响2.exe。

        那么就有一个问题, 如果我们的1.exe是操作系统的代码呢?意思就是说如果我们的操作系统被当作一个可执行程序可以吗?——操作系统上面再跑一个操作系统, 而又因为进程间的独立性, 所以这个操作系统不影响我们原来的操作系统。 ——这就是内核级别的虚拟机的原理!!!

        现在回归我们的正题, 就是动态库是如何被加载的呢?

        我们知道, linux下一切皆文件, 那么, 动态库也是一个文件。 并且是一个可执行的文件, 既然是一个可执行的文件, 那么他就能够被加载到内存中。 如下图红框框就是一个动态库文件。

而加载过程就是如下:

  •         一开始, 我们的1.exe加载到内存中, 形成了一个进程, 并且疯狂的运行。 可是, 当我们需要使用某个方法的时候, 我们当前的代码中没有所谓的方法的实现, 那么我们就会利用编译的时候所说的库的位置去寻找对应的库。 找到后, 就将对应的动态库加载到内存。
  •         加载到内存后,很显然, 我们的进程并不能看到这个库, 因为没有也表映射, 所以我们就要将加载进来的库, 映射到我们虚拟地址空间中的共享区中!!!
  •         最后, 以后我们的进程, 再想调用库里面的某种方法, 就直接向共享区里面找就可以了!

        从上面我们就可以看到, 也可使说是下一个结论——就是动态库加载到内存, 建立映射关系后, 从此往后, 我们执行的任何代码, 都是在我们的进程的地址空间中进行执行!!!

        并且, 因为系统中的动态库不可能只有一个, 会有很多个, 那么操作系统就势必会将这些动态库管理起来——先描述, 再组织——也就是说, 对于系统中的所有的库的各种情况, 操作系统都非常清楚!!!

        那么问题来了, 我们说过, 动态库是共享库。 那么多个进程之间是是可能使用一个库的。 而这个库里面可能存在errno这种全局变量。而库在物理内存中只有一份, 那么当一个进程修改这个变量的时候,  会不会影响其他进程呢?

        答案是当然不会, 为什么? 

        因为会发生写时拷贝——可能有的友友会问, 问什么会发生写时拷贝?写时拷贝不是发生在父子进程中的吗, 我们的多个进程可能不是父子进程啊,也会发生写时拷贝吗?——这里就牵扯到了发生写时拷贝的本质,而写时拷贝的本质就是有两个进程的页表映射到了内存上面的同一块区域, 并且发生了修改, 这就会发生写时拷贝!!!

 ————以上就是本篇的全部内容, 本篇内容到此就结束啦, 感谢友友的阅读, 下面是本节的笔记, 和正文几乎一样的, 觉得本节内容有用的话可以保存方便查阅哦

 

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

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

相关文章

如何开发潜在客户

本文将介绍发掘潜在客户的关键步骤,其中利用企搜客大数据拓客,是相对高效的工具模式,快速开发潜在客户拓展业务。 一、潜在客户开发的重要性 潜在客户开发是企业营销中至关重要的一环。通过精准的目标客户定位和有效的开发策略,企…

J.U.C Review - CopyOnWrite容器

文章目录 什么是CopyOnWrite容器CopyOnWriteArrayList优点缺点源码示例 仿写:CopyOnWriteMap的实现注意事项 什么是CopyOnWrite容器 CopyOnWrite容器是一种实现了写时复制(Copy-On-Write,COW)机制的并发容器。在并发场景中&#…

2024年高教社杯数学建模国赛E题解题思路

E 题 交通流量管控 问题背景 随着城市化进程的加快、机动车的快速普及,以及人们活动范围的不断扩大,城市道路交通拥堵问题日渐严重,即使在一些非中心城市,道路交通拥堵问题也成为影响地方经济发展和百姓幸福感的一个“痛点”&a…

SpringDataJPA系列(6)Entiry注解使用

SpringDataJPA系列(6)Entiry注解使用 JPA协议规定 实体是直接进行数据库持久化操作的领域对象,必须通过 Entity 注解进行标示实体必须有一个 public 或者 protected 的无参数构造方法实体里面必须要有一个主键,主键标示的字段可以是单个字段&#xff0…

《机器学习》—— PCA降维

文章目录 一、PCA降维简单介绍二、python中实现PCA降维函数的介绍三、代码实现四、PCA降维的优缺点 一、PCA降维简单介绍 PCA(主成分分析,Principal Component Analysis)是一种常用的数据降维技术。它通过线性变换将原始数据转换到新的坐标系…

持久化分析

目录 介绍步骤WMI持久化分析注册表映像劫持IFEO持久化 介绍 1、WMI 的全称是 Windows Management Instrumentation,即 Windows 管理规范,在 Windows 操作系统中,随着 WMI 技术的引入并在之后随着时间的推移而过时,它作为一项功能…

Linux【6】系统

时间日期 date日期 cal——当月日历 cal -y 今年的日历 磁盘占用df du df 剩余空间 du 目录下的文件大小 进程ps ps aux a——其他用户 u——详细状态 x——没有控制终端 只看CPU占用高的进程top kill pid代号 ——杀死程序 通配符(简略版) …

每日OJ_牛客_解读密码(简单模拟)

目录 牛客_解读密码(简单模拟) 解析代码 牛客_解读密码(简单模拟) 解读密码__牛客网 解析代码 题目意思:给定字符串中包含其他符合一级数字,将字符串中数字解析出来。 解析步骤: 题目明确…

LabVIEW声发射数据采集系统开发

声发射(Acoustic Emission, AE)技术是材料检测中的一种无损检测方法,广泛用于结构健康监测。本文将介绍一个基于LabVIEW的声发射数据采集系统的真实案例,涵盖工作原理、开发流程、硬件选型、注意事项及难点。该系统通过LabVIEW平台…

LlamaIndex 使用 RouterOutputAgentWorkflow

LlamaIndex 中提供了一个 RouterOutputAgentWorkflow 功能,可以集成多个 QueryTool,根据用户的输入判断使用那个 QueryEngine,在做查询的时候,可以从不同的数据源进行查询,例如确定的数据从数据库查询,如果…

2024年装电脑,就认准这几个型号,能避坑!

前言 小伙伴是否都会觉得,自己又不懂电脑,跑电脑城去装机又怕被坑。这时候只能找熟人给装机,至少……熟人应该不会坑自己吧?! 这不,小白电脑技术的抖音评论区上就有这么一条评论: 这哥们找一熟…

最新HTML5中的视频和音频讲解

第6章 HTML5中的视频和音频 H5新增video,audio,播放视频和音频,统称为多媒体元素。 6.1 多媒体元素基本属性 video用于电影文件和其他视频流的播放。 audio用于音乐文件和其他音频流的播放。 video的属性 src:文件路径,本地或者网络上。…

Android经典实战之SurfaceView原理和实践

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点 SurfaceView 是一个非常强大但也相对复杂的 UI 组件,特别适用于对性能要求较高的绘制任务,如视频播放、游戏等。 1. Su…

Java 方法的定义

目录 1.Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段。 2.方法包含一个方法头和方法体,下面是一个方法的所有部分: (1)修饰符:可选。告诉编译器如何调用该方法,定义了该…

Java笔试面试题AI答之JDBC(2)

文章目录 7. 列出Java应该遵循的JDBC最佳实践?8. Statement与PreparedStatement的区别,什么是SQL注入,如何防止SQL注入Statement与PreparedStatement的区别什么是SQL注入如何防止SQL注入 9. JDBC如何连接数据库?1. 加载JDBC驱动程序2. 建立数…

Python复杂网络社区检测:并行谱聚类算法设计与多种算法应用实战研究

原文链接:https://tecdat.cn/?p37574 分析师:Leiyun Liao 在当今的网络科学领域,复杂网络中的社区检测成为了一个至关重要的研究课题。随着信息技术的飞速发展,各种大规模网络不断涌现,如社交网络、生物网络等。准确地…

chapter12-异常(Exception)——(作业)——day15

目录 457-异常课后作业 458-异常课后作业2 457-异常课后作业 package chapter12.exception.homework;/*** author LuHan* version 1.0*/ public class Homework01 {public static void main(String[] args) {try {if(args.length!2){throw new ArrayIndexOutOfBoundsException…

立创商城9.9免邮活动开始啦!

从9月2日起,立创商城推出免邮活动,每月在领券中心>精选专区领取免邮券,即可享受满9.9元使用免邮券服务。 未注册的用户,可扫描下方二维码注册哦~

2024高教社杯数学建模国赛ABCDE题选题建议+初步分析

提示&#xff1a;DS C君认为的难度&#xff1a;C<B<A&#xff0c;开放度&#xff1a;A<C<B 。 D、E题推荐选E题&#xff0c;后续会直接更新E论文和思路&#xff0c;不在这里进行选题分析&#xff0c;以下为A、B、C题选题建议及初步分析 A题&#xff1a;“板凳龙”…

AI技术的新篇章:GPT Next、Gemini 2、GPT-6 和千代理人探索虚拟世界

在AI技术飞速发展的今天&#xff0c;许多令人兴奋的突破正逐渐进入公众视野。最近的新闻显示&#xff0c;诸如OpenAI的GPT Next、Google的Gemini 2.0、GPT-6以及模拟虚拟世界中的1000个AI代理人等前沿项目&#xff0c;标志着人工智能领域即将进入一个全新阶段。本文将深入探讨这…