【Linux】编译器gcc和g++与调试器gdb的使用

2023-11-01

一、Linux编译器-gcc/g++

1.程序运行的四个阶段

我们知道,一个程序被运行起来之后需要经历四个阶段然后才变成一个可执行的程序,他们分别为:预处理,编译,汇编和链接。下面我们将介绍这四个阶段的操作,测试代码如下:

#include <stdio.h>
// 宏定义
#define N 10

int main()
{
	// 测试注释
	// printf("hello 1\n");
	// printf("hello 2\n");
	// printf("hello 3\n");
	// printf("hello 4\n");
	printf("hello 5\n");
	printf("hello 6\n");
	printf("hello 7\n");
	printf("hello 8\n");
	printf("hello 9\n");
	printf("hello 10\n");

	// 测试宏
	printf("%d\n", N);

	// 测试条件编译
#ifdef SHOW
	printf("hello SHOW\n");
#else
	printf("hello DEFAULT\n");
#endif

	return 0;
}

1.1 预处理

预处理也叫预编译,程序在预处理阶段会完成如下操作:

展开头文件,将头文件中的代码拷贝到当前代码中

删除注释

执行条件编译

完成#define定义的符号,宏的替换以及删除

在Linux下我们可以通过如下指令就可以得到经过预处理之后的代码

gcc -E test.c -o test.i

gcc : 表示使用gcc编译器来编译该代码

-E :表示让代码完成预处理之后就停下来

test.c : 我们要编译的代码

test.i:预处理产生的文件一般以.i为后缀

-o test.i :用于指明临时文件的名称(test.i),经过预处理之后它会形成一个名称为test.i的临时文件,而不是输出到终端上

在这里插入图片描述

在这里插入图片描述

我们可以看到,经过预处理之后,头文件stdio.h中的内容全部会被拷贝到test.i中,所以test.i一个有800多行,我们注释的内容也全部被删除,宏定义被替换,条件编译被执行

1.2 编译

程序在编译阶段会完成如下操作:

语法分析

词法分析

语义分析

符号汇总

我们通过如下指令来获取程序编译之后的代码:

gcc -S test.i -o test.s

-s:表示让代码在完成编译之后停下来,不再继续之后后面的过程

编译产生的文件一般以.s为后缀

在这里插入图片描述

在这里插入图片描述

我们可以看到,编译阶段会将高级语言转换成汇编语言

1.3 汇编

汇编阶段会将编译阶段生成的汇编代码转换成计算机可以识别的二进制目标代码,其中生成的.o文件被称为可重定向的二进制目标文件

我们通过如下指令来获取程序汇编之后的代码:

gcc -c test.o -o test.s

-c:表示让代码在完成汇编之后停下来,不再继续往后执行

汇编产生的文件一般以.s为后缀

在这里插入图片描述

在这里插入图片描述

这样我们就得到了二进制目标文件,但我们使用一般的文本编辑器打开发现是一堆我们看不见的符号,我们可以通过如下指令以指定的格式打开它(默认是八进制):

od test.o

在这里插入图片描述

1.4 链接

程序在链接的过程中会完成如下操作:

1.合并段表,编译器会把汇编阶段生成的多个文件中相同格式的数据合并到一起,最终形成一个.exe文件

2.符号表的合并和重定位,符号表的合并是编译器会把在汇编阶段生成的多个符号表合成一个符号表,重定位是当同一个符号出现在两个符号表中时,编译器会选取其中一个有效的地址相关的一个,舍弃另外一个

在Linux中,链接我们直接使用gcc即可,如果我们没有指定将生成的临时文件的话,默认会将结果存放在a.out中

gcc test.o //默认
gcc test.c -o test.out // 保存到test.out文件中

在这里插入图片描述

在这里插入图片描述

我们最后通过链接得到的文件称为可执行程序,它里面保存的也是计算机能够识别的二进制指令

我们两个文件都可以输出最终的结果:

在这里插入图片描述

虽然我们将gcc编译代码分为预处理,编译,汇编和链接,但是我们在日常生活中运行代码时,直接使用"gcc test.c -o test.out"或者"gcc test.c"即可,这里分为四个阶段是为了让我们更好的理解程序的运行过程

2.链接方式与函数库

2.1 动态链接与静态链接

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现“printf”函数的呢?

最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用

这是因为我们在编写代码的时候,除了自己实现的函数外,比如printf函数我们会去调用函数库中的代码,但是stdio.h里只有函数的声明,没有函数的实现。此外,程序在预处理,编译和汇编阶段处理的都是我们自己的代码,只有在链接的时候,库函数的实现才会和我们的代码关联起来(符号表的重定位),所以,链接的本质是我们在调用库函数的时候如何与标准库相关联的问题,程序一共有两种链接方式:动态链接和静态链接

动态链接

动态链接是指执行代码的时候,如果遇到库函数调用就跳转到动态库中对应的函数的定义的地方,然后执行该函数,执行完毕后再跳转到原程序处然后继续往后执行,它的优点就是形成的可执行程序小,缺点是受动态库变动(删除,升级等)的影响

静态链接

静态链接是直接将程序需要使用的库函数都从对应的静态库中拷贝一份到原程序中,它的优点是不受静态库(删除,升级等)的影响,缺点是形成的可执行程序大

2.2 动态库与静态库

函数库是一些事先写好的,用于给别人使用的函数的集合,函数库分为静态库和动态库

静态库

静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。在Linux在其后缀名一般为“.a“,在windows在的后缀名为”.lib“

动态库

动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。在Linux下动态库一般后缀名为“.so”,在windows下其后缀名为“.dll”

【注意】

1.动态链接必须使用动态库,静态链接必须使用静态库,即进行动态链接时只能跳转到动态库中对应的函数实现处,进行静态链接时只能拷贝静态库中的函数实现

2.gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,gcc默认生成的二进制程序,是动态链接的.原因如下:

  • 如果使用静态链接,那么程序形成的可执行程序大,此时,不仅仅占用磁盘的空间大,其被运行时加载到内存所占用的空间也会很大,而我们的内容有限,运行过大的程序可能会导致卡死无法运行
  • 虽然动态链接受库函数变动的影响,但是库函数一般是很少变动的,即使变动也会兼容以前的版本,所以对程序的影响不大
  • 在Linux下,我们可以使用"file"指令来辨别文件的类型,使用"ldd"来打印或查看程序运行所 需要的共享库

在这里插入图片描述

Linux一般会自动安装C语言动态库,因为Linux下的大多数指令以及我们默认使用的gcc编译得到的可执行程序都是动态链接的,依赖于C动态库,但是C静态库,C++静态库可能就需要我们自己进行安装。我们可以使用如下命令来安装C和C++的静态库:

sudo yum install -y glibc-static
sudo yum install -y libstdc++-static

我们可以使用"-static"选项来指定程序使用静态方式进行链接:

在这里插入图片描述

我们可以看到,使用静态链接方式形成的可执行程序比动态链接方式形成的可执行程序的要大100倍左右,即一个动态链接只有1M的文件,使用静态链接方式形成的可执行程序就有几百M,两者之间相差非常大,所以Linux默认使用静态链接的方式

3.gcc/g++的使用

在Linux在,我们可以使用如下指令来安装gcc和g++:

sudo yum install -y gcc
sudo yum install -y gcc-g++ libstdc++-devel

gcc与g++的使用如下:

-E 只激活预处理,在完成预处理后停下来,不再往后继续执行,生成以.i为后缀的文件

-S 在完成编译后停下来,不再继续往后执行,生成的文件以.o为后缀 编译到汇编语言不进行汇编和链接

-c 在完成汇编之后停下来,不再继续往后执行,生成以.o为后缀的文件 编译到目标代码

-o 将输出到终端的内容保存到指定的文件中

-static 此选项对生成的文件采用静态链接

-g 以debug的方式发布软件,生成调试信息。GNU 调试器可利用该信息。

-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.

-O0-O1-O2-O3 编译器的优化选项的4个级别

-O0表示没有优化,这是默认的编译选项

-O1为缺省值,编译器会在不花费太多编译时间的同时试图生成更快更小的代码。这些优化是非常基础的,但一般这些任务肯定能顺利完成

-O2 -O2会比-O1启用多一些标记。设置了-O2后,编译器会试图提高代码性能而不会增大体积和大量占用的编译时间

-O3优化级别最高 在O2的基础上进行更多的优化,用这个选项会延长编译代码的时间,并且在使用gcc4.x的系统里不应全局启用

-w 不生成任何警告信息。

-Wall 生成所有警告信息。

二、Linux调试器–gdb

1.debug与release

在windows中使用VS的时候我们知道,程序的发布方式一共有两种-debug模式和release模式,其中debug模式是给程序员使用的,其中包含调试的各种信息,程序员可以根据这些调试信息对程序进行修改和完善,而release模式则是给用户使用的,它不包含调试信息,因为用户不负责也不关心对程序进行调试。

Linux gcc/g++出来的二进制程序,默认是release模式,如果我们要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项,标识以debug方式发布

#include <stdio.h>
#include <time.h>

int AddToVal(int from,int to)
{
    int sum=0;
    int i=0;
    for(i=from;i<=to;++i)                                                                     {
        sum+=i;
    }
    return sum;
}
void Print(int sum)
{
    long long timestamp=time(NULL);
    printf("result=%d,timestamp:%lld\n",sum,timestamp);
}
int main()
{
    int sum=AddToVal(0,100);
    Print(sum);
    return 0;
}

在这里插入图片描述

在这里插入图片描述

我们可以发现,以debug方式发布的和以release方式发布的程序无论是在大小,程序内部的包含的相关的调试信息,还是debug模式下是否具有调试样例都是由明显区别的

2.gdb 的使用

当我们使用-g选项的时候就可以得到以debug模式发布的可执行程序,然后我们就可以使用gdb对其进行调试了:

gdb的安装

在Linux在,我们可以使用如下指令来安装gdb:

sudo yum install -y gdb

gdb调试的常见选项如下:

list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。我们第一次使用l显示源代码后,我们下一次再使用l或者下一次使用enter时,它会接着上一次的位置接着向下显示

list/l 函数名:列出某个函数的源代码。

r或run:运行程序。调试运行,如果程序中有断点,则在断点处停下来,如果没有,则直接 将程序执行完毕,相当于VS中的F5

n 或 next:单条执行。逐过程调试,相当于VS的F10

s或step:进入函数调用 逐语句调试,相当于VS的F11

break(b) 行号:在某一行设置断点,相当于VS的F9

break 函数名:在某个函数开头设置断点

info break :查看断点信息。

finish:执行到当前函数返回,然后停下来等待命令 把当前函数执行完毕

print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数

p 变量:打印变量值。

set var:修改变量的值

continue(或c):从当前位置开始连续而非单步执行程序 运行至下一个断点处停下来,如果断点所在行不是一条语句,比如"{" “}” 或者空行,那么它会继续往后继续执行到有效行处

run(或r):从开始连续而非单步执行程序

delete breakpoints:删除所有断点

delete breakpoints n:删除序号为n的断点 (d 断点编号),每个断点都有自己的编号,我们删除断点时需要指明对应的断点编号

disable breakpoints:禁用断点

enable breakpoints:启用断点

info(或i) breakpoints:参看当前设置了哪些断点

display 变量名:跟踪查看一个变量,每次停下来都显示它的值

undisplay:取消对先前设置的那些变量的跟踪

until X行号:跳至X行

breaktrace(或bt):查看各级函数调用及参数

info(i) locals:查看当前栈帧局部变量的值

quit:退出gdb

我们重点掌握下面的即可:

在这里插入图片描述

指令演示

l行号显示源代码

在这里插入图片描述

l 函数显示该函数的源代码

在这里插入图片描述

r 运行程序

在这里插入图片描述

b 行号打断点,info b查看断点,d 断点编号 删除断点

在这里插入图片描述

r 调试运行

在这里插入图片描述

n 逐过程调试,s逐语句调试

在这里插入图片描述

c 运行至下一个断点处停下

在这里插入图片描述

bt 查看调用堆栈

在这里插入图片描述

p 变量,查看变量值,display 跟踪查看变量 undisplay取消跟踪

在这里插入图片描述

finish 把当前函数运行完,q 退出gdb1

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux】编译器gcc和g++与调试器gdb的使用 的相关文章

  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template

随机推荐

  • Java的mkdir()与mkdirs()引发的悲剧---关于java的mkdir()方法无法创建文件目录问题

    昨晚深夜在做项目的文件上传 在上传之前要先判断指定的文件目录是否存在 如果不存在就先创建改目录 因为之前已经做过类似的功能了 所以就把判断文件目录以及创建的代码直接copy过来了 然而很郁闷的是 一模一样的代码 这回却遇到一个特别奇葩的问题
  • 【python】CliffWalking悬崖寻路问题

    强化学习 简介 gym库 CliffWalking SARSA Q learning 示例 SARSA Q learning 简介 机器学习 监督学习 非监督学习 强化学习 模仿人类和动物的试错机制进行学习 智能体与环境交互 根据当前的环境
  • 数据结构面试常见问题总结

    数据结构面试常见问题总结 写在前面 本文记录了一些数据结构面试常见问题 本意用于考研复试 以下面试题为网上整理的问题以及自己加入的一些问题 答案仅供参考 Q 数据结构三要素 A 逻辑结构 物理结构 数据运算 Q 数组与链表有什么区别 A 数
  • innovus中常用命令整理

    restoreDesign load 之前的db list property type 列举出相应的属性 get property 得到相应的object的属性 get pin 获取pin get port 获取port ecoRoute
  • 2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆

    2023开学礼山东财经大学 乡村振兴战略下传统村落文化旅游设计 许少辉新财经图书馆
  • adworld攻防世界 reverse asong

    asong 攻防世界 reverse 进阶区 asong 题目文件 https www jianguoyun com p DQ3g5b4QiNbmBxjX fQC 访问密码 AgV9Sh 主要是集中我们常见的处理方式的整合 注意一个对于ou
  • 进程间通信---管道通信

    进程间通信为什么有那么多不同的方法 资源的不同 所以通信的方式不同 想要获取管道资源 就需要用管道来通信 想要获取消息队列资源 就需要用消息队列来通信 如上所示 一个进程就是一个PCB PCB中的file struct有三个默认文件描述符
  • 【转】 如何提高自己的acm个人能力

    转载自 简单de数字 最终编辑 fading code by zfy0701 本来以为HNU的huicpc035和我一样退役了 后来听说他组成了新的footman队 于是又关注了下他 035体现了两个我觉得非常重要的品质 1 刻苦的训练 2
  • vmtools的安装和使用

    介绍 vmtools工具是在虚拟系统和主机系统进行共享文件夹的工具 1 用root用户登录CentOS后删除桌面的光驱 2 点击菜单栏的虚拟机 gt 安装VMwareTools 3 安装结果如下所示 4 打开VMwareTools 复制VM
  • 【python】Something is wrong with the numpy installation

    2020年2月5日 0次阅读 共448个字 0条评论 0人点赞 QueenDekimZ COCO API windows下安装COCO API时 python setup py build ext install 出现报错 ImportEr
  • Unity中UI框架的使用3-主界面中的弹窗和关闭

    效果图 在主页面点击排位赛按钮 就会弹出图2中的一个弹窗 再点击弹窗右上角的关闭按钮 就会关闭弹窗 回到图3的效果 方法 1 将PopUp这个面板添加到UIPanelType cs文件中 并且将其名称和路径添加到UIPanelType js
  • Python高级函数1:使用 map()、reduce()、filter()、zip() 和 enumerate() 简化代码

    Python高级函数1 使用 map reduce filter zip和 enumerate 简化代码 1 原理 1 1 map 函数 1 2 reduce 函数 1 3 filter 函数 1 4 zip 函数 1 5 enumerat
  • 在分布式环境下标准支付流程的梳理

    支付流程图的梳理 https www processon com diagraming 61a18a895653bb136f893ecc 提交订单 当用户点击立即购买或者提交订单的这个时候数据库就会记录一笔订单 此项业务主要是用到了rabb
  • Android 设置ListView不可滚动 及在ScrollView中不可滚动的设置

    转载请注明出处 http blog csdn net androiddevelop article details 38815493 希望得到的效果是ListView不能滚动 但是最大的问题在与ListView Item还必有点击事件 如果
  • 2023华为OD机试真题【区间交叠/贪心算法】【Python Java C++】

    题目描述 给定坐标轴上的一组线段 线段的起点和终点均为整数并且长度不小于1 请你从中找到最少数量的线段 这些线段可以覆盖住所有线段 输入描述 第一行输入为所有线段的数量 不超过10000 后面每行表示一条线段 格式为 x y x和y 分别表
  • vscode因网络下载失败的问题

    复制出失败的下载链接 https az764295 vo msecnd net stable d045a5eda657f4d7b676dedbfa7aab8207f8a075 VSCodeUserSetup x64 1 72 2 exe 将
  • 多任务视频推荐方案,百度工程师实战经验分享

    推荐系统的应用场景非常广泛 比如非常火爆的短视频推荐 电商平台商品推荐 搜索推荐等 但是你知道吗 短视频APP在向你展示一个你感兴趣的视频之前 通常既要预测你对这个视频是否感兴趣 又要预测你会看多久 点赞还是不点赞 会不会偷偷收藏起来下次接
  • 迁移学习matlab

    迁移学习是一种机器学习技术 它可以利用已有的模型和数据来加速新模型的训练 在Matlab中实现迁移学习 需要先选定一个预训练的模型 然后使用该模型的权重来初始化新模型 最后对新模型进行微调以适应特定的任务 Matlab中有一些已经预先训练好
  • 常用函数式接口

    常用函数式接口 JDK 8 中重要的函数接口 接口 参数 返回 中文 示例 Supplier None T 提供者 工厂方法创建对象 Consumer T void 消费者 输出一个值 Predicate T boolean 谓语 顾名思义
  • 【Linux】编译器gcc和g++与调试器gdb的使用

    文章目录 一 Linux编译器 gcc g 1 程序运行的四个阶段 1 1 预处理 1 2 编译 1 3 汇编 1 4 链接 2 链接方式与函数库 2 1 动态链接与静态链接 2 2 动态库与静态库 3 gcc g 的使用 二 Linux调