g++编译详解

2023-11-14

g++编译详解

资料准备:

为了方便演示和讲解,在这里提前准备好几个简单的文件:test.cpp test.h main.cpp 文件内容如下:

main.cpp

#include "test.h"

int main (int argc, char **argv)
{
    Test t;
    t.hello();
    return 0;
}

test.h

//test.h
#ifndef _TEST_H_ 
#define _TEST_H_ 

class Test
{
public:
    Test();
    void hello();
    ~Test();
};
#endif  //TEST

test.cpp

//test.cpp
#include "test.h"
#include <iostream>
using namespace std;

Test::Test()
{

}

void Test::hello()
{
    cout << "hello" << endl;
}

Test::~Test()
{

}

C++的编译过程

一个完整的C++编译过程(例如g++ a.cpp生成可执行文件),总共包含以下四个过程:

  • 编译预处理,也称预编译,可以使用命令g++ -E执行
  • 编译,可以使用g++ -S执行
  • 汇编,可以使用as 或者g++ -c执行
  • 链接,可以使用g++ xxx.o xxx.so xxx.a执行

可以通过添加g++ --save-temps参数,保存编译过程中生成的所有中间文件
下面对这四个步骤进行逐一讲解

1、编译预处理阶段:主要对包含的头文件(#include )和宏定义(#define,#ifdef … )还有注释等进行处理。

可以使用g++ -E 让g++ 在预处理之后停止编译过程,生成 *.ii(.c文件生成的是*.i) 文件。
因为上面写的main.cpp中没有任何预编译指令,所以预编译生成与源文件几乎没有差别。这里预编译一下test.cpp文件

g++ -E test.cpp test.h -o test.ii

可以打开test.ii查看,刚刚的main.cpp文件预编译完成后的内容:
在这里插入图片描述

预编译完成后,#include引入的内容 被全部复制进预编译文件中,除此之外,如果文件中有使用宏定义也会被替换处理。

  • 预编译过程最主要的工作,就是宏命令的替换
  • #include命令的工作就是单纯的导入,这里其实并不限制导入的类型,甚至可以导入.cpp.txt等等。
  • 感兴趣的同学可以预编译一个包含Qt中信号的文件,会看到预编译之后:
    • emit直接成了空。发射信号实质就是一次函数调用;
    • 头文件中的signals:也被替换成了protected:(Qt5被替换为public:
    • 以及Qt中其他的宏定义都在预编译时被处理如:Q_OBJECT Q_INVOKEABLE

2、g++ 编译阶段:C++ 语法错误的检查,就是在这个阶段进行。在检查无误后,g++ 把代码翻译成汇编语言。

可以使用-S 选项进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

g++ -S main.ii -o main.s

汇编代码中生成的是和CPU架构相关的汇编指令,不同CPU架构采用的汇编指令集不同,生成的汇编代码也不一样:
在这里插入图片描述

3、g++ 汇编阶段:生成目标代码 *.o

有两种方式:

  • 使用 g++ 直接从源代码生成目标代码 g++ -c *.s -o *.o
  • 使用汇编器从汇编代码生成目标代码 as *.s -o *.o

到编译阶段,代码还都是人类可以读懂的。汇编这一阶段,正式将汇编代码生成机器可以执行的目标代码,也就是二进制码。

# 编译
g++ -c main.s -o main.o
# 汇编器编译
as main.s -o main.o

也可以直接使用as *.s, 将执行汇编、链接过程生成可执行文件a.out, 可以像上面使用-o 选项指定输出文件的格式。

4、g++ 链接阶段:生成可执行文件;Windows下生成.exe

修改main.cpp的内容,引用Test

#include "test.h"

int main (int argc, char **argv)
{
    Test t;
    t.hello();
    return 0;
}

生成目标文件:

  • g++ test.cpp -c -o test.o
  • g++ main.cpp -c -o main.o

链接生成可执行文件:

g++ main.o test.o -o a.out

链接的过程,其核心工作是解决模块间各种符号(变量,函数)相互引用的问题,更多的时候我们除了使用.o意外,还将静态库和动态库链接一同链接生成可执行文件。

对符号的引用本质是对其在内存中具体地址的引用,因此确定符号地址是编译,链接,加载过程中一项不可缺少的工作,这就是所谓的符号重定位。本质上来说,符号重定位要解决的是当前编译单元如何访问「外部」符号这个问题。

接下来我们先讲解如何将源文件编译成动态库和静态库,然后再讲述如何在链接时链接我们编译好的库。

编译动态库和静态库

大型项目中不可能使用一个单独的可执行程序提供服务,必须将程序的某些模块编译成动态或静态库:

编译生成静态库

使用ar命令进行“归档”(.a的实质是将文件进行打包)

ar crsv libtest.a test.o 
  • r 替换归档文件中已有的文件或加入新文件 (必要)
  • c 不在必须创建库的时候给出警告
  • s 创建归档索引
  • v 输出详细信息

编译生成动态库

使用g++ -shared 命令指定编译生成的是一个动态库

g++ test.cpp -fPIC -shared -Wl,-soname,libtest.so -o libtest.so.0.1
  • shared:告诉编译器生成一个动态链接库
  • -Wl,-soname:指示生成的动态链接库的别名(这里是libtest.so
  • -o:指示实际生成的动态链接库(这里是libtest.so.0.1
  • -fPIC
    • fPIC的全称是 Position Independent Code, 用于生成位置无关代码(看不懂没关系,总之加上这个参数,别的代码在引用这个库的时候才更方便,反之,稍不注意就会有各种乱七八糟的报错)。
    • 使用-fPIC选项生成的动态库,是位置无关的。这样的代码本身就能被放到线性地址空间的任意位置,无需修改就能正确执行。通常的方法是获取指令指针的值,加上一个偏移得到全局变量/函数的地址。
    • 关于PIC参数的详细解读:点此链接

在gcc中,如果指定-shared不指定-fPIC会报错,无法生成非PIC的动态库,不过clang可以。

库中函数和变量的地址是相对地址,不是绝对地址,真实地址在调用动态库的程序加载时形成。
动态库的名称有别名(soname),真名(realname)和链接名(linker name)。

  • 真名是动态库的真实名称,一般总是在别名的基础上加上一个小的版本号,发布版本构成
    别名由一个前缀lib,然后是库的名字加上.so构成,例如:libQt5Core.5.7.1
  • 链接名,即程序链接时使用的库的名字,例如:-lQt5Core
  • 在动态链接库安装的时候总是复制库文件到某个目录,然后用软连接生成别名,在库文件进行更新的时候仅仅更新软连接即可。

注意:

  • 生成的库文件总是以libXXX开头,这是一个约定,因为在编译器通过-l参数寻找库时,比如-lpthread会自动去寻找libpthread.solibpthread.a
  • 如果生成的库并没有以lib开头,编译的时候仍然可以连接到,不过只能以显示加在编译命令参数里的方式链接。例如g++ main.o test.so

静态编译和动态编译

编译C++的程序可以分为动态编译和静态编译两种

静态编译

链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。这种称为静态编译,静态编译中使用的库就是静态库(*.a*.lib)生成的可执行文件在运行时不需要依赖于链接库。

  • 优点:
    • 代码的装载速度快,执行速度也比较快
    • 不依赖其他库执行,移植方便
  • 缺点:
    • 程序体积大
    • 更新不方便,如果静态库需要更新,程序需要重新编译
    • 如果多个应用程序使用的话,会被装载多次,浪费内存。
g++ main.o libtest.a

编译完成后可以运行a.out查看效果,通过ldd命令查看运行a.out所需依赖,可以看到静态编译的程序并不依赖libtest库。
在这里插入图片描述

动态编译

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。

动态编译中使用的库就是动态库(*.so*.dll

动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

动态库在链接过程中涉及到加载时符号重定位的问题,感兴趣的同学参看链接:动态编译原理分析

  • 优点:
    • 多个应用程序可以使用同一个动态库,而不需要在磁盘上存储多个拷贝
    • 动态灵活,增量更新
  • 缺点:
    • 由于是运行时加载,多多少少会影响程序的前期执行性能
    • 动态库缺失会导致文件无法运行
g++ main.o libtest.so

编译完成后可以运行a.out查看效果,通过ldd命令查看运行a.out所需依赖
在这里插入图片描述

gcc链接参数 -L、-l、-rpath、-rpath-link

从上面的截图中,我们已经看到了刚才的程序运行报错,原因是找不到动态链接库libtest.so

这个报错的解决方案有很多例如:

  • LD_LIBRARY_PATH=. ./a.out

那么明明编译成功,运行时为什么会找不到库?为了弄清这个问题,我们需要对链接动态库的过程有一个更深入的理解。

我们在main.cpp中明确引用到了Test类,所以在编译进行到最后阶段,链接的时候。如果在所有参与编译的文件中没能检索到Test这个符号,则会报错未定义的引用。
所以在编译过程中必须能够找到包含Test符号的文件,可以是.o.a、或者.so
如果是.o或者.a,也就是静态链接,那么它会将.o或者.a中的内容一起打包到生成的可执行文件中,生成的可执行文件可以独立运行不受任何限制。
而如果是.so这种动态链接库,就比较麻烦了。链接器将不会把这个库打包到生成的可执行文件里,而仅仅只会在这里记录一个地址,告诉程序,如果遇到Test符号,你就去文件libtest.so的第三行第五列(打个比方,实际是一个相对的内存地址)找它的定义。

综上所述

  • 编译链接main.cpp的时候,必须能够找到libtest.so的动态库,记录下Test符号的偏移地址。
  • 运行的时候,程序必须找到libtest.so,然后寻址找到Test

编译时链接库

-L-l 链接器参数,就是指定链接时去(哪里)找(什么)库。

  • -l,代表链接哪个库,会自动检索lib开头的对应库名。 例如-lpthread,-lQt5Core。会自动检索libpthread.so,libpthread.a,libQt5Core.so,libQt5Core.a
    • 如果静态库动态库同时存在,优先链接动态库
  • -L,指定去哪里找库文件。例如指定:-L/home/threedog/test,则在编译时会优先检索/home/threedog/test/libpthread.so等文件。
  • 链接库最直接的办法是不用任何参数,直接写库的路径加载编译参数里。
  • 查找顺序
    • 如果直接写的库的全路径,则会直接去找到库,不走下面的顺序检索。
    • -L,优先级最高
    • 然后是系统的环境变量LIBRARY_PATH
    • 最后再找内定目录 /lib /usr/lib /usr/local/lib 这是当初编译 gcc时写在程序内的
    • 如果都找不到,会报错找不到文件或找不到-lxxxx

所以以上的编译命令,可以通过多种方式通过编译:

  • g++ main.o libtest.so,或g++ main.o ./libtest.so
  • g++ main.o /home/threedog/test/libtest.so
  • g++ main.o -ltest -L.,或g++ main.o -ltest -L/home/threedog/test/
  • LIBRARY_PATH=. g++ main.o -ltest,或LIBRARY_PATH=/home/threedog/test/ g++ main.o -ltest
  • 或者把libtest.so拷贝到/usr/lib目录下去。

运行时链接库

通过上面的方法编译出的a.out,运行会报错,通过ldd命令查看,发现编译时链接的libtest.so成了not found
这就引出了第二个问题:如何让程序运行的时候能够找到对应的库。

-Wl,-rpath就是做这个事情的:-Wl代表后面的这个参数是一个链接器参数,-rpath+库所在的目录,会给程序明确指定去哪里找对应的库。
手动将一个目录指定成了ld的搜索目录。

在这里插入图片描述

另外,也可以通过在环境变量LD_LIBRARY_PATH里添加路径的方式成功运行

在这里插入图片描述

运行时库的查找顺序:

  1. 编译目标代码时指定的动态库搜索路径(-rpath);
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
  4. 默认的动态库搜索路径/lib;
  5. 默认的动态库搜索路径/usr/lib.

rpath与rpath-link

其实rpath和rpath-link都是链接器ld的参数,不是gcc的。

rpath-linkrpath只是看起来很像,可实际上关系并不大,rpath-link-L一样也是在链接时指定目录的。
rpath-link的作用,在我们的这个实例中体现不出来。
例如你上述的例子指定的需要libtest.so,但是如果 libtest.so 本身是需要 xxx.so 的话,这个 xxx.so 我们你并没有指定,而是 libtest.so 引用到它,这个时候,会先从 -rpath-link 给的路径里找。
rpath-link指定的目录与并运行时无关。

C++头文件的搜索原则

上面提到了编译时链接库的查找顺序和运行时动态库的检索顺序,顺便再提一下C++编译时头文件的检索顺序:

  • #include<file.h>只在默认的系统包含路径搜索头文件
  • #include"file.h"首先在当前目录以及-I指定的目录搜索头文件, 若头文件不位于当前目录, 则到系统默认的包含路径搜索

顺序:

  1. 先搜索当前目录
  2. 然后搜索-I指定的目录
  3. 再搜索gcc的环境变量CPLUS_INCLUDE_PATH(C程序使用的是C_INCLUDE_PATH
  4. 最后搜索gcc的内定目录
    • /usr/include
    • /usr/local/include
    • /usr/lib/gcc/x86_64-redhat-linux/4.1.1/include

以上,就是对gcc参数的一些详细总结,下面根据上面的讲解解决几个常遇到的疑问:

问题1:-l链接的到底是动态库还是静态库?

  • 如果链接路径下同时有 .so 和 .a 那优先链接 .so

问题2:如果路径下同时有静态库和动态库如何链接静态库?

  • 最好的办法,是参数里直接写上静态库的全路径。
  • 另一个办法,可以使用-static参数,会强制链接静态库。这种方式生成的文件可以执行,但是文件的elf头会有问题,使用ldd,readelf -d查看会显示不是动态可执行文件。

问题3:如果文件中没有使用对应的库,编译器是否仍然会进行链接?

  • 这个取决于编译器的类型和版本,我本地gcc5.4,如果没有用到的库,即使写了-l也不会链接。而我本地的clang,则会明确链接对应的库即使我没有用到它。
    在这里插入图片描述

参考链接:

  • https://www.cnblogs.com/king-lps/p/7757919.html
  • https://blog.csdn.net/abcdu1/article/details/86083295
  • https://blog.csdn.net/weixin_40240269/article/details/86702090
  • https://www.jianshu.com/p/b2f611acba3d
  • https://www.cnblogs.com/youxin/p/5357614.html
  • https://blog.csdn.net/benpaobagzb/article/details/51277960
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

g++编译详解 的相关文章

  • 我如何理解这个 C 类型声明?

    double bar int double double double double 在查看讲座幻灯片时 我发现了留给学生的练习 用简单的英语来说 什么是类型bar在这个 C 声明中 Please帮助我解决这个问题 我什至不知道从哪里开始
  • 为什么在创建矩阵类时使用向量不好?

    对于我的矩阵类 我做了 template
  • EF Core 通过完全替换断开集合导航属性的更新

    使用 EF Core 5 0 我有一个 SPA 页面 可以加载Group实体及其集合Employee来自 API 的实体 var groupToUpdate await context Groups Include g gt g Emplo
  • 类特定的新删除运算符是否必须声明为静态

    标准中是否要求类特定的 new new delete 和 delete 是静态的 我可以让它们成为非静态成员运算符吗 为什么需要它们是静态的 它们被隐式声明为静态 即使您没有键入 static
  • 信号处理程序有单独的堆栈吗?

    信号处理程序是否有单独的堆栈 就像每个线程都有单独的堆栈一样 这是在 Linux C 环境中 来自 Linux 手册页signal 7 http kernel org doc man pages online pages man7 sign
  • 如何在 SqlDataReader.Read() 期间从死锁异常中恢复

    我的 NET 应用程序的事件日志显示 它在从 Sql Server 读取数据时偶尔会出现死锁 这种情况通常非常罕见 因为我们已经优化了查询以避免死锁 但有时仍然会发生 过去 我们在调用ExecuteReader函数在我们的SqlComman
  • 如何在 C# 控制台应用程序中将修饰符(ctrl、alt、shift)按键捕获为单个按键?

    Console ReadKey 仅在按下 正常 键时捕获输入 然后将修饰符 如果有 附加为键信息的一部分 如何将单个修饰键注册为输入 提供了一种解决方案这个链接 https blogs msdn microsoft com toub 200
  • vs2008 c#:Facebook.rest.api如何使用它来获取好友列表?

    如何在此基础上取得进一步的进步 获取好友列表的下一步是什么 string APIKey ConfigurationManager AppSettings API Key string APISecret ConfigurationManag
  • 单例模式和 std::unique_ptr

    std unique ptr唯一地控制它指向的对象 因此不使用引用计数 单例确保利用引用计数只能创建一个对象 那么会std unique ptr与单例执行相同 单例确保只有一个实例属于一种类型 A unique ptr确保只有一个智能指针到
  • 在 Linux 上的 Python 中使用受密码保护的 Excel 工作表

    问题很简单 我每周都会收到一堆受密码保护的 Excel 文件 我必须解析它们并使用 Python 将某些部分写入新文件 我得到了文件的密码 当在 Windows 上完成此操作时 处理起来很简单 我只需导入 win32com 并使用 clie
  • Visual Studio Code:如何配置 includePath 以获得更好的 IntelliSense 结果

    我是使用 Visual Studio Code 的完全初学者 我不知道我在做什么 我已经四处搜索 也许还不够 但我找不到像我这样的人如何配置的简单解释c cpp properties json每当我单击带有绿色波浪线下划线的行旁边的黄色灯泡
  • 将二进制数据从 C# 上传到 PHP

    我想将文件从 Windows C 应用程序上传到运行 PHP 的 Web 服务器 我知道 WebClient UploadFile 方法 但我希望能够分块上传文件 以便我可以监控进度并能够暂停 恢复 因此 我正在读取文件的一部分并使用 We
  • AES 输出是否小于输入?

    我想加密一个字符串并将其嵌入到 URL 中 因此我想确保加密的输出不大于输入 AES 是可行的方法吗 不可能创建任何始终会创建比输入更小的输出的算法 但可以将任何输出反转回输入 如果您允许 不大于输入 那么基本上您只是在谈论同构算法alwa
  • 在 EnvDTE 中调试时捕获 VS 局部变量

    是否可以使用 EnvDTE 进行 vsix Visual Studio 扩展来捕获本地和调试窗口使用的调试数据 或者可以通过其他方法吗 我想创建一个自定义的本地窗口 我们可以修改它以根据需要显示一些较重的内容 而无需为高级用户牺牲原始的本地
  • 如何更改 Ubuntu 14.04 上的 php-cli 版本?

    我是 Linux 新手 在篡改时破坏了一些 php 设置 如果我执行一个包含以下内容的 php 脚本 phpinfo 它显示 php 版本为 5 6 但通过命令行 如果我运行php v它返回 7 0 版本 我想让两个版本匹配 我怎样才能修复
  • 了解使用 Windows 本机 WPF 客户端进行 ADFS 登录

    我已经阅读了大量有关 ADFS 与 NodeJS Angular 或其他前端 Web 框架集成以及一般流程如何工作的文献 并通过 Auth0 Angular 起始代码构建了概念证明 但我不明白如何这可以与本机 WPF Windows 应用程
  • 每个数据库多个/单个 *.edmx 文件

    我有一个通过 ADO net 数据服务与数据库交互的项目 数据库很大 近 150 个具有依赖关系的表 该项目几年前开始 当时使用的是数据集 现在我们正在转向实体模型关系 由于我们添加了更多需要使用的表 该模型正在不断增长 这是管理这一切的正
  • 矩阵到数组 C#

    这将是转换方阵的最有效方法 例如 1 2 3 4 5 6 7 8 9 into 1 2 3 4 5 6 7 8 9 in c 我在做 int array2D new int 1 2 3 4 5 6 7 8 9 int array1D new
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959

随机推荐

  • QNX下获取系统信息的方法(cpu,内存,进程等等)

    有不少朋友问在qnx下如何获取内存及cpu占有率等等 想到两年前自己做过一个类似windows里的任务管理器的东东 里面有一部分就是获取内存 cpu 磁盘及进程信息的GUI程序 记得也美其名曰xxxTaskMan 把里面的关键代码写下来供兄
  • 关于C++的精度输出的解释。cout.precision()

    关于C 的精度输出的解释 cout precision 网上解释cout precision 是控制浮点数的输出精度的 这句话是没有错误的 但是程序又提示cout precision 是控制输出结果的小数点位数的 这句话是不对的 我们可以做
  • 腾讯云S5服务器2核2G、2核4G和4核8G五年租用时长

    腾讯云五年服务器来了 标准型S5云服务器 可选2核2G 2核4G和4核8G配置 一次性购买五年低至2折 免去xufei烦恼 腾讯云百科分享腾讯云5年服务器限制条件 云服务器配置 目录 腾讯云S5服务器CPU 五年机服务器配置 腾讯云标准型S
  • 转:win7系统下安装SQL Server 2005图文教程

    转载链接 https www cnblogs com icewee articles 2019783 html 转载内容如下 操作系统 Microsoft Windows 7 旗舰版 32位 数据库版本 SQL Server 2005 简体
  • TCP/IP五层模型——应用层(HTTP协议、简单的HTTP服务器)

    关于TCP IP模型的简单介绍 在TCP IP五层模型一文中有简单介绍 本文主要详细介绍其中的应用层相关内容 首先介绍一个定义 应用层协议 在网络版加法计算器一文中 我们介绍的实现网络版本的加法计算器有两种方法 其实无论是哪种方法 只要能够
  • python计算身体质量指数_如何用Python计算身体质量指数BMI,并给出胖瘦程度评价?...

    问 身体质量指数 Body Mass Index 简称 BMI 是目前国际上常用的衡量人体胖瘦程度以及是否健康的一个标准 BMI 值超标 意味着你必须减肥了 BMI 的计算公式是 BMI 体重 千克 身高的平方 米 按照中国人的体质特征 B
  • PPT 各行各业素材 10000套 讲解

    10000套各行各业PPT模板 提供下载 请扫二维码 PPT模板动态工作教育毕业答辩总结教师商务中国风清新简约素材 PPT模板简约风格中国动态模板静态模板唯美清新扁平论文答辩教育 PPT模板工作总结党政机关节日庆典儿童卡通教学课件岗位竞聘
  • 非参数统计吴喜之_看过许多统计教程,这篇是我最推荐的

    01 两组间比较 对于数值变量 首先进行正态性检验 如果各组均满足正态性且两组间方差相等 采用均数 标准差进行统计描述 采用t检验进行组间比较 否则采用中位数 四分位数间距 进行统计描述 采用非参数检验 Mann whitney检验 进行组
  • 【UE4】两个摄像头之间切换

    一 效果展示 二 操作步骤 创建两个摄像机 一个放在场景中 另一个放在Cube上 摄像机的细节面板中约束宽高比 Constrain Aspect Ratio 属性勾选后可以让两个摄像头之间的切换更流畅 但这不是必须的 会用到 include
  • 计算机中的指令跳转

    前言 在上一章中说了 高级语言为什么怎么样成为计算机能够读懂的语言 下面就来说说这些计算机能够读懂的语言中 他们究竟是如何设计的 CPU是如何执行指令的 在我们现实常用的CPU中 有几百亿的晶体管通过电路起来 在逻辑上我们可以认为CPU就是
  • [Pytorch系列-29]:神经网络基础 - 全连接浅层神经网络实现10分类手写数字识别

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 120607797 目录 前言 深度学习
  • Redis部署——主从备份(一)

    1 简单方式 利用Docker完成Redis主从备份 1 先搭建reids master实例 docker run d name redis master p 6379 6379 redis 4 0 9 2 在搭建redis slave实例
  • 代码随想录算法训练营day10

    Leetcode232 用栈实现队列 链接 232 用栈实现队列 力扣 LeetCode 首先大家要知道 队列的模式是先进先出 如下图所示 来自代码随想录 如图所示 这是就是我们题目要求的用两个栈实现队列 我们可以用第一个栈来储存当前队列出
  • Vue Element UI 之富文本图片上传服务器 + 图片地址插入富文本

    vue版本 vue cli3 插件 vue quill editor 插件增强模块 quill image extend module quill image extend module功能 提供图片上传到服j务器的功能 复制插入 显示上传
  • [xenclient 使用小结] [xen] vhdpartx的作用。

    对于硬盘映像vhd的操作 主要是用vhd utils 但是在 usr sbin 目录下 还发现一个 vhdpartx 的工具 看名字似乎和分区有关 但是又没说明 试着运行下 结果也没有任何的输出 貌似也没有任何的影响 网上也找不到任何的描述
  • Spring和mybatis整合

    一 Spring整合MyBatis 1 导入pom依赖 1 1 添加spring相关依赖 5 0 2 RELEASE spring core spring beans spring context spring orm spring tx
  • 管好【SD-WEBUI】中大量的模型:名称+预览图+备注+分组管理

    文章目录 零 前言 一 模型管理 1 1 模型名称 文件名 1 2 模型缩略图 1 3 模型备注文字 1 4 模型分组 子目录 1 5 模型详细信息 二 第二部分 三 第三部分 零 前言 本篇主要讲怎么管理大量的模型 比如模型不要大幅改名
  • 使用Struts2的JSON插件来实现JSON数据传递

    想要实现此功能第一步需要Struts2的核心架包 第二步需要struts2 json plugin 2 3 30架包 在lib文件夹下可以找到 还是借用上次的笔记 来继续写 这个时候我们就不需要用到Servlet了 要使用到Action 配
  • 自学python入门要买什么书?

    文章目录 1 Python编程 从入门到实践 2 Python编程快速上手 让繁琐工作自动化 3 Python基础教程 第3版 4 Python核心编程 第3版 5 Python 3网络爬虫开发实战 6 Python神经网络编程 自学pyt
  • g++编译详解

    g 编译详解 资料准备 为了方便演示和讲解 在这里提前准备好几个简单的文件 test cpp test h main cpp 文件内容如下 main cpp include test h int main int argc char arg