欢迎光临
我们一直在努力

【Linux系统编程】——【基础开发工具(3)】Cyber骇客视角带你掌握Linux中常用的编译器gcc/g++(内含GCC核心功能速查表)

在这里插入图片描述

⚡ CYBER_PROFILE ⚡

/// SYSTEM READY ///


[ WARNING ]: DETECTING HIGH ENERGY 🌊 🌉 🌊 心手合一 · 水到渠成

分隔符

>>> ACCESS TERMINAL <<<
[ 🦾 作者主页 ] [ 🔥 C++初阶 ]
[ 💾C++进阶 ] [ 📡 代码仓库 ]

—————————————Running Process: 100% | Latency: 0ms


索引与导读

  • 前言
    • 一、什么是编译器?
      • 1.1 编译器的工作流程
      • 1.2 编译型语言 VS 解释型语言
      • 1.3 编译器 VS IDE
    • 二、GCC/G++
      • 2.1)gcc/g++ 命令的核心行为特点
        • 1)GCC
        • 2)G++
      • 2.2)GCC核心认知:编译的四个阶段
        • 1)预处理
        • 2)编译
        • 3)汇编
        • 4)链接
        • 5)一键编译(合并四阶段)
        • 知识补充
    • 四、GCC核心功能速查表
    • 五、条件编译的逻辑
      • 1)条件编译的底层逻辑
      • 2)核心区别:条件编译 vs 运行时条件判断(`if-else`)
      • 3)关键逻辑:
    • 六、关键原理:静态链接和动态链接
      • 1)静态链接
      • 2)动态链接
    • 七、查看依赖的库文件
  • 💻结尾— 核心连接协议

前言

🌐 欢迎来到赛博骇客的逻辑矩阵

一个穿梭在 0 与 1 虚拟世界里的数字行者,热衷于用底层逻辑重构系统,用硬核代码致敬未来。在这里,我们不只聊代码,更聊如何掌控规则

在 Linux 的世界里,如果我们写下的 C/C++ 源代码是错综复杂的“神经元网络”,那么 GCC/G++ 编译器就是重塑这些网络、将其具象化为冰冷机器指令的“矩阵核心”

本篇将带你撕开高级语言的外衣,深入 Linux 系统的核心,彻底玩转 gcc 与 g++ 我们将从最基础的命令格式出发,一路拆解预处理、编译、汇编、链接的完整生命周期,并解锁那些能让代码性能飙升的硬核编译选项

一、什么是编译器?

我们在电脑上写的代码(如 C、C++、Java)是高级语言,充满了人类能看懂的单词和逻辑,但计算机的 CPU 非常“笨”,它只能听懂由 0 和 1 组成的机器语言(机器指令)

编译器的核心任务,就是把你写的代码(源码),翻译成计算机能直接执行的程序(可执行文件,如 Windows 上的 .exe)

1.1 编译器的工作流程

编译器内部的翻译过程非常精密,通常可以分为以下几个核心阶段:

  • 词法分析: 像读文章一样,把代码切分成一个个有意义的 Token,比如关键字 int、变量名、运算符等

  • 语法分析: 检查你的代码句法是否正确 它会把单词组合成一棵“语法树”,如果少写了分号或括号不匹配,在这个阶段就会报错

  • 语义分析: 检查代码的逻辑是否有错误,比如“把一个字符串赋值给整型变量”这种类型不匹配的问题

  • 中间代码生成与优化: 编译器会把代码先转换成一种中间形式,并开动脑筋帮你优化代码,让它运行得更快、占用的内存更少

  • 目标代码生成: 最后,把优化后的代码翻译成特定 CPU 能够识别的机器指令或汇编代码 在这里插入图片描述


1.2 编译型语言 VS 解释型语言

并不是所有的编程语言都需要传统的编译器

  • 根据运行方式的不同,语言主要分为两类:
特性编译型语言 (如 C, C++, Go)解释型语言 (如 Python, JavaScript)
工作原理 运行前,用编译器把所有代码一次性翻译成机器码文件 运行时,用解释器把代码一行一行地翻译并立即执行
执行速度 极快 因为运行前已经翻译好了,CPU 可以全速前进 较慢 因为边翻译边执行,多了一道转译的手续
跨平台性 编译出的程序只能在特定的系统和 CPU 上运行(换平台需要重新编译) 只要有对应平台的解释器,同一份代码到处都能运行

1.3 编译器 VS IDE

很多刚接触编程的朋友容易把 编译器 和 IDE(集成开发环境) 混淆:

  • 编译器:没有图形界面,它只是一个命令行工具(比如前面提到的 gcc、g++、clang) 你给它一个代码文件,它吐出一个可执行文件

  • IDE:比如 VS Code、Visual Studio、CLion 它是一个集成了“代码编辑器(打字用)+ 界面美化 + 编译器 + 调试器”的豪华大礼包 你在 IDE 里点一下“运行”按钮,实际上是 IDE 在幕后帮你调用了编译器


二、GCC/G++

现在的 GCC 全称是GNU 编译器套件

不仅仅支持 C 语言,还支持 C++、Objective-C、Fortran、Ada、Go 等多种编程语言


G++是 GCC 编译器套件中,专门为了方便开发 C++ 程序而封装的前端驱动程序


2.1)gcc/g++ 命令的核心行为特点

1)GCC
  • 根据后缀名区分语言:遇到 .c 后缀的文件,它会将其作为 C 语言代码进行预处理、编译和汇编;遇到 .cpp(或 .cc, .cxx)后缀的文件,它会自动调用 C++ 编译器来处理

  • 默认只链接 C 语言标准库:即使你用 gcc 编译了一个 .cpp 文件,在最后的“链接”阶段,gcc 默认只会去寻找 C 语言的标准库(libc) 这意味着如果你代码里用了 <iostream> 或 <stdio.h> 等 C++ 特性,链接时会报错(找不到符号)

  • 如何用 gcc 编译 C++:如果硬要用 gcc 编译和链接 C++ 程序,你需要手动指定链接 C++ 标准库:

gcc main.cpp -lstdc++ -o main


2)G++
  • 强制作为 C++ 处理:无论文件的后缀是 .c 还是 .cpp,g++ 都会将其视为 C++ 代码来编译 由于 C++ 的语法检查比 C 语言更严格,且命名修饰机制不同,用 g++ 编译纯 C 代码有时会因为类型转换等问题报错

  • 自动链接 C++ 标准库:使用 g++ 编译时,它会自动在链接阶段加入 C++ 标准库(libstdc++) 因此编译 C++ 程序时,命令极其简洁:

g++ main.cpp -o main

  • 宏定义:g++ 会自动在预处理阶段定义 __cplusplus 宏,这在 C/C++ 混编时用于条件编译非常重要

2.2)GCC核心认知:编译的四个阶段

GCC 编译 C/C++ 程序并非一步到位,而是分为预处理、编译、汇编、链接四个阶段,每个阶段完成特定任务,最终生成可执行文件

1)预处理
  • 核心任务

    1. 宏展开与替换:处理所有的 #define 指令

    2. 头文件展开:处理 #include <header> 或 #include "header"

    3. 条件编译逻辑裁剪:处理 #if、#ifdef、#ifndef、#elif、#else 和 #endif

    4. 剥离无用信息:删除注释 + 处理特殊符号

  • 关键选项 -E(仅执行预处理,停止后续编译)

  • 输出文件: .i后缀(预处理后的 C 文件)

  • 命令实操: vim hello.i 在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


2)编译
  • 核心任务:检查语法错误,将预处理后的代码(.i)转换为汇编语言代码

  • 关键选项:-s(仅执行编译,停止后续流程)

  • 输出文件:.s 后缀(汇编文件)

  • 命令实操: 在这里插入图片描述 在这里插入图片描述

  • 说明: 生成的汇编代码与 CPU 架构相关(如 x86_64、ARM) 在这里插入图片描述


3)汇编
  • 核心任务:将汇编代码(.s)转换为机器可识别的二进制目标文件

  • 关键选项:-c(仅执行汇编,生成目标文件)

  • 输出文件:.o 后缀(目标文件、二进制格式、不可执行)

  • 命令实操: 在这里插入图片描述 在这里插入图片描述

  • 说明: 目标文件包含机器指令,但缺少库依赖(如 printf 函数的实现),无法直接运行 在这里插入图片描述


4)链接
  • 核心任务:将目标文件(.o)与系统库、第三方库链接,生成可执行文件

  • 核心逻辑:解析未定义符号(如 printf),从库文件(如 libc.so)中找到实现并关联

  • 输出文件:默认名为 a.out(可通过 -o 指定名称)

  • 命令实操: 在这里插入图片描述 在这里插入图片描述


5)一键编译(合并四阶段)

在日常开发中,无需分步执行,GCC 支持一键自动完成编译的全部四个阶段 在这里插入图片描述


知识补充

我们会发现四个阶段的命令语句都会有一个-o

它的作用是:明确目标新文件的名称!

当我们执行:

gcc hello.c -o code.exe

本质是gcc自动依次执行 预处理→编译→汇编→链接,隐藏了中间文件


四、GCC核心功能速查表

选项功能描述使用示例
-E 只进行预处理,不编译、汇编和链接 gcc -E main.c -o main.i
-S 编译到汇编语言,不进行汇编和链接 gcc -S main.c -o main.s
-c 编译到目标代码,生成 .o 文件 gcc -c main.c -o main.o
-o 指定输出文件名 gcc main.c -o myapp
-static 使用静态链接生成可执行文件 gcc main.c -static -o app_static
-g 生成调试信息,供 GDB 使用 gcc -g main.c -o debug_app
-shared 尽量使用动态库,生成较小的文件 gcc -shared lib.c -o lib.so
-O0 不进行优化(编译速度最快) gcc -O0 main.c -o app
-O1 基本优化(默认级别) gcc -O1 main.c -o app
-O2 较多优化,平衡性能与编译时间 gcc -O2 main.c -o app
-O3 最高级别优化(可能增加代码大小) gcc -O3 main.c -o app
-w 禁止所有警告信息 gcc -w main.c -o app
-Wall 开启所有常见警告信息 gcc -Wall main.c -o app

五、条件编译的逻辑

1)条件编译的底层逻辑

要理解条件编译,首先需要了解程序从源代码到可执行文件的过程(以 C/C++ 为例):

源代码 (.c/.cpp)

预处理器 (Preprocessor)

预处理文件 (.i)

编译器 (Compiler)

汇编/目标文件 (.obj/.o)

\\text{源代码 (.c/.cpp)} \\xrightarrow{\\text{预处理器 (Preprocessor)}} \\text{预处理文件 (.i)} \\xrightarrow{\\text{编译器 (Compiler)}} \\text{汇编/目标文件 (.obj/.o)}

源代码 (.c/.cpp)预处理器 (Preprocessor)

预处理文件 (.i)编译器 (Compiler)

汇编/目标文件 (.obj/.o)

条件编译发生在 预处理阶段 预处理器是一个“文本复制与剪贴”的工具,它在真正的编译之前运行

1. 扫描指令:预处理器在代码中寻找以 # 开头的预处理指令(如 #ifdef, #ifndef, #if)

2. 逻辑判断:检查指定的宏是否已被定义,或者某个常量表达式是否为真

3. 文本裁剪:

  • 如果条件为真,保留该代码块,删除条件指令本身
  • 如果条件为假,直接将整段代码从源文件中抹去(变成空行或被剔除)

2)核心区别:条件编译 vs 运行时条件判断(if-else)

  • if-else 语句:所有代码都会被编译进可执行文件 在程序运行时,CPU 根据变量的值决定走哪个分支 会占用内存,且有运行开销

  • 条件编译(#if):在编译时就做出了决定 不满足条件的代码根本不会变成机器码,零运行时开销,完全不占用最终的程序体积

3)关键逻辑:

  • 为什么任何语言最后都要变成二进制 -> 因为编译器只认识它
  • 一定是先有语言再有编译器的 并且语言和编译器之间是一个自举的关系

六、关键原理:静态链接和动态链接

链接阶段的核心:关联库文件,GCC支持两种链接方式,各有优劣

在这里插入图片描述


为什么要有库:

  • 本质是程序员之间的一种协助方式
  • 我们可以站在巨人的肩膀上,使用那些大佬实现好的库
  • 提供必备的函数——提高开发效率

库在哪里?

  • 库命名规则:libXXX.{a/so} 在这里插入图片描述
库类型动态库静态库
Windows .dll .lib
Linux .so .a

1)静态链接

在实际项目开发中,代码量巨大,不可能将所有代码塞进一个源文件

  • 多文件并存: 项目由多个源文件(如多个 .c 文件)组成

  • 相互依赖: 这些文件并非孤立存在,一个源文件往往需要调用另一个源文件中定义的函数或变量

编译器处理这些文件时,遵循 独立编译 原则

  • 每个 .c 文件都会被单独编译,生成对应的目标文件(通常是 .o 或 .obj 文件)

  • 局限性:在这一阶段,虽然目标文件已经产生,但由于它们之间还未“打通”,程序尚无法运行

为了解决各文件间的依赖关系,必须进行 链接

  • 动作:将所有独立的目标文件(.o)整合在一起

  • 结果:形成一个完整的、可执行的程序

  • 定义:这种在编译后期将所有模块物理合并的过程,被称为静态链接

多个源文件 (.c)

独立编译

\\xrightarrow{\\text{独立编译}}

独立编译

多个目标文件 (.o)

静态链接

\\xrightarrow{\\text{静态链接}}

静态链接

一个可执行程序


  • 原理: 编译时将依赖的库文件(如 libc.a)全部复制到可执行文件中

  • 关键选项: -static;

  • 实操命令: gcc hello.c -o hello_static -static – 强制静态链接生成可执行文件(因为GCC默认是动态链接的)——强制静态链接生成可执行文件(因为GCC默认是动态链接的)

  • 安装命令:

    • yum:sudo yum install glibc-static libstdc++-static -y
    • apt:sudo apt install build-essential libstdc+±dev -y

ubuntu的静态库版本会随着ubuntu的版本变化,具体大家可以自行AI搜索一下最新版本

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


  • 如何验证是否真的 静态 ?

编译成功后,你可以用 file 命令检查一下生成的可执行文件:

file hello_static

如果输出里包含 statically linked,那就大功告成了!!!


  • 优点: 可执行文件不依赖系统库,移植性强
  • 缺点: 会让可执行程序变大,运行的时候,加载到内存:占据更多的内存空间,浪费资源!!!

2)动态链接

动态链接的核心逻辑:对静态链接缺陷的改进,改变了铁板一块的构建方式,实现了程序的模块化分离

  • 独立解耦:将程序按照功能拆分成多个相对独立的模块

  • 延迟整合:不再像静态链接那样在编译期就把所有模块硬塞进一个文件


动态链接最本质的区别在于链接发生的时机:

  • 静态链接:在编译/链接阶段完成,生成巨大的可执行文件

  • 动态链接:在程序启动或运行过程中,由操作系统按需将所需的模块(如 Linux 下的 .so 或 Windows 下的 .dll)链接在一起,拼装成完整的程序


  • 原理: 编译时仅记录库依赖(如 libc.so),程序运行时才加载库文件

  • 特点: GCC 默认采用动态链接

  • 实操命令: gcc hello.c -o hello_dynamic – 动态链接(默认)

如何验证是否真的动态?

file hello_dynamic

如果显示 dynamically linked,说明是动态链接

在这里插入图片描述


  • 优点: 文件体积小,库文件被多个程序共享,节省内存和磁盘空间

  • 缺点: 依赖系统中对应的库文件,一旦库文件缺失,所有程序都无法运行了


七、查看依赖的库文件

通过 ldd 可以查看可执行程序依赖的动态库

ldd hello_dynamic

在这里插入图片描述


💻结尾— 核心连接协议

警告: 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠


【📡】 建立深度链接: 关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。

【⚡】 能量过载分发: 执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。

【💾】 离线缓存核心: 将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。

【💬】 协议加密解密:评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。

【🛰️】 信号频率投票: 通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。


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

赞(0)
未经允许不得转载:171主机测评 » 【Linux系统编程】——【基础开发工具(3)】Cyber骇客视角带你掌握Linux中常用的编译器gcc/g++(内含GCC核心功能速查表)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址