C++编译之(1)-g++单/多文件/库的编译及C标准的发展历程

news/2024/11/20 1:26:53/

g++编译入门

本文为您介绍g++的编译用法;通过从最简单的单文件编译,到多文件编译,再到动态库、静态库的编译及使用;
例子都经过实际编译并运行,可谓全网最良心之作呐,放心拷贝粘贴学习!

1、g++的编译单文件

c++的程序编译的过程如下:

1.预编译->2.编译->3.汇编->4.链接

以个Test.cpp为例说明整个编译过程

Test.cpp 文件如下

#include <iostream>
#define MAX_NUM 10
// 定义宏
int main() {// 输出Hello worldstd::cout<<"Hello world!"<<std::endl;// 输出Max+nint n = 100;std::cout<<"MAX_NUM+n:"<<MAX_NUM+n<<std::endl;return 0;
}

1)预编译

宏的替换,还有注释的消除,还有找到相关的库文件

g++ -E Test.cpp > Test.i

只做预处理,生成一个Test.i的文件,该文件多了很多内容,我们省略前面1万+行,只看最下面对我们的写的源码做了什么处理;
Test.i内容如下

int main() {std::cout<<"Hello world!"<<std::endl;int n = 100;std::cout<<"MAX_NUM+n:"<<10 +n<<std::endl;return 0;
}

这就是预处理做的我们能理解的事,其他的有空自己再慢慢深挖。

2)编译

将预处理后的文件转换为汇编文件,里面为汇编指令

g++ -S Test.i

当然也可以直接将源代码cpp处理 g++ -S Test.cpp,两者编译后的输出结果一模一样

编译后,将得到一个Test.s的文件,前面0行开始截取部分内容如下:

	.file	"Test.cpp".text.section	.rodata.type	_ZStL19piecewise_construct, @object.size	_ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:.zero	1.local	_ZStL8__ioinit.comm	_ZStL8__ioinit,1,1
.LC0:.string	"Hello world!"
.LC1:.string	"MAX_NUM+n:".text.globl	main.type	main, @function
main:
.LFB1522:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$16, %rspleaq	.LC0(%rip), %rsileaq	_ZSt4cout(%rip), %rdicall	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLTmovq	%rax, %rdxmovq	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %raxmovq	%rax, %rsimovq	%rdx, %rdicall	_ZNSolsEPFRSoS_E@PLTmovl	$100, -4(%rbp)leaq	.LC1(%rip), %rsileaq	_ZSt4cout(%rip), %rdicall	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLTmovq	%rax, %rdxmovl	-4(%rbp), %eaxaddl	$10, %eaxmovl	%eax, %esimovq	%rdx, %rdicall	_ZNSolsEi@PLTmovq	%rax, %rdxmovq	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %raxmovq	%rax, %rsimovq	%rdx, %rdicall	_ZNSolsEPFRSoS_E@PLTmovl	$0, %eaxleave.cfi_def_cfa 7, 8ret.cfi_endproc
...

显然,上面的是机器无关的汇编指令

3)汇编

将汇编文件转为目标文件

g++ -c Test.s # 当然也可以直接使用`g++ -c Test.cpp`从源文件直接输出

汇编执行后,将生成Test.o的目标文件(二进制文件)

4)链接

将目标文件和库文件整合为一个可执行文件

g++ Test.o -L usr/include/iostream

-L后为库文件目录

命令执行后,将生成一个a.out,我们可以运行一下该文件,前面的代码正常运行,如下所示:

$ ./a.out 
Hello world!
MAX_NUM+n:110

使用-o可以为可执行文件命名

g++ Test.o -o Test -L usr/include/iostream
# 同理可以直接对源码输出`g++ Test.cpp -o Test -L usr/include/iosream`

执行后,将输出Test的可执行文件

5)一步到位编译

# 直接编译
g++  Test.cpp -o Test
# 执行结果
./Test
Hello world!
MAX_NUM+n:110

直接输出Test可执行文件

g++常用的参数

-c 生成.o目标文件

-o可执行文件命名

-shared 指定生成动态链接库

-static 指定生成静态链接库

-L 要链接的库所在目录

-l 指定链接时需要的库,隐含命名规则,即自动在前加lib,在后加.a或.so确定库文件名

-std 设置C语言版本 g++ -std=c11 Test.cpp -o Test

2、g++编译多文件

前面我们知道如何编译一个c++代码,如果多个c++文件组织在一起如何用g++编译呢?

我们基于前面测试项目,再创建一个文件HelloToolsClass.cpp,现在我们有两个文件了:Test.cppHelloToolsClass.cpp

其中HelloToolsClass.cpp为完整的类声明及定义为一体文件,内如如下;

class HelloTools{public:void print(int a, int b);int add(int a, int b);
};void HelloTools::print(int a, int b){std::cout<<"a+b="<<a<<"+"<<b<<"="<<a+b<<std::endl;
}int HelloTools::add(int a, int b){int c = 0;c = a+b;print(a , b);return c;
}

Test.cpp改造成如下所示

#include <iostream>
// 定义宏
#define MAX_NUM 10// 声明类
class HelloTools{public:void add(int a, int b);
};int main() {// 输出Hello worldstd::cout<<"Hello world!"<<std::endl;// 输出Max+nint n = 100;std::cout<<"MAX_NUM+n:"<<MAX_NUM+n<<std::endl;std::cout<<"================================="<<std::endl;// create objHelloTools hello;// call functionhello.add(200,300);return 0;
}

现在Test.cpp中调用了HelloToolsClass.cpp,如何编译他们呢?

如果我们像前面一样,一步到位直接编译Test.cpp它能自动找到我们需要引用的类吗?往下看

$ g++ Test.cpp -o Test
/usr/bin/ld: /tmp/ccVmXnOM.o: in function `main':
Test.cpp:(.text+0xbf): undefined reference to `HelloTools::add(int, int)'
collect2: error: ld returned 1 exit status

显然,出错了,不是我们要的结果,提示HelloTools未定义

这里需要注意,一般我们会将类的声明与定义拆开两个文件,声明放在*.h,函数的定义放在*.cpp中,这样在需要引用其他类或方法时,只需要使用#include "*.h"引用头文件即可;而*cpp文件是不允许用#include引用的;
那如何直接使用一个声明与定义的都在cpp的类呢,可以像例子那样,直接在需要引用的地方,再次声明一下,其作用就等同#include做的工作

正确的姿势如下:

g++ Test.cpp HelloToolClass.cpp -o Test

运行测试一下结果

./Test 
Hello world!
MAX_NUM+n:110
=================================
a+b=200+300=500

成功了!

这里,我们得到一个结论:一次编译环境下,源文件之间的函数一般情况下是可以互相调用的,前提是要声明以及不限制它的作用域同

3、g++编译复杂多层次文件

我们先对前面的项目做个小改动,把HelloToolsClass.cpp拆分成*.h*.cpp,如下所示:

// HelloToolsClass.h
#include <iostream>class HelloTools{public:void print(int a, int b);int add(int a, int b);
};
// HelloToolClass.cpp
#include <iostream>
#include "HelloToolClass.h"void HelloTools::print(int a, int b){std::cout<<"a+b="<<a<<"+"<<b<<"="<<a+b<<std::endl;
}int HelloTools::add(int a, int b){int c = 0;c = a+b;print(a , b);return c;
}

Test.cpp 则改造成如下所示

// Test.cpp
#include <iostream>
#include "HelloToolClass.h"
// 定义宏
#define MAX_NUM 10int main() {// 输出Hello worldstd::cout<<"Hello world!"<<std::endl;// 输出Max+nint n = 100;std::cout<<"MAX_NUM+n:"<<MAX_NUM+n<<std::endl;std::cout<<"================================="<<std::endl;// create objHelloTools hello;// call functionhello.add(200,300);return 0;
}

编译,并执行

# 编译
g++ Test.cpp TestHelloToolClass.cpp -o Test
# 执行结果
Hello world!
MAX_NUM+n:110
=================================
a+b=200+300=500

执行成功

显然,分离后的*.h文件,并没有直接参与我们的编译命令,编译命令及结果都与前面的简单的多文件编译一样。但是*.h确实必须参与编译
这里再次说明,有*.h头文件在开发复用时,非常方便,否则,我们得一个个声明我们需要引用的外部方法或函数

我们继续改造前面的项目,使之更加结构化、项目化、工程化。首先我们构造出如下项目结构

- mutilFilesDemo- include // 头文件目录- HelloTools.h- Prints.h- src // 源码目录- module // 源码模块- Prints.cpp // Prints类- HelloTools.cpp // HelloTools类- main.cpp // main类

首先我们把前面的Test.cpp 重命名为 main.cpp 看起来高大上一点;再修改内容如下:

// main.cpp
#include <iostream>
#include "include/HelloTools.h"
// 定义宏
#define MAX_NUM 10int main() {// 输出Hello worldstd::cout<<"Hello world!"<<std::endl;// 输出Max+nint n = 100;std::cout<<"MAX_NUM+n:"<<MAX_NUM+n<<std::endl;std::cout<<"================================="<<std::endl;// create objHelloTools hello;// call functionhello.add(200,300);return 0;
}

接着我们继续在相应目录下创建两个新文件,其内容如下:

Prints.h内容如下:

// Prints.h
class Prints{public:void printOneLine(std::string s);
};

Prints.cpp内容如下:

// Prints.cpp
#include <iostream>
#include "../../include/Prints.h"void Prints::printOneLine(std::string s){std::cout<<s<<std::endl;
}

最后把原来的HelloTools.cpp拆分成两个文件*.h*.cpp

HelloTools.h内容如下:

// HelloTools.h
class HelloTools{public:void print(int a, int b);int add(int a, int b);
};

HelloTools.cpp内容如下:

// HelloTools.cpp
#include <iostream>
#include "../include/HelloTools.h"
#include "../include/Prints.h"void HelloTools::print(int a, int b){std::cout<<"a+b="<<a<<"+"<<b<<"="<<a+b<<std::endl;// 调用Prints类Prints p;p.printOneLine("call Prints Class:PrintOneLine.");}int HelloTools::add(int a, int b){int c = 0;c = a+b;print(a , b);return c;
}

完整的目录解构见前面开始给出的解构

代码编码完成后开始编译,如何编译呢?我们参考前面的方法进行编译!

g++ main.cpp ./src/HelloTools.cpp ./src/modules/Prints.cpp -o main

编译成功输出main,执行后

$ ./main
Hello world!
MAX_NUM+n:110
=================================
a+b=200+300=500
call Prints Class:PrintOneLine.

成功!

4、g++编译为静态编库(*.a)及调用

静态库编译(lib*.a)

我们创建两个文件ToolLibs.hToolLibs.cpp,内容如下

ToolLibs.h

// ToolLibs.h
class ToolLibs{public:int add(int a, int b);
};

ToolLibs.cpp

// ToolLibs.cpp
#include <iostream>
#include "ToolLibs.h"int ToolLibs::add(int a, int b){std::cout<<"使用库-add(a,b)"<<std::endl;std::cout<<"结果为:a+b="<<a+b<<std::endl;return a+b;
}

执行编译,生成目标文件*.o

# 生成目标文件
$ g++ -c ToolLibs.cpp
# 查看生成的结果*.o
$ ls
ToolLibs.cpp  ToolLibs.h  ToolLibs.o

执行生成静态库文件

注意库文件应符合linux命名规则,lib*.a为静态库,lib*.so为动态库

# 创建静态库
$ ar crv libToolLibs.a ToolLibs.o
a - ToolLibs.o
# 查看结果
$ ls
libToolLibs.a  ToolLibs.cpp  ToolLibs.h  ToolLibs.o

上面生成了libToolLibs.a而文件*.o则可删除丢弃

ar命令用于 创建建、修改库,也可以从库中提出单个模块;常用的命令参数如下:
ar crv <创建的静态库名> <目标文件名1> <目标文件名n> 可合并多个目标库文件(*.o)
c - 创建库文件
r - 将文件插入库文件中
v - 程序执行时显示详细的信息
t - 显示库文件中所包含的文件

如何使用静态库?

我们还是已前面的例子为例,介绍如何使用这个库

我们把这个库的源代码copy到前面的项目解构下方便介绍,新的目录解构如下:

- mutilFilesDemo- include // 头文件目录- HelloTools.h- Prints.h- libs // 库子项目目录- ToolLibs.h- ToolLibs.cpp- libToolLibs.a // 编译好的静态库文件- src // 源码目录- module // 源码模块- Prints.cpp // Prints类- HelloTools.cpp // HelloTools类- main.cpp // main类

使用一个外部的资源,必须先声明,我们修改一下前面的HelloTools.cpp,修改后的文件如下:

// HelloTools.cpp
#include <iostream>
#include "../include/HelloTools.h"
#include "../include/Prints.h"
// 开发的静态库头文件
#include "../libs/ToolLibs.h"void HelloTools::print(int a, int b){std::cout<<"a+b="<<a<<"+"<<b<<"="<<a+b<<std::endl;// 调用Prints类Prints p;p.printOneLine("call Prints Class:PrintOneLine.");}int HelloTools::add(int a, int b){int c = 0;ToolLibs tool;c = tool.add(a,b);return c;
}

其他不做任何修改,我们参考前面的方式编译一下

g++ main.cpp ./src/HelloTools.cpp ./src/modules/Prints.cpp -o main

报如下错误:

/usr/bin/ld: /tmp/cc6RvAl9.o: in function `HelloTools::add(int, int)':
HelloTools.cpp:(.text+0x17c): undefined reference to `ToolLibs::add(int, int)'
collect2: error: ld returned 1 exit status

提示,未定义ToolLibs:add,显然我们只引入一个库的头文件是不够的,我们需要想办法把库的内容真正引进来,怎么办呢?

  • 方案1,源码直接编译

因为我们有源码(开源真是好),我们可以不顾任何道德,直接编译

显然这种方式必定可行的,效果就跟前面我们的多文件编译一样

# 直接一块编译libs库源码
g++ main.cpp ./src/HelloTools.cpp ./src/modules/Prints.cpp ./libs/ToolLibs.cpp -o main

然而,这样就不需要创造静态库这东西了;于是的核心主题终于出场

  • 方案2,编译时链接到静态库
g++ main.cpp ./src/HelloTools.cpp ./src/modules/Prints.cpp -L ./libs -l ToolLibs -o main

g++ 参数含义
L - 需要链接的库的目录地址
l - 需要链接的库名称,注意:库名需要吧去除前缀'lib'及后缀'.a'

编译成功,执行测试一下:

$ ./main
Hello world!
MAX_NUM+n:110
=================================
使用库-add(a,b)
结果为:a+b=500

显示,这种方式可以让我们达到构件级的程序复用,实现核心库与应用代码的解耦,而不是仅仅是代码级的复用(复制/粘贴代码);而且如果我们想保护自己的库,或者不希望公开源码,这也是一种很好的方式

5、g++编译为动态编库(*.so)及调用

动态库编译(lib*.so)

接着前面的项目,在libs目录下,执行如下命令

# 编译为动态库
$ g++ -fPIC -shared ToolLibs.cpp -o libToolLibs.so
# 查看输出
$ ls
libToolLibs.a  libToolLibs.so  ToolLibs.cpp  ToolLibs.h  ToolLibs.o

注意输出的动态库的命名规范,务必满足lib*.so规则

我们看到,输出了我们期待的*.so动态库

如何链接到动态库呢?

我们先把前面生成的静态库删除,同时把前面出来的可执行main也更改一下名

# 改名
cd mutilFilesDemo
mv main main2
# 删除静态库
cd libs
rm libToolLibs.so toolLibs.o

注意,如果静态库与动态库同名(仅仅后缀.a.so不一样),则链接时,优先链接到动态库*.so;为避免分析干扰,这里删除了先前的静态库

编译时的链接方式跟我们前面的基本一致,如下命令:

g++ main.cpp ./src/HelloTools.cpp ./src/modules/Prints.cpp -L ./libs -l ToolLibs -o main

注意:编译时,无论链接动态库还是静态库,我们需要本地必须存在这样一个库,否则将会编译失败

编译成功,输出main,我们查看对比一下原来main2的发现采用动态库链接编译的结果会更小一点

我们执行一下编译的结果

$ ./main
./main: error while loading shared libraries: libToolLibs.so: cannot open shared object file: No such file or directory

发现报错,无法执行并提示缺少libToolsLibs.so库,我们把前面编译好的libToolsLibs.so复制到/usr/lib/目录下,再次执行/main

$ ./main
Hello world!
MAX_NUM+n:110
=================================
使用库-add(a,b)
结果为:a+b=500

执行成功!

由此可见,静态库链接编译的会吧整个静态库的内容放在编译结果中,而显然动态库链接编译的结果并不会;这就是导致静态编译的结果更大的原因;

有上可知,动态链接在运行时,本地必须存在该动态库资源,且存放系统指定的目录下/usr/lib,当然这个目录是可配置的,我们可以查看一下哪些放在哪些目录下,可以被自动定位到

cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf

/etc/ld.so.conf/etc/ld.so.conf.d/*.conf文件都是配置的动态库加载路径配置文件,可添加一个so自动加载配置文件实现动态库自动加载

当然,我们还有临时的解决方案,临时设置某个动态库路径

$ cd mutilFilesDemo
# 添加路径
$ LD_LIBRARY_PATH=LD_LIBRARY_PATH:./libs
# 执行
$ ./main
Hello world!
MAX_NUM+n:110
=================================
使用库-add(a,b)
结果为:a+b=500

当然这是临时方案,也只能在当面session中有效,且上面用的相对路径,只能在当前目录下有效;如需要长久一点有效(本次用户登录内),则需要设置在~/.bashrc中,并使用srouce命令生效;永久有效可在设置到/etc/profile;更优雅的方案,建议还是用前面/etc/ld.so.conf.d/*.conf的方案

C语言版本的发展历程简介

如何查看编译支持C语言标准?

先查看一下gcc的版本

$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

版本为9.4.0,可以去官网查看9.4.0支持那些C的标准,或者直接用下面的命令

$ man gcc
-std=Determine the language standard.   This option is currently only supported when compiling C or C++.The compiler can accept several base standards, such as c90 or c++98, and GNU dialects of those standards, such as gnu90 or gnu++98.  When a base standard is specified, the compileraccepts all programs following that standard plus those using GNU extensions that do not contradict it.  For example, -std=c90 turns off certain features of GCC that are incompatiblewith ISO C90, such as the "asm" and "typeof" keywords, but not other GNU extensions that do not have a meaning in ISO C90, such as omitting the middle term of a "?:" expression. On theother hand, when a GNU dialect of a standard is specified, all features supported by the compiler are enabled, even when those features change the meaning of the base standard.  As aresult, some strict-conforming programs may be rejected.  The particular standard is used by -Wpedantic to identify which features are GNU extensions given that version of the standard.For example -std=gnu90 -Wpedantic warns about C++ style // comments, while -std=gnu99 -Wpedantic does not.A value for this option must be provided; possible values arec90c89iso9899:1990Support all ISO C90 programs (certain GNU extensions that conflict with ISO C90 are disabled). Same as -ansi for C code.iso9899:199409ISO C90 as modified in amendment 1.c99c9xiso9899:1999iso9899:199xISO C99.  This standard is substantially completely supported, modulo bugs and floating-point issues (mainly but not entirely relating to optional C99 features from Annexes F and G).See <http://gcc.gnu.org/c99status.html> for more information.  The names c9x and iso9899:199x are deprecated.c11c1xiso9899:2011ISO C11, the 2011 revision of the ISO C standard.  This standard is substantially completely supported, modulo bugs, floating-point issues (mainly but not entirely relating tooptional C11 features from Annexes F and G) and the optional Annexes K (Bounds-checking interfaces) and L (Analyzability).  The name c1x is deprecated.c17c18iso9899:2017iso9899:2018ISO C17, the 2017 revision of the ISO C standard (published in 2018).  This standard is same as C11 except for corrections of defects (all of which are also applied with -std=c11)and a new value of "__STDC_VERSION__", and so is supported to the same extent as C11.c2x The next version of the ISO C standard, still under development.  The support for this version is experimental and incomplete.

上面存在C11 C17 C18说明,都支持这些C语言版本

C语言版本的发展历程

C89之前,并没有统一C标准规范

  • C89
    考虑到标准化的重要,ANSI(American National Standards Institute)制定了第一个 C 标准,在1989年被正式采用(American National Standard X3.159-1989),故称为 C89,也称为 ANSI C。
    该标准随后被 ISO 采纳,成为国际标准(ISO/IEC 9899:1990)。

  • C95
    这是对 C89 的一个修订和扩充,称为“C89 with Amendment 1”或 C95,严格说来并不是一个真正的标准。

  • C99
    1999年,在做了一些必要的修正和完善后,ISO 发布了新的 C 语言标准,命名为 ISO/IEC 9899:1999,简称“C99”

  • C11
    2007 年,C语言标准委员会又重新开始修订C语言,到了 2011 年正式发布了 ISO/IEC 9899:2011,简称为 C11 标准。
    C11 标准新引入的特征尽管没 C99 相对 C90 引入的那么多,但是这些也都十分有用,比如:字节对齐说明符、泛型机制(generic selection)、对多线程的支持、静态断言、原子操作以及对 Unicode 的支持。

  • C17
    C17(也被称为为 C18)是于2018年6月发布的 ISO/IEC 9899:2018 的非正式名称,也是目前(截止到2020年6月)为止最新的 C语言编程标准,被用来替代 C11 标准。
    C17 没有引入新的语言特性,只对 C11 进行了补充和修正。

  • C2x
    C 2x下一个版本的 C 标准,预计将于2022年12月1日完成。


http://www.ppmy.cn/news/18809.html

相关文章

【3-神经网络八股】北京大学TensorFlow2.0

课程地址&#xff1a;【北京大学】Tensorflow2.0_哔哩哔哩_bilibiliPython3.7和TensorFlow2.1六讲&#xff1a;神经网络计算&#xff1a;神经网络的计算过程&#xff0c;搭建第一个神经网络模型神经网络优化&#xff1a;神经网络的优化方法&#xff0c;掌握学习率、激活函数、损…

【C++】从0到1入门C++编程学习笔记 - 核心编程篇:类和对象(上)

文章目录一、封装1.1 封装的意义1.2 struct和class区别1.3 成员属性设置为私有二、对象的初始化和清理2.1 构造函数和析构函数2.2 构造函数的分类及调用2.3 拷贝构造函数调用时机2.4 构造函数调用规则2.5 深拷贝与浅拷贝2.6 初始化列表2.7 类对象作为类成员2.8 静态成员三、C对…

深入理解Mysql底层数据结构

一. 索引的本质 索引是帮助MySQL高效获取数据的排好序的数据结构。 二. 索引的数据结构 二叉树红黑树Hash表BTreeBTree mysql的索引采用的是B树的结构 mysql为什么不用二叉树&#xff0c;因为对于单边增长的数据列&#xff0c;二叉树和全表扫描差不多&#xff0c;效率没有什…

国科大-高性能计算考试

考试比较难,课程比较繁琐. 高性能计算2022加粗样式考试 1. 启动MPI程序时系统生成的是1维程序 写出一个子程序或函数生成行和列通讯子 思路&#xff1a; 首先进行参数合法性的检查&#xff0c;然后将通信子对应的进程组进行划分&#xff0c;再将通信子对应的进程组进行列划分&…

grant之后要跟着flush privileges吗?

在 MySQL 里面,grant 语句是用来给用户赋权的。不知道你有没有见过一些操作文档里面提到,grant 之后要马上跟着执行一个 flush privileges 命令,才能使赋权语句生效。我最开始使用 MySQL 的时候,就是照着一个操作文档的说明按照这个顺序操作的。 那么,grant 之后真的需要…

Android蓝牙开发

前言 这是我大二做的一个智能小车配套使用的APP&#xff0c;用Android的蓝牙接口实现&#xff0c;当时有些os相关的内容Thread之类还有一些Android接口、java语法&#xff0c;我其实不是很理解。学了操作系统&#xff0c;再来回顾一下&#xff0c;并整理项目代码&#xff0c;项…

第四十四章 动态规划——背包问题模型(一)

第四十四章 动态规划——背包问题模型&#xff08;一&#xff09;一、模型概述二、模型变形1、AcWing 423. 采药&#xff08;1&#xff09;问题&#xff08;2&#xff09;分析&#xff08;3&#xff09;代码2、AcWing 1024. 装箱问题&#xff08;1&#xff09;问题&#xff08;…

[LeetCode周赛复盘] 第 329 场周赛20230122

[LeetCode周赛复盘] 第 329 场周赛20230122 一、本周周赛总结二、 [Easy] 6296. 交替数字和1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6297. 根据第 K 场考试的分数排序1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6298. 执行逐位运算使字符串相等1. 题目描述2. 思路…