007 Linux 开发工具(上)—— vim、解放sudo、gc+

🦄 个人主页: 小米里的大麦-CSDN博客
🎏 所属专栏: Linux_小米里的大麦的博客-CSDN博客
🎁 GitHub主页: 小米里的大麦的 GitHub
⚙️ 操作环境: Visual Studio 2022

在这里插入图片描述

文章目录

Linux 开发工具(上)

Linux 编辑器 —— vim

Vim 是一个强大、多模式的文本编辑器,具有高度的可定制性和丰富的功能。打开文件:在命令行中输入 vim filename,打开指定文件。Vim 从入门到牛逼 / Vim 从入门到牛逼(备用)

打开当前目录下的文件 code.c,使用命令:vim code.c,同时 支持相对路径和绝对路径 打开:vim home/code.c

Vim 的主要模式:
  • 命令模式(Normal Mode):命令模式是 Vim 的核心模式,几乎所有操作都从这里开始,启动 Vim 后,默认处于命令模式。在此模式下,键盘输入被解释为命令而非文本输入。只能使用方向键、删除、复制、粘贴等操作用来进行文本的导航、选择、删除、替换等。(按 i 进入插入模式,按 : 进入底行模式,按 Esc 从其他模式返回到命令模式)
  • 插入模式(Insert Mode):按下 i 键进入插入模式。插入模式下可以像普通文本编辑器一样输入文本。按 Esc 键返回命令模式。
  • 底行模式(Ex Mode 或 Bottom Line Mode):底行模式是 Vim 的强大功能之一,支持复杂的文件操作和配置。按下 shift+ (也就是 :) 键进入底行模式。在底行模式下可以执行文件保存、退出、查找替换等命令。例::wq 保存并退出 Vim。按 Esc 键返回命令模式。(注意: Ctrl+C 也可以退出底行模式并返回命令模式,尽量不要使用,可能会出现未知错误)
Vim 命令模式下的一些常用操作: 史上最全 Vim 快捷键键位图(入门到进阶)

导航

  • gg: 定位光标到最开始行。
  • shift+gG: 定位光标到最结尾行。
  • n+shift+gnG: 定位光标到任意行——跳到指定行号 n
  • shift+$: 定位光标到当前行的结尾。
  • shift+^: 定位光标到当前行的开始。
  • w, b: 光标按照单词进行行内移动,分别向右或向左移动一个单词。
  • h, j, k, l: 分别移动光标左、下、上、右。

文本编辑

  • (n)yy: 复制光标所在行或指定行数的内容。
  • (n)dd: 剪切(删除)当前行或指定行数的内容。
  • (n)p: 粘贴 n (次数)行到光标所在下一行。
  • u: 撤销上一步操作。
  • ctrl+r: 撤销之前的撤销(重做撤销的操作)。

其他操作

  • shift+~: 大小写转换。
  • (n)r: 对光标字符之后的 n 个字符进行批量化替换。
  • shift+R: 进入替换模式,对内容进行整体替换 → 第四种模式。
  • (n)x: 对光标后面的 n 个字符进行删除。
  • :w 保存,:w! 强制保存,:q 退出,:q! 强制退出,:wq! 强制退出并保存

这两个命令 (shift+$shift+^) 被称为“锚点”,因为它们将光标快速固定到行的特定位置——就像锚点固定物体的位置一样。在文本编辑器中,锚点 通常指的是可以用来快速定位或固定某个位置的标记。

为什么 vim 快捷键不像现在键盘的常规操作:主要是因为早期的键盘并不像现在这样有功能键(F1-F12)、方向键或数字小键盘。Vim 的许多快捷键都是为了适应这些限制而设计的。(可以查找一下那个时代的键盘 🤪)

Vim 多文件编辑与窗口管理

在 Vim 中,你可以同时打开多个文件,并使用 分割窗口 来进行并行编辑。

打开多个文件 —— 垂直分割窗口 (vs):

  • 作用:在当前窗口 右侧 打开一个新的窗口,并加载指定的文件。
  • 用法::vs file2.c。示例:先打开 file1.c,然后在 Vim 中输入 :vs file2.c,即可在右侧创建一个窗口并打开 file2.c

窗口切换 Ctrl+w w

  • 作用:在多个窗口之间切换光标位置。
  • 操作方式:按 Ctrl+w,再按 w,光标会跳到下一个窗口。如果有多个窗口,重复 Ctrl+w w 可以在所有窗口之间循环切换。

窗口内编辑光标在哪个窗口里面,就对哪一个窗口进行操作,你可以在当前窗口进行 插入、删除、复制等编辑操作,而不会影响其他窗口。

  • 水平分割窗口::sp file2.c
  • 调整窗口大小:Ctrl+w +(增加高度)、Ctrl+w -(减少高度)。

简单的 vim 配置(Vim 改装

Vim 的配置文件有两个主要位置,注意:一个用户配置一个 vim 文件,不会互相影响,不建议给 root 做配置,推荐用普通用户!

  1. 全局配置文件/etc/vimrc,对所有用户都生效。
  2. 用户私有配置文件:每个用户可以在其主目录下创建 .vimrc,仅对该用户生效。例如,/root/.vimrc 适用于 root 用户。

如何修改 .vimrc

  1. 切换到自己的用户目录(确保在自己的 home 目录下):cd ~
  2. 打开 .vimrc 文件(如果不存在,则创建):vim .vimrc

常用 Vim 配置

.vimrc 文件中添加以下配置:

  • 设置语法高亮:syntax on,作用: 开启 Vim 的语法高亮功能,使代码有不同颜色,提高可读性。
  • 显示行号:set nu,作用: 开启行号显示,在左侧显示每行的编号,方便定位代码行。
  • 设置缩进的空格数:set shiftwidth=4,作用: 设定每次缩进的空格数为 4。适用于代码自动缩进,提高代码可读性。

测试配置

修改 .vimrc 文件后,需要重新打开 Vim 或者在 Vim 内输入以下命令让配置生效::source ~/.vimrc 然后可以在 Vim 中测试:

  • 输入 :set nu? 检查行号是否开启。
  • 输入 :set shiftwidth? 检查缩进空格数。

这样就完成了简单的 Vim 配置! 🚀

使用插件(进阶)

Vim 支持插件扩展功能,要配置好看的 vim,原生的配置可能功能不全,可以选择安装插件来完善配置,保证用户是你要配置的用户。Vim 的插件管理工具如 Vim-Plug,一些常用的插件如 NERDTree、coc.nvim 等,以增强 Vim 的功能。

推荐使用 Vim-Plug 作为插件管理器。

  1. 安装 Vim-Plug

    curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
        https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
    
  2. 配置插件:在 .vimrc 中添加以下内容:

    call plug#begin('~/.vim/plugged')
    Plug '插件名称'  " 例如:Plug 'preservim/nerdtree'
    call plug#end()
    
  3. 安装插件
    在 Vim 中运行 :PlugInstall 安装配置的插件。


我的 vim 配置

安装方法:shell 中执行指令(想在哪个用户下让 vim 配置生效,就在哪个用户下执行这个指令。强烈 “不推荐” 直接在 root 下执行),需要按照提示输入 root 密码。您的 root 密码不会被上传,请放心输入。

curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh

注意:安装完成后一定要手动执行 source ~/.bashrc 或重启终端,这样配置才能生效! 说明:安装完成后,目录下会有一个名为 install.sh 的配套脚本,处于安全考虑,可以执行 mv install.sh .install.sh 进行隐藏。

默认缩进配置为 2 个字符,将 tab 键换成 4 个字符的方法:到达家目录下执行 vim .vimrc,大概在 50 行进行修改:

set tabstop=4       " 每个制表符占用 4 个空格
set softtabstop=4   " 插入制表符时插入 4 个空格
set shiftwidth=4    " 自动缩进和文本块操作时使用 4 个空格

卸载方法 在安装了 VimForCpp 的用户下执行:

bash ~/.VimForCpp/uninstall.sh

解放 sudo 权限

使用 root 账户执行命令:

vim /etc/sudoers

按下 i 键进入插入模式,找到大约第 100 行左右的位置(附近会有 root ALL=(ALL) ALL 的字眼),在其下方添加以下内容:

username    ALL=(ALL)    ALL
  • username:替换为你要授权的账户名。
  • ALL=(ALL):允许用户以任何用户身份执行命令。
  • ALL:允许执行所有命令。

Esc 键返回到默认模式,然后输入 :wq! 强制保存并退出。

验证:切换到该用户并测试 sudo 权限:

su - username
sudo ls /root

如果 sudo 配置正确,系统会提示输入密码,然后执行命令。


Linux 编译器 —— gcc / g++

背景知识回顾 —— C/C++ 程序的编译流程

C/C++ 程序从源代码到可执行文件需经历四个主要阶段:预处理 → 编译 → 汇编 → 链接。每个阶段由编译器(如 GCC、Clang 或 MSVC)逐步处理,最终生成可执行文件或库文件。

1. 预处理 (Preprocessing)

预处理是编译的第一步,主要处理源代码中的宏、注释、头文件等。预处理的输出是一个经过宏替换、去注释、头文件展开的中间代码。

主要工作:

  • 去除注释:删除代码中的注释部分。
  • 宏替换:替换代码中的宏定义(如 #define)。
  • 头文件展开:将 #include 指令包含的头文件内容插入到当前文件。
  • 条件编译:根据条件编译指令(如 #ifdef#endif)选择性地包含或排除代码。

输出:

  • 预处理后的文件通常是一个 .i 文件,它是源代码经过宏替换和头文件展开后的中间结果。

命令示例:

gcc -E temp.c -o temp.i		# 执行预处理后就停止,`-E` 选项表示只进行预处理,输出 `.i` 文件。

重要说明: 预处理后的文件仍然是 C 语言代码,因此可以说经过预处理后,程序依然是 C 语言程序。

2. 编译 (Compilation)

编译阶段的作用是将预处理后的代码转化为汇编代码。编译器会对代码进行语法分析、语义分析和优化,最终生成与平台架构相关的汇编代码。

主要工作:

  • 语法分析:检查代码是否符合语法规则。
  • 语义分析:检查变量类型、函数调用等语义是否正确。
  • 优化:对代码进行优化,使得生成的汇编代码更高效。

输出:

  • 编译后的文件是一个 .s 文件,包含了平台特定的汇编代码。

命令示例:

gcc -S temp.i -o temp.s		# 执行编译后就停止,`-S` 选项表示将预处理后的文件编译成汇编语言。
3. 汇编 (Assembly)

汇编阶段的作用是将汇编代码转换为机器可识别的二进制目标代码。汇编器会将汇编代码翻译为机器指令,生成可重定位目标文件。

主要工作:

  • 汇编:将汇编代码转换为目标代码,生成二进制文件。

输出:

  • 汇编后的文件是一个 .o(Linux)或 .obj(Windows)文件,这些文件是目标文件,包含机器指令,但尚未链接成最终的可执行文件。

命令示例:

gcc -c temp.s -o temp.o		# 执行汇编后停止,`-c` 选项表示只进行汇编,不进行链接,输出 `.o` 文件。

重要说明: 可重定位目标二进制文件,简称目标文件,.obj 文件不可以独立执行,虽然已经是二进制了,还需要经过链接才能执行!

传道解惑

在 Linux 中,可重定位目标二进制文件(Relocatable Object File)是一种 中间二进制文件,它是由编译器或汇编器生成的,但还 不能直接执行,必须经过 链接(Linking) 处理后才能成为最终的可执行文件或库文件。

Q1:什么是可重定位目标文件?

可重定位目标文件通常以 .o 结尾(Windows 上是 .obj),它包含:

  • 机器指令(Machine Instructions):程序的可执行代码,但未指定最终内存地址。
  • 符号表(Symbol Table):记录函数、变量等符号,供链接器解析。
  • 重定位信息(Relocation Information):用于在链接时调整地址。
  • 节(Section)结构:比如 .text(代码段)、.data(已初始化数据段)、.bss(未初始化数据段)等。

这些文件是 “可重定位” 的,因为它们的地址信息 尚未固定,而是由 链接器 在合并多个目标文件时决定最终的内存布局。

Q2:为什么需要可重定位目标文件?

如果编译器直接把 C/C++ 源代码编译成最终可执行程序,那么:

  • 无法将不同的模块(文件)合并,无法进行大规模项目开发。
  • 无法使用动态链接库(Shared Library),会导致程序体积庞大。
  • 无法延迟地址分配,不适用于操作系统的内存管理策略。

所以,编译器通常 生成 可重定位目标文件,再由 链接器 进行 地址调整和符号解析,最终得到可执行程序。

示例:生成可重定位目标文件

假设我们有一个简单的 C 代码 temp.c

#include <stdio.h>

void hello()
{
   printf("Hello, World!\n");
}

第一步:编译但不链接

gcc -c temp.c -o temp.o

这样会生成 temp.o,它是一个 可重定位目标文件,里面的 hello 还没有绑定到最终的地址。

第二步:使用 readelf 查看目标文件

readelf -h temp.o

部分输出:

Type:           REL (Relocatable file)
Machine:        x86-64

可以看到 TypeREL,表示它是一个 可重定位文件

Q3:什么时候会被最终链接?

可重定位目标文件 通常不会单独使用,它最终会:

  1. 静态链接(Static Linking):被 ld(链接器)合并到最终的可执行文件中。
  2. 动态链接(Dynamic Linking):与共享库(.so 文件)进行链接,在运行时加载。

例如:

gcc temp.o -o temp
./temp

这时 temp.o 经过链接后变成可执行文件 temp,可以直接运行。

4. 链接 (Linking)

链接阶段的作用是将多个目标文件(如 .o 文件)和库文件进行链接,生成最终的可执行文件或库文件。链接器会处理符号引用、重定位等工作,确保程序能够正确运行。

主要工作:

  • 符号解析:将不同目标文件中的符号(如函数名、变量名等)进行链接,确保各部分之间的正确引用。
  • 重定位:调整代码中地址的引用,使得目标文件能够在内存中正确加载。

输出:

  • 链接后的输出通常是一个可执行文件(如 a.outprogram.exe)或动态链接库(如 .so.dll 文件)。

命令示例:

gcc source.o -o program		# 默认链接 C 标准库,这条命令将目标文件 `temp.o` 链接为可执行文件 `program`。

为什么我们能在 Windows 或 Linux 上进行 C/C++ 开发?

在 Windows 和 Linux 上进行 C/C++ 开发是因为这些操作系统提供了支持编译和执行 C/C++ 代码的工具链。具体而言,需要安装开发环境(如 GCC、Visual Studio 等)以及相关的库文件和头文件。

1. 头文件和库文件

  • 头文件:定义了语言的核心功能、标准库以及外部库的接口(如 stdio.hstdlib.h)。
  • 库文件:包含预编译的二进制代码(如 libc.amsvcrt.dll),在链接阶段与用户代码结合。

2. 开发工具

  • 编译器:如 GCC(GNU Compiler Collection)、g++,以及 Visual Studio(VS)等 IDE,提供了代码编辑、编译、调试等功能。
  • 语言支持包安装开发工具时,会自动下载头文件和库。 例如,安装 VS 时选择“C++ 开发”,会附带 C++ 标准库(如 STL)和 Windows API 库。

3. 跨平台开发

  • 条件编译:通过宏(如 #ifdef __linux__)区分不同平台的代码逻辑。
  • 平台专属库:Linux 依赖 glibc,Windows 依赖 msvcrt.dll。开发者需调用标准接口或使用跨平台库(如 Qt、Boost)。
软件版本管理:社区版 vs 专业版

1. 代码维护策略

  • 单一代码库:企业无需维护多份代码,而是通过 条件编译 控制功能模块的启用或禁用。例如:

    #ifdef PROFESSIONAL_EDITION
      enableAdvancedFeatures();
    #endif
    
  • 编译参数控制:构建时通过宏定义(如 -DPROFESSIONAL_EDITION)选择版本。

2. 功能裁剪 —— 不需要维护两份代码,根据不同的编译条件,会裁剪掉社区版不需要的功能即可

  • 社区版:禁用部分高级功能(如性能分析工具、企业级加密)。
  • 专业版:包含完整功能,通过编译选项开启。

关键命令与文件类型总结
阶段命令选项输入文件输出文件文件内容
预处理-E.c.i展开后的 C 代码
编译-S.i.s汇编代码
汇编-c.s.o(Linux)目标二进制文件
链接.oa.out(默认)可执行文件
记忆法
  • ESC 键记忆法:键盘左上角的 ESC 键可以关联各个阶段的后缀:

    • .i -> 预处理(Preprocessing)
    • .s -> 汇编(Assembly)
    • .o -> 目标文件(Object file)
  • iso → 镜像文件后缀。 最终的可执行文件或库文件就是链接的结果。

    编译流程:
    .c 源文件 → 预处理 → 编译 → 汇编 → 链接 → 可执行文件
              (-E)     (-S)   (-c)    
            
    多文件编译例如:
    processBar.c → processBar.o ─┐
                                 ├→ processBar
    processBarmain.c → processBarmain.o ─┘
    

函数库的概念与作用

在 C/C++语言编程中,函数库(Library)允许开发者复用已有的代码,而不必每次都从头开始编写。函数库通常包含一组预先编写好的函数,这些函数可以被多个程序调用。通过使用函数库,开发者可以节省时间,减少重复劳动,并提高代码的可靠性和可维护性。

函数库的分类

函数库主要分为两种类型:静态库动态库

1. 静态库(Static Library)

  • 定义:静态库在编译链接时,会将库文件的代码全部加入到可执行文件中。因此,生成的可执行文件会比较大,但在运行时不再需要库文件。
  • 后缀名:静态库的后缀名通常为 .a(例如 libmylib.a)。
  • 优点:由于库代码被直接嵌入到可执行文件中,程序运行时不需要依赖外部的库文件,因此具有较好的独立性。
  • 缺点:生成的可执行文件较大,且如果多个程序使用相同的静态库,每个程序都会包含一份库代码的副本,导致内存浪费。

2. 动态库(Dynamic Library)

  • 定义:动态库在编译链接时并不会将库文件的代码加入到可执行文件中,而是在程序运行时由操作系统的运行时链接器动态加载。这样可以节省系统的开销。
  • 后缀名:动态库的后缀名通常为 .so(例如 libmylib.so)。
  • 优点:生成的可执行文件较小,多个程序可以共享同一个动态库,节省内存和磁盘空间。此外,动态库可以在不重新编译程序的情况下更新。
  • 缺点:程序运行时需要依赖外部的库文件,如果库文件丢失或版本不兼容,程序可能无法运行。
函数库的命名规则

函数库的命名通常遵循一定的规则,以便于识别和使用。常见的命名格式为:

libname.so.XXX
  • lib 是前缀,表示这是一个库文件。
  • name 是库的名称,例如 c 表示 C 标准库。
  • .so 表示这是一个动态库(静态库通常使用 .a)。
  • XXX 是版本号,表示库的版本。

例如,libc.so.6 是 C 标准库的动态库,版本号为 6。

函数库的使用

在 C 语言中,常用的函数库如 printf 函数的实现并没有直接包含在源代码中,也没有在头文件 stdio.h 中定义。那么,这些函数是如何被调用的呢?

1. 头文件的作用:头文件(如 stdio.h)中只包含了函数的声明(即函数原型),告诉编译器这些函数的存在及其参数和返回值的类型。头文件并不包含函数的实现。

2. 库文件的作用:函数的实现通常位于库文件中。例如,printf 函数的实现位于 C 标准库的动态库 libc.so.6 中。编译器在链接阶段会将这些库文件与代码结合起来,生成最终的可执行文件。

3. 链接过程

  • 静态链接:在编译时,静态库的代码会被直接嵌入到可执行文件中。生成的可执行文件不依赖外部的库文件。
  • 动态链接:在编译时,动态库的代码不会被嵌入到可执行文件中。程序运行时,动态链接器会根据预定义的路径规则去查找这些库文件。
函数库的作用 —— 不让我们做重复工作,站在巨人的肩膀上享受
  • 代码复用:避免重复造轮子,开发者可以直接使用封装好的函数,而不用自己实现复杂的功能。
  • 隐藏源码:共享库只提供 .so 文件,而不公开 .c 源文件,保护代码的知识产权。
  • 提高程序效率:通过动态链接的方式,多个程序可以 共享同一份库文件,减少内存占用,提高运行效率。

Linux 中的可执行文件链接方式:动态、静态与混合链接

在 Linux 中,gcc 编译形成可执行程序时,默认采用动态链接,即程序运行时依赖 .so(共享库),这样可以减少可执行文件的大小,并允许多个程序共享相同的库文件。然而,我们可以通过 静态链接混合链接 的方式来改变默认的链接行为。

小故事理解动静态链接

想象一下,你正在设计一款可以快速适应不同地形的汽车。为了实现这一目标,你决定使用一些通用的动态链接库,比如“越野轮子”、“城市轮胎”和“雪地链”。这些组件可以在不同的车型之间共享,并且可以根据需要在车辆启动时或行驶过程中即时替换。如果一个顾客今天想要一辆适合城市驾驶的汽车,你可以安装“城市轮胎”;如果明天他想去越野冒险,只需更换为“越野轮子”。这种灵活性就像动态链接(库),程序运行时按需加载,多个程序共享,节省内存,灵活高效。

另一方面,有些汽车组件一旦安装就不能轻易更改了,例如车身结构或者发动机。这些是根据特定需求量身定做的。当你选择了一种类型的发动机后,它就永久性地成为汽车的一部分,无法在不拆卸整个汽车的情况下进行更换。静态链接库就像发动机,在编译时整合进程序,成为其固定组成部分,提供稳定功能,但程序体积较大,独立运行。

1. 默认情况下,gcc 采用动态链接

当我们编译 C 语言程序:

gcc hello.c -o hello

默认情况下,gcc 优先链接动态库.so),并在执行时加载库文件。例如,标准 C 库 libc.so.6 是动态链接的:

ldd hello

示例输出:

linux-vdso.so.1 =>  (0x00007fffca9fe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2a4c3c9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2a4c788000)

这表示 hello 依赖 libc.so.6,而不是 libc.a(静态库)。

2. 强制使用静态链接(-static 选项)

如果想让程序采用 纯静态链接,可以使用 -static 选项:

gcc hello.c -o hello -static

这时,编译器会 优先查找并链接静态库(.a,把所有库代码嵌入可执行文件中,使其不再依赖外部 .so 文件。检查:

ldd hello

如果是纯静态链接,输出会是:

not a dynamic executable

这表明 hello 不依赖任何动态库

3. -static 的本质:改变库的优先级

使用 -static 选项时,必须保证所有依赖库的静态版本(.a)都存在,否则,编译会失败。如果没有静态库,但强制使用 -static 会报错!如果系统只有静态库,没有动态库,即使没有 -staticgcc 仍然会成功编译,并采用静态链接。因为它找不到 .so 文件,只能用 .a

  • 默认情况下gcc 优先选择动态库(.so
  • 加上 -staticgcc 强制使用静态库(.a,如果找不到静态库,则会报错。
  • 并且 -static 是“一次性”的,即:
    • 所有 需要的库都必须静态链接。
    • 不能部分使用动态库,部分使用静态库

如果 -static 选项下某个库没有 .a 文件,编译就会失败。

4. 混合链接(部分静态,部分动态)

虽然 -static 会让所有库都静态链接,但我们可以通过手动指定库的链接方式,实现 混合链接(部分库静态,部分库动态)。

让特定库使用静态链接:使用 -Wl,-Bstatic 指定部分库静态链接:

gcc hello.c -o hello -Wl,-Bstatic -lcustomlib -Wl,-Bdynamic -lc
  • -Wl,-Bstatic-lcustomlib 采用静态库 libcustomlib.a
  • -Wl,-Bdynamic-lc 采用动态库 libc.so(恢复默认动态链接)。
  • 这样就实现了 混合链接(部分库静态,部分库动态)。
5. -static 的优缺点
方式优点缺点
动态链接(默认)- 可执行文件小,多个程序共享库,节省磁盘、内存、网络等空间 - 库可以独立更新,不需重新编译程序- 依赖外部 .so 文件,库缺失时程序无法运行
静态链接(-static)- 程序可以独立运行,无需依赖库文件 - 适用于嵌入式系统或无共享库的环境- 可执行文件大(是动态的几十上百倍!),占用更多磁盘、内存、网络空间 - 无法通过库更新修复漏洞,需要重新编译
6. 推荐场景
场景建议链接方式
普通 Linux 应用默认动态链接(节省空间,便于更新)
需要独立运行的程序静态链接(-static,避免外部依赖
服务器软件混合链接,关键库静态,其余动态
嵌入式系统静态链接,减少依赖

Debug 与 Release 模式 & ELF 可执行文件格式

1. Debug 模式(调试模式)
  • 包含调试信息(如变量名、函数符号表)。
  • 没有优化,代码尽量保持源代码的执行逻辑,方便调试。
  • 可追踪调试,可以用 gdb(GNU Debugger)进行断点、变量查看等操作。

编译方式:

gcc -g hello.c -o hello_debug
  • -g 选项:生成调试信息,方便 gdb 进行调试。
  • hello_debug:可执行文件,但体积较大,因为包含了额外的调试信息。

调试方式:

gdb ./hello_debug		# 可以设置断点、查看变量值等。
2. Release 模式(发布模式)
  • 优化代码,提高执行效率
  • 去除调试信息,减小可执行文件大小
  • 适用于正式发布的程序,但不方便调试。

编译方式:

gcc -O2 hello.c -o hello_release
  • -O2(优化等级 2):让编译器优化代码,提高执行速度(-O3 可进一步优化)。
  • 没有 -g,不生成调试信息
3. ELF(Executable and Linkable Format,可执行文件格式)

在 Linux 下,可执行程序形成的时候,不是无顺的二进制构成,可执行程序有自己的二进制格式 —— ELF 格式。

  • 程序头部(Program Header):描述如何加载程序。
  • 代码段(.text):存放程序的可执行代码。
  • 数据段(.data、.bss):存放全局变量、静态变量等。
  • 符号表(仅 Debug 模式下存在):包含函数、变量等信息,方便调试器使用。

查看 ELF 结构:

readelf -h hello_debug   # 查看 ELF 头部信息
objdump -d hello_debug   # 反汇编可执行文件

总结

GCC/G++ 在 Linux 下的常用编译选项

选项作用示例
-o <文件名>指定输出的可执行文件名(gcc -o 输出文件 源文件gcc 源文件 -o 输出文件 均可,更推荐后者!)gcc hello.c -o hello
-c仅编译,不进行链接,生成 .o 目标文件gcc -c hello.c -o hello.o
-g生成调试信息,便于 gdb 调试gcc -g hello.c -o hello_debug
-O0不优化,适用于 Debuggcc -O0 hello.c -o hello
-O1基本优化,适用于一般调试gcc -O1 hello.c -o hello
-O2标准优化,提高性能,适用于 Releasegcc -O2 hello.c -o hello
-O3高级优化,可能影响可读性和调试gcc -O3 hello.c -o hello
-Wall启用所有常见警告gcc -Wall hello.c -o hello
-Wextra启用额外的警告信息gcc -Wall -Wextra hello.c -o hello
-Werror把所有警告当作错误gcc -Werror hello.c -o hello
-static进行 静态链接,不依赖 .sogcc -static hello.c -o hello
-shared生成 共享库(动态库) .so 文件gcc -shared -fPIC hello.c -o libhello.so
-fPIC生成位置无关代码(用于动态库)gcc -fPIC -c hello.c -o hello.o
-L<路径>指定库文件搜索路径gcc hello.c -L/usr/local/lib -lhello -o hello
-I<路径>指定头文件搜索路径gcc hello.c -I/usr/local/include -o hello
-l<库名>链接指定的库(默认搜索 /lib/usr/libgcc hello.c -lm -o hello(链接 libm.so 数学库)
-pthread支持多线程编译gcc hello.c -pthread -o hello
-std=<标准>指定 C 或 C++ 标准gcc -std=c99 hello.c -o hello
-D<宏定义>定义宏,等效于 #definegcc -DDEBUG hello.c -o hello
-E仅进行 预处理,输出 .i 文件gcc -E hello.c -o hello.i
-S仅进行 编译,输出汇编代码 .s 文件gcc -S hello.c -o hello.s
-v显示详细的编译过程gcc -v hello.c -o hello
--version显示 gcc/g++ 版本gcc --version

G++ 额外的选项

选项作用示例
-fno-rtti禁用运行时类型识别(RTTI)g++ -fno-rtti hello.cpp -o hello
-fno-exceptions禁用异常处理(try/catchg++ -fno-exceptions hello.cpp -o hello
-std=c++11使用 C++11 标准g++ -std=c++11 hello.cpp -o hello
-std=c++17使用 C++17 标准g++ -std=c++17 hello.cpp -o hello
-std=c++20使用 C++20 标准g++ -std=c++20 hello.cpp -o hello

gccg++ 的关系

  • g++ 可以使用 gcc 的全部选项,因为 g++ 本质上是 gcc 的一个前端,专门用于编译 C++ 代码。

  • gcc 既可以编译 C 也可以编译 C++,但默认 不会自动链接 C++ 标准库

  • g++ 默认 会自动链接 C++ 标准库(如 libstdc++),并开启 C++ 语法支持。

共勉

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米里的大麦

您的支持是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值