Linux基础开发工具知识点全面汇总

🌈 say-fall:个人主页 🚀 专栏:《手把手教你学会C++》 | 《系统深入Linux操作系统》 | 《数据结构与算法》 | 《小游戏与项目》 💪 格言:做好你自己,才能吸引更多人,与他们共赢,这才是最好的成长方式。
📝 前言
拿到一台全新的 Linux 服务器,你是否也曾手足无措?用户怎么建?软件怎么装?vim 怎么退出?gcc 编译报一堆错......这些问题几乎每个初学者都会遇到。
别慌------本文就是你的 Linux 开发工具速通指南。我会从用户管理 讲起,让你快速上手服务器配置;再介绍 软件包管理器,搞定"装软件"的问题;然后深入 vim 编辑器,让你摆脱只会用 i 进入编辑模式的困境;最后重点讲解 gcc/g++ 编译器 的编译全过程,吃透动态链接与静态链接的区别。四个模块循序渐进,帮你从"Linux 小白"蜕变为"能独立开发"。
通过本文,你将掌握:
| 技能 | 应用场景 |
|---|---|
| 用户管理(创建/删除/sudo权限) | 服务器初始化、多用户协作 |
| yum/apt 包管理操作 | 安装、卸载、配置国内镜像源 |
| vim 多模式编辑 | 高效编写和修改配置文件/代码 |
| gcc/g++ 编译流程 | 理解预处理→编译→汇编→链接四阶段 |
| 动态链接与静态链接 | 选择合适的链接方式,理解程序依赖 |
📌 前置知识: 熟悉 Linux 基本命令(如 ls、cd、pwd),能登录 Linux 系统即可。
文章目录
- Linux基础开发工具知识点全面汇总
-
- [📝 前言](#📝 前言)
- [一、👤 用户管理:创建与删除](#一、👤 用户管理:创建与删除)
-
- [1.1 创建用户](#1.1 创建用户)
-
- [1.1.1 基础创建命令](#1.1.1 基础创建命令)
- [1.1.2 设置密码](#1.1.2 设置密码)
- [1.1.3 添加 sudo 权限](#1.1.3 添加 sudo 权限)
- [1.2 一键创建用户(直接复制即用)](#1.2 一键创建用户(直接复制即用))
- [1.3 常用检查命令](#1.3 常用检查命令)
- [1.4 删除用户(清理干净)](#1.4 删除用户(清理干净))
- [1.5 用户管理速查表](#1.5 用户管理速查表)
- [二、📦 软件包管理器:yum 与 apt](#二、📦 软件包管理器:yum 与 apt)
-
- [2.1 什么是软件包与包管理器](#2.1 什么是软件包与包管理器)
- [2.2 Linux 软件生态](#2.2 Linux 软件生态)
-
- [2.2.1 软件分发流程](#2.2.1 软件分发流程)
- [2.2.2 操作系统的生态问题](#2.2.2 操作系统的生态问题)
- [2.2.3 软件包依赖的问题](#2.2.3 软件包依赖的问题)
- [2.3 国内镜像源](#2.3 国内镜像源)
-
- [2.3.1 软件源分类](#2.3.1 软件源分类)
- [2.4 yum/apt 常用操作](#2.4 yum/apt 常用操作)
-
- [2.4.1 查看软件包](#2.4.1 查看软件包)
- [2.4.2 安装软件](#2.4.2 安装软件)
- [2.4.3 卸载软件](#2.4.3 卸载软件)
- [2.4.4 注意事项](#2.4.4 注意事项)
- [2.5 安装源配置与更新](#2.5 安装源配置与更新)
-
- [2.5.1 安装源文件路径](#2.5.1 安装源文件路径)
- [2.5.2 CentOS7 更新阿里云源步骤](#2.5.2 CentOS7 更新阿里云源步骤)
- [2.5.3 Ubuntu 20.04 更新阿里云源步骤](#2.5.3 Ubuntu 20.04 更新阿里云源步骤)
- [三、✏️ 文本编辑器:vim](#三、✏️ 文本编辑器:vim)
-
- [3.1 vim 简介](#3.1 vim 简介)
- [3.2 vim 的核心模式与切换](#3.2 vim 的核心模式与切换)
- [3.3 命令模式常用命令](#3.3 命令模式常用命令)
-
- [3.3.1 光标移动](#3.3.1 光标移动)
- [3.3.2 删除操作](#3.3.2 删除操作)
- [3.3.3 复制与粘贴](#3.3.3 复制与粘贴)
- [3.3.4 替换与撤销](#3.3.4 替换与撤销)
- [3.3.5 更改操作](#3.3.5 更改操作)
- [3.3.6 跳至指定行](#3.3.6 跳至指定行)
- [3.4 底行模式常用命令](#3.4 底行模式常用命令)
-
- [3.4.1 保存与退出](#3.4.1 保存与退出)
- [3.4.2 行号与跳转](#3.4.2 行号与跳转)
- [3.4.3 查找与替换](#3.4.3 查找与替换)
- [3.5 vim 简单配置](#3.5 vim 简单配置)
-
- [3.5.1 配置文件位置](#3.5.1 配置文件位置)
- [3.5.2 常用配置选项](#3.5.2 常用配置选项)
- [3.6 vim 插件安装(了解)](#3.6 vim 插件安装(了解))
-
- [TagList 插件:代码结构导航](#TagList 插件:代码结构导航)
- [WinManager 插件:文件浏览器和窗口管理器](#WinManager 插件:文件浏览器和窗口管理器)
- [四、🔧 编译器:gcc/g++](#四、🔧 编译器:gcc/g++)
-
- [4.1 编译过程概述](#4.1 编译过程概述)
- [4.2 编译四阶段详解](#4.2 编译四阶段详解)
-
- [4.2.1 预处理(Preprocessing)](#4.2.1 预处理(Preprocessing))
- [4.2.2 编译(Compilation)](#4.2.2 编译(Compilation))
- [4.2.3 汇编(Assembly)](#4.2.3 汇编(Assembly))
- [4.2.4 链接(Linking)](#4.2.4 链接(Linking))
- [4.3 动态链接与静态链接](#4.3 动态链接与静态链接)
-
- [4.3.1 静态链接](#4.3.1 静态链接)
- [4.3.2 动态链接](#4.3.2 动态链接)
- [4.3.3 库文件的概念](#4.3.3 库文件的概念)
- [4.3.4 查看动态库依赖](#4.3.4 查看动态库依赖)
- [4.3.5 静态链接编译](#4.3.5 静态链接编译)
- [4.3.6 动态链接与静态链接对比](#4.3.6 动态链接与静态链接对比)
- [4.4 gcc 常用选项汇总](#4.4 gcc 常用选项汇总)
- [五、🤔 几个思考题](#五、🤔 几个思考题)
-
- [1️⃣ 创建用户时,如果不指定 `-s /bin/bash`,会发生什么?](#1️⃣ 创建用户时,如果不指定
-s /bin/bash,会发生什么?) - [2️⃣ yum 和 apt 能同时安装多个软件包吗?如果遇到依赖冲突怎么办?](#2️⃣ yum 和 apt 能同时安装多个软件包吗?如果遇到依赖冲突怎么办?)
- [3️⃣ vim 中 `/` 和 `?` 查找有何区别?](#3️⃣ vim 中
/和?查找有何区别?) - [4️⃣ gcc 编译时报错 "undefined reference to 'xxx'",大概率是哪个阶段出了问题?](#4️⃣ gcc 编译时报错 "undefined reference to 'xxx'",大概率是哪个阶段出了问题?)
- [1️⃣ 创建用户时,如果不指定 `-s /bin/bash`,会发生什么?](#1️⃣ 创建用户时,如果不指定
一、👤 用户管理:创建与删除
在 Linux 服务器上,用户管理是最基础也是最重要的操作之一。无论是日常开发还是团队协作,都需要创建合适的用户并分配相应权限。
1.1 创建用户
1.1.1 基础创建命令
bash
# 创建用户,同时创建家目录,指定 shell 为 bash
useradd -m -s /bin/bash 用户名
参数说明:
| 参数 | 作用 |
|---|---|
-m |
同时创建用户的家目录(/home/用户名) |
-s /bin/bash |
指定用户的默认 shell 为 bash |
⚠️ 注意: 如果不指定
-s,某些系统可能默认使用/bin/sh,功能较弱;甚至可能出现拼写错误如basj,导致用户无法正常登录。
1.1.2 设置密码
bash
passwd 用户名
执行后会提示输入两次密码,密码不会回显。
1.1.3 添加 sudo 权限
在 CentOS/RHEL 系统中,wheel 组的用户拥有 sudo 权限:
bash
# 将用户加入 wheel 组,获得管理员权限
usermod -aG wheel 用户名
参数说明:
| 参数 | 作用 |
|---|---|
-a |
追加模式,不覆盖已有的附加组 |
-G wheel |
将用户加入 wheel 组 |
💡 Ubuntu/Debian 系统中使用
sudo组:usermod -aG sudo 用户名
1.2 一键创建用户(直接复制即用)
把下面命令中的 xxx 换成你要的用户名即可:
bash
useradd -m -s /bin/bash xxx && passwd xxx && usermod -aG wheel xxx
这条命令一次性完成:创建用户 → 设置密码 → 添加 sudo 权限。
💡 为什么用
&&连接? 只有前一条命令成功(返回码为 0),才会执行下一条。这样避免用户创建失败却执行后续操作。
1.3 常用检查命令
bash
# 查看用户是否存在、所属组
id 用户名
# 查看 shell 是否配置正确
grep 用户名 /etc/passwd
# 切换到新用户登录
su - 用户名
id 命令输出示例:
uid=1001(testuser) gid=1001(testuser) groups=1001(testuser),10(wheel)
uid:用户 IDgid:主组 IDgroups:用户所属的所有组
1.4 删除用户(清理干净)
bash
# 删除用户,同时删除其家目录
userdel -r 用户名
| 参数 | 作用 |
|---|---|
-r |
同时删除用户的家目录和邮件池 |
⚠️ 注意: 删除用户前,确保该用户没有运行的进程,否则会报错:
bash# 强制终止用户的所有进程 pkill -u 用户名
1.5 用户管理速查表
| 操作 | 命令 |
|---|---|
| 创建用户(带家目录+bash) | useradd -m -s /bin/bash 用户名 |
| 设置密码 | passwd 用户名 |
| 添加 sudo 权限(CentOS) | usermod -aG wheel 用户名 |
| 添加 sudo 权限(Ubuntu) | usermod -aG sudo 用户名 |
| 查看用户信息 | id 用户名 |
| 切换用户 | su - 用户名 |
| 删除用户(含家目录) | userdel -r 用户名 |
| 一键创建+设密+加sudo | useradd -m -s /bin/bash xxx && passwd xxx && usermod -aG wheel xxx |
二、📦 软件包管理器:yum 与 apt
2.1 什么是软件包与包管理器
在 Linux 中,传统的软件安装方式是下载源代码并手动编译------光是想想 configure、make、make install 这一套流程就让人头疼,而且依赖关系处理不好分分钟报错。
所以 Linux 引入了软件包 的概念:把常用软件提前编译好,打成一个包(类似 Windows 的 .exe 安装程序),再通过包管理器自动完成查找、下载、安装、卸载和依赖解决。
类比记忆: 软件包 ≈ 手机 App,包管理器 ≈ 应用商店。
| 发行版 | 包管理器 | 示例 |
|---|---|---|
| CentOS / RHEL / Fedora | yum(Yellow dog Updater, Modified) | yum install gcc |
| Ubuntu / Debian | apt(Advanced Package Tool) | apt install gcc |
2.2 Linux 软件生态
2.2.1 软件分发流程
开发者编写源代码 → 社区维护者编译打包 → 上传到软件包服务器
↓
用户通过包管理器下载安装
2.2.2 操作系统的生态问题
操作系统的好坏评估------生态问题:一个操作系统是否有丰富的软件生态,直接决定了用户的使用体验。这也是为什么有人愿意免费提供软件并维护软件源------良好的生态能吸引更多用户和开发者,形成良性循环。
2.2.3 软件包依赖的问题
软件之间存在复杂的依赖关系:A 依赖 B,B 依赖 C......手动处理这些依赖关系非常繁琐。包管理器的核心价值就是自动解决依赖关系。
2.3 国内镜像源
国外服务器访问速度慢,甚至完全不可达。国内高校和企业将国外软件包同步到国内服务器,大幅提升下载速度。
常用镜像源:
| 镜像 | 地址 |
|---|---|
| 阿里云 | https://developer.aliyun.com/mirror/ |
| 清华大学 | https://mirrors.tuna.tsinghua.edu.cn/ |
| 中国科学技术大学 | http://mirrors.ustc.edu.cn/ |
| 北京交通大学 | https://mirror.bjtu.edu.cn/ |
| 上海交通大学 | https://ftp.sjtu.edu.cn/ |
| 网易开源镜像站 | http://mirrors.163.com/ |
| 中国科学院软件研究所 | http://mirror.iscas.ac.cn/ |
2.3.1 软件源分类
- 标准源(base):包含系统稳定的核心软件包,默认已配置
- 扩展源(epel):包含更多第三方软件包,CentOS 需要手动安装:
bash
# CentOS 安装 epel 扩展源
sudo yum install -y epel-release
2.4 yum/apt 常用操作
2.4.1 查看软件包
bash
# CentOS:列出所有软件包,筛选 lrzsz(Windows和Linux文件互传工具)
yum list | grep lrzsz
# Ubuntu:搜索 lrzsz 软件包
apt search lrzsz
# Ubuntu:查看软件包详细信息
apt show lrzsz
💡 提示: 软件包名称格式为
主版本号.次版本号.源程序发行号-软件包发行号.主机平台.cpu架构。例如
lrzsz.x86_64 0.12.20-36.el7:
x86_64:64位系统包;i686:32位系统包el7:对应 CentOS7/RHEL7;el6:对应 CentOS6/RHEL6base:软件源名称,类似"小米应用商店"、"华为应用商店"的概念
2.4.2 安装软件
bash
# CentOS 安装 lrzsz
sudo yum install -y lrzsz
# Ubuntu 安装 lrzsz
sudo apt install -y lrzsz
⚠️
-y参数:自动确认安装,无需手动输入y。安装需要 root 权限,所以必须加sudo。yum/apt 会自动找到都有哪些软件包需要下载,出现 "complete" 字样或中间未出现报错,说明安装完成。
2.4.3 卸载软件
bash
# CentOS 卸载 lrzsz
sudo yum remove -y lrzsz
# Ubuntu 卸载 lrzsz
sudo apt remove -y lrzsz
2.4.4 注意事项
- 安装软件时由于需要向系统目录中写入内容,一般需要 sudo 或者切到 root 账户下才能完成
- yum/apt 安装软件只能一个装完了再装另一个。正在 yum/apt 安装一个软件的过程中,如果再尝试用 yum/apt 安装另外一个软件,yum/apt 会报错
- 如果 yum / apt 报错,请自行百度排查
- 关于 yum/apt 的所有操作必须保证主机(虚拟机)网络畅通 ,可通过
ping www.baidu.com验证
2.5 安装源配置与更新
2.5.1 安装源文件路径
| 系统 | 安装源路径 |
|---|---|
| CentOS | /etc/yum.repos.d/ 目录下的 .repo 文件 |
| Ubuntu | /etc/apt/sources.list(标准源)和 /etc/apt/sources.list.d/(扩展源) |
CentOS 的 yum.repos.d/ 目录下常见文件:
CentOS-Base.repo:标准源配置文件epel.repo:扩展源配置文件
2.5.2 CentOS7 更新阿里云源步骤
第一步:备份原有源
bash
sudo mkdir /etc/yum.repos.d/backup
sudo mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/
第二步:下载阿里云源配置文件
bash
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
第三步:清理旧缓存并生成新缓存
bash
sudo yum clean all
sudo yum makecache
第四步(可选):更新系统
bash
sudo yum update
第五步:验证 Yum 源
bash
sudo yum repolist
2.5.3 Ubuntu 20.04 更新阿里云源步骤
第一步:备份原有源
bash
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
第二步:下载阿里云源配置文件
bash
sudo wget -O /etc/apt/sources.list http://mirrors.aliyun.com/repo/ubuntu-sources.list
第三步:确保配置文件适配 Ubuntu 20.04(focal)
打开文件检查,确保所有行包含 focal,例如:
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
💡 如果下载的文件不是 focal 版本,需要手动将所有
bionic、jammy等替换为focal。
第四步:更新 APT 缓存
bash
sudo apt update
第五步:验证 APT 源
bash
sudo apt policy
三、✏️ 文本编辑器:vim
3.1 vim 简介
vim 是 vi 的升级版本,是 Linux 下最常用的多模式文本编辑器。它不仅兼容 vi 的所有指令,还有一些新特性:语法高亮、可视化操作等,不仅可以在终端运行,也可以运行于 x window、mac os、windows。
与普通编辑器不同,vim 启动后默认进入命令模式,而不是直接可编辑状态------这是它看起来"难用"的根本原因。只要搞懂三种核心模式的切换,vim 就能大幅提升你的编辑效率。
💡 vi/vim 的区别简单点来说,它们都是多模式编辑器,不同的是 vim 是 vi 的升级版本。我们课堂上统一按照 vim 来进行讲解。
3.2 vim 的核心模式与切换
vim 有 12 种模式,最核心的是以下三种,模式之间的切换关系如下:
┌─────────────────────────────────────────┐
│ 命令模式 (Normal mode) │ ← 默认入口
│ h左 j下 k上 l右 / 删除 / 复制 / 粘贴 │
└──────────┬──────────────────────────────┘
│ 按 i / a / o
▼
┌─────────────────────────────────────────┐
│ 插入模式 (Insert mode) │
│ 可以正常输入文字 │
└──────────┬──────────────────────────────┘
│ 按 ESC
▼
┌─────────────────────────────────────────┐
│ 底行模式 (Last line mode) │
│ :w 保存 / :q 退出 / :wq 保存退出 │
└─────────────────────────────────────────┘
| 操作 | 按键 |
|---|---|
| 命令模式 → 插入模式 | i(从光标当前位置开始输入)、a(从光标下一位置开始输入)、o(插入新的一行,从行首开始输入) |
| 插入模式 → 命令模式 | ESC |
| 命令模式 → 底行模式 | shift + :(即输入 :) |
| 底行模式 → 命令模式 | ESC |
💡 入门小技巧: 不知道当前在哪个模式?狂按几下
ESC肯定回到命令模式。要查看所有模式:打开 vim,底行模式输入
:help vim-modes,一共有 12 种模式:six BASIC modes 和 six ADDITIONAL modes。
3.3 命令模式常用命令
命令模式(Normal mode)是 vim 的核心,用于控制光标移动、删除、复制、粘贴等操作。
3.3.1 光标移动
bash
# 基础移动(左手食指放在 h 上,h左边 j下 k上 l右边)
h # 左移一格
j # 下移一格
k # 上移一格
l # 右移一格
# 快速跳转
gg # 跳转到文件开头
G # 跳转到文件末尾(shift+g)
nG # 跳转到第 n 行(如 15G 跳转到第 15 行)
^ # 跳转到光标所在行的行首(第一个非空白字符)
$ # 跳转到光标所在行的行尾
w # 跳转到下一个单词的开头(word)
e # 跳转到下一个单词的结尾(end)
b # 跳转到上一个单词的开头(back)
#l # 光标移到该行的第 # 个位置,如 5l, 56l
# 翻页
Ctrl+f # 向前翻一页(forward)
Ctrl+b # 向后翻一页(backward)
Ctrl+d # 向前翻半页(down)
Ctrl+u # 向后翻半页(up)
💡 记忆技巧:
hjkl四个键从左到右依次对应 ←↓↑→,是 vim 最经典的设计。初学者不习惯很正常,用多了就熟练了。
3.3.2 删除操作
bash
x # 删除光标所在位置的一个字符
nx # 删除光标所在位置及后面 n 个字符(如 6x 删除 6 个字符)
X # 删除光标所在位置前面的一个字符
nX # 删除光标所在位置前面 n 个字符
dd # 删除光标所在行(剪切)
ndd # 从光标所在行开始删除 n 行(如 3dd 删除 3 行)
💡
dd实际上是"剪切",配合粘贴命令p可以实现移动行的效果。
3.3.3 复制与粘贴
bash
yw # 复制光标所在处到字尾的字符(yank word)
nyw # 复制 n 个单词
yy # 复制光标所在行
nyy # 复制从光标所在行开始的 n 行(如 6yy 表示拷贝往下数 6 行)
p # 将缓冲区内的字符粘贴到光标所在位置(paste)
⚠️ 注意: 所有与"y"有关的复制命令都必须与"p"配合才能完成复制与粘贴功能。
3.3.4 替换与撤销
bash
r # 替换光标所在处的一个字符(replace one)
R # 进入替换模式,替换光标所到之处的所有字符,直到按 ESC
u # 撤销上一次操作,可多次撤销(undo)
Ctrl+r # 恢复撤销的操作(redo)
3.3.5 更改操作
bash
cw # 更改光标所在处的字到字尾(change word)
cnw # 更改 n 个字(如 c3w 表示更改 3 个字)
3.3.6 跳至指定行
bash
Ctrl+g # 列出光标所在行的行号
nG # 如 15G,表示移动光标至文章的第 15 行首
3.4 底行模式常用命令
在使用底行模式之前,请记住先按 ESC 键确定您已经处于命令模式,再按 : 冒号即可进入底行模式。
3.4.1 保存与退出
bash
:w # 保存当前文件(write)
:wq # 保存并退出(write + quit)
:q # 退出(文件未修改时有效)
:q! # 不保存强制退出
⚠️ 危险操作:
:q!不保存强制退出,修改会丢失!一般建议离开时搭配w一起使用,这样在退出的时候还可以保存文件。
3.4.2 行号与跳转
bash
:set nu # 显示行号(number)
:set nonu # 取消显示行号
:n # 跳转到第 n 行(如 :15 跳转到第 15 行)
3.4.3 查找与替换
bash
:/关键字 # 向下查找关键字,按 n 跳转到下一个匹配项
:?关键字 # 向上查找关键字,按 n 跳转到上一个匹配项
💡 问题:
/和?查找有何区别?/是向下查找,?是向上查找,n都是继续查找(方向与原查找方向一致)。
3.5 vim 简单配置
3.5.1 配置文件位置
| 配置文件 | 路径 | 作用域 |
|---|---|---|
| 系统配置 | /etc/vimrc |
对所有用户生效 |
| 用户配置 | ~/.vimrc |
仅对当前用户生效(不存在则创建) |
💡 切换用户成为自己执行
su,进入自己的主工作目录执行cd ~,打开自己目录下的.vimrc文件执行vim .vimrc
3.5.2 常用配置选项
常用配置选项(写入 ~/.vimrc):
vim
" 设置语法高亮
syntax on
" 显示行号
set nu
" 设置缩进的空格数为 4
set shiftwidth=4
3.6 vim 插件安装(了解)
要配置好看的 vim,原生的配置可能功能不全,可以选择安装插件来完善配置。
TagList 插件:代码结构导航
bash
# 1. 下载 taglist_xx.zip,解压完成
# 2. 将解压出来的 doc 的内容放到 ~/.vim/doc
# 3. 将解压出来的 plugin 下的内容拷贝到 ~/.vim/plugin
# 4. 在 ~/.vimrc 中添加配置:
let Tlist_Show_One_File=1
let Tlist_Exit_OnlyWindow=1
let Tlist_Use_Right_Window=1
WinManager 插件:文件浏览器和窗口管理器
bash
# 1. 下载 winmanager.zip(2.X 版本以上)
# 2. 解压,将 doc 的内容放到 ~/.vim/doc,plugin 的内容拷贝到 ~/.vim/plugin
# 3. 在 ~/.vimrc 中添加配置:
let g:winManagerWindowLayout='FileExplorer|TagList'
nmap wm :WMToggle<cr>
然后重启 vim,打开 ~/XXX.c 或 ~/XXX.cpp,在 normal 状态下输入 wm,将看到效果。
💡 其他手册,请执行
vimtutor命令。
四、🔧 编译器:gcc/g++
4.1 编译过程概述
gcc(GNU Compiler Collection)是 Linux 下最主流的 C 编译器,g++ 则是 C++ 编译器。两者都将源代码翻译成机器可执行的文件,但背后经历了一个复杂的多阶段过程。
C/C++ 代码从源代码到可执行文件,需要经过预处理 → 编译 → 汇编 → 链接四个阶段:
hello.c →(预处理)→ hello.i →(编译)→ hello.s →(汇编)→ hello.o →(链接)→ hello (可执行文件)
4.2 编译四阶段详解
以 hello.c 为例,逐步演示每个阶段:
4.2.1 预处理(Preprocessing)
作用: 处理所有以 # 开头的预处理指令,包括宏定义、文件包含、条件编译、去注释等。
bash
gcc -E hello.c -o hello.i
-E:让 gcc 在预处理结束后停止编译(不进行后续步骤)-o:指定输出文件,.i是预处理后的 C 原始程序
💡 预处理阶段会发生什么?
#include <stdio.h>被替换为 stdio.h 的全部内容#define MAX 100被替换为具体的值#if/#ifdef/#else/#endif条件编译根据条件决定保留哪些代码- 所有注释被删除
4.2.2 编译(Compilation)
作用: gcc 首先要检查代码的规范性、是否有语法错误等,在检查无误后,把代码翻译成汇编语言。
bash
gcc -S hello.i -o hello.s
-S:只进行编译(不对汇编代码进行汇编),生成.s汇编文件
💡 如果代码有语法错误,gcc 会在编译阶段报错,这个阶段发现错误最多。
4.2.3 汇编(Assembly)
作用: 把编译阶段生成的 .s 文件转成目标文件。
bash
gcc -c hello.s -o hello.o
-c:将汇编代码编译成目标代码,生成.o二进制目标代码.o文件已经是二进制了,但尚不能执行------因为可能引用了其他文件中的函数
4.2.4 链接(Linking)
作用: 在成功编译之后,就进入了链接阶段。
bash
gcc hello.o -o hello
生成的 hello 就是可执行文件,通过 ./hello 即可运行。
💡 为什么链接这一步必不可少? 比如你调用了
printf函数,但printf的实现不在hello.o里------它在 C 标准库(libc.so)中。链接器负责把这些"外部引用"找到并连接起来,程序才能正常运行。
4.3 动态链接与静态链接
在实际开发中,不可能将所有代码放在一个源文件中,所以会出现多个源文件,而且多个源文件之间不是独立的,而会存在多种依赖关系。每个 *.c 文件会形成一个 *.o 文件,为了满足依赖关系,则需要将这些源文件产生的目标文件进行链接。
4.3.1 静态链接
原理: 把库文件的代码全部加入到可执行文件中。
静态链接的缺点:
- 浪费空间:每个可执行程序中对所有需要的目标文件都要有一份副本。如果多个程序都调用了
printf()函数,则这多个程序中都含有printf.o - 更新比较困难:每当库函数的代码修改了,就需要重新进行编译链接形成可执行程序
静态链接的优点:
- 运行速度快:在可执行程序中已经具备了所有执行程序所需要的任何东西
4.3.2 动态链接
原理: 把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。
动态链接其实远比静态链接要常用得多。
动态链接的优点:
- 节省空间:多个程序可以共享同一个库文件
- 更新方便:库文件更新后无需重新编译程序
4.3.3 库文件的概念
库文件是预先编译好的代码集合,提供了常用功能(如 printf 函数),程序员无需重复造轮子。
我们的 C 程序中,并没有定义"printf"的函数实现,且在预编译中包含的"stdio.h"中也只有该函数的声明,而没有定义函数的实现。系统把这些函数实现都做到名为 libc.so.6 的库文件中去了,gcc 会到系统默认的搜索路径 /usr/lib 下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数"printf"了。
| 类型 | Linux 后缀 | Windows 后缀 |
|---|---|---|
| 动态库(运行时加载) | .so |
.dll |
| 静态库(编译时复制) | .a |
.lib |
4.3.4 查看动态库依赖
bash
# ldd 命令用于打印程序或者库文件所依赖的共享库列表
ldd hello
输出示例:
linux-vdso.so.1 => (0x00007fffeb1ab000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff776af5000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff776ec3000)
💡
libc.so.6就是 C 标准库,printf函数就在里面。linux-vdso.so.1是 Linux 内核提供的虚拟动态库。
4.3.5 静态链接编译
bash
gcc hello.c -o hello_static -static
⚠️ 注意: 一般云服务器,C/C++ 的静态库并没有安装,需要先安装:
bash# CentOS sudo yum install glibc-static libstdc++-static -y
4.3.6 动态链接与静态链接对比
| 特性 | 动态链接 | 静态链接 |
|---|---|---|
| 可执行文件大小 | 小 | 大 |
| 运行时依赖库 | 需要 | 不需要 |
| 磁盘空间占用 | 少(共享库) | 多(每个程序一份) |
| 更新难度 | 易(只需更新库) | 难(需重新编译所有程序) |
| 运行速度 | 稍慢 | 快 |
💡 gcc 默认生成的二进制程序是动态链接的,可以通过
file命令验证。
4.4 gcc 常用选项汇总
| 选项 | 作用 |
|---|---|
-E |
只激活预处理,不生成文件,需要重定向到输出文件 |
-S |
编译到汇编语言,不进行汇编和链接 |
-c |
编译到目标代码 |
-o <file> |
文件输出到 file |
-static |
采用静态链接方式生成可执行文件 |
-g |
生成调试信息,GNU 调试器可利用该信息 |
-shared |
尽量使用动态库,生成文件比较小 |
-O0 / -O1 / -O2 / -O3 |
编译器优化选项的 4 个级别,-O0 无优化,-O3 优化级别最高 |
-w |
不生成任何警告信息 |
-Wall |
生成所有警告信息 |
💡 实战建议: 平时开发建议用
-Wall开启所有警告,能帮助发现潜在 bug;发布版本用-O2或-O3优化性能。
五、🤔 几个思考题
学完本文,来试试回答这些问题:
1️⃣ 创建用户时,如果不指定 -s /bin/bash,会发生什么?
答: 系统会使用默认 shell(通常是 /bin/sh)。/bin/sh 功能较弱,不支持很多 bash 特性(如命令补全、历史记录等),使用体验很差。甚至可能因为拼写错误(如写成 basj)导致用户无法正常登录。
💡 正确做法: 创建用户时始终指定
-s /bin/bash,或使用一键命令确保 shell 正确。
2️⃣ yum 和 apt 能同时安装多个软件包吗?如果遇到依赖冲突怎么办?
答: 不能。yum/apt 同一时间只能处理一个软件包的安装操作,同时安装多个会报错。
如果遇到依赖冲突,可以尝试以下方法:
- CentOS:
sudo yum clean all清理缓存后重试 - Ubuntu:
sudo apt clean清理缓存后重试 - 检查是否有版本冲突的手动安装包需要先卸载
- 查看具体依赖报错信息,有时需要先安装冲突的依赖包
3️⃣ vim 中 / 和 ? 查找有何区别?
答:
/关键字:向下查找关键字?关键字:向上查找关键字- 按
n都是继续查找,方向与原查找方向一致
4️⃣ gcc 编译时报错 "undefined reference to 'xxx'",大概率是哪个阶段出了问题?
答: 这是链接阶段 的问题。编译阶段只检查语法,汇编阶段生成目标文件,但链接阶段负责把所有目标文件和库文件"拼"在一起。"undefined reference"表示链接器在所有目标文件和默认库中都找不到 xxx 的定义。
常见原因:
- 忘记链接某个
.o文件:gcc main.o func.o -o app - 忘记链接数学库:
gcc calc.c -o calc -lm(-lm链接 libm.so) - 库文件路径不对:用
-L指定库搜索路径
✅ 本节完...
📝 作者:say-fall | 编辑:say-fall | 🌟 原创不易,如果对你有帮助,记得 👍 点赞 + ⭐ 收藏 哦!