Ubuntu 18.04离线安装gcc-7与默认切换避坑指南 简介面向 Ubuntu 18.04 环境下需要离线部署 C/C 编译工具链的开发与运维人员这份压缩包提供了一套可直接落地的 GCC 7 安装来源重点解决无外网或受限网络机器上依赖缺失、版本不匹配导致的编译环境搭建困难。包内共 25 个文件以 24 个 deb 安装包和 1 个 sh 脚本为主deb 覆盖编译器本体、cpp 预处理组件、binutils 二进制工具及 libgcc、libasan、libgomp 等运行与开发依赖脚本可用于辅助执行安装流程压缩包约 20.05MB体积适中便于拷贝到目标主机。已有 3205 人学习下载说明其在同类离线部署场景中具有一定参考价值。读者可据此在隔离环境中补齐 GCC 7 及相关依赖减少逐个查找软件包的重复劳动并为后续驱动编译、项目构建和依赖排错提供可复用的包集合与安装思路。1. 别急着下载Ubuntu 18.04 的 gcc7 多数是被换掉了并非没装一台跑着 Ubuntu 18.04 的内网机器编译某相机 SDK 的驱动时冒出cc1: error: unrecognized command line option -Wno-format-truncation敲gcc --version一看是 5.4.0。前一个同事为了跑别的老工程装了 gcc-5把/usr/bin/gcc直接指了过去谁也没记着改回来。这就是 gcc7 在 ubuntu18.04 上最典型的一幕系统本来自带 7.5.0可你手上这台机器的 gcc 早就不是你以为了。于是大家开始搜 gcc7 ubuntu18.04 dpkg 的装法方向没错但动手前得先分清自己是“没有 gcc7”还是“gcc7 被顶掉了”。这篇记录的是后者也是离线内网机最难受的那种不能联网 apt只能拿一组 deb 包靠 dpkg 一层层铺回去。适合手上有包、机器不通外网、又必须把 gcc-7 固定成默认编译器的人。2. 装前摸底ubuntu18.04 上的 gcc-7 版本、依赖和架构怎么确认装 gcc-7 这件事翻车最多的不是命令写错是没确认现场。同一句sudo dpkg -i gcc-7_*.deb在两种机器上结果完全相反一种是干净的最小化系统缺一堆运行库dpkg 直接停在依赖检查另一种是被人来回折腾过的开发机包全在只是链接指错了地方你装第二遍等于白装。所以第 2 章不写安装只写怎么在动手前把这三件事问清楚当前真实的编译器是谁、这套系统里哪些 gcc 包已经在、目标机器的架构和运行库能不能接住 gcc-7。2.1 三条命令摸清现状你缺的是 gcc-7 还是别的先别管压缩包先把“现在到底是谁在干活”钉死。很多人习惯which gcc看一眼就完事这不够因为which只看 PATH 里第一个命中的看不到被 alternatives 藏起来的候选也看不到/usr/local/bin里那份从源码编译的野版本。# 一次性看清 PATH 里所有同名命令的位置和顺序 type -a gcc g cc c # 解析软链接的最终落点这一步才看得到真实二进制 readlink -f $(command -v gcc) # 输出版本和目标三元组确认架构别装错 gcc -dumpversion gcc -dumpmachinetype -a会按 PATH 顺序把所有命中列出来如果/usr/local/bin/gcc排在最前面那你后面怎么折腾 alternatives 都没用它优先级天然高于/usr/bin。readlink -f用来剥掉/usr/bin/gcc - /etc/alternatives/gcc - /usr/bin/gcc-7这层层跳转直接告诉你最终落在哪个版本上。-dumpversion只吐主版本号比--version好解析-dumpmachine输出x86_64-linux-gnu这类三元组是判断“该装 amd64 还是 arm64 包”的第一手依据。接着看包的状态这一步比看gcc --version更有信息量# 只看 gcc 相关的包前缀两个字母是 dpkg 的状态位 dpkg -l | grep -E ^(ii|rc|iU|iF)[[:space:]](gcc|g\\|cpp|libstdc|libgcc)状态位里ii表示已装好rc表示卸载了但配置文件还留着iU是解包了没配置iF是配置失败。后三种都是“装过但坏了”的痕迹出现iU或iF时你直接重装同一个包大概率还是失败得先走一遍sudo dpkg --configure -a把半成品收尾。还有一个信号值得留意如果gcc-5和gcc-7同时是ii那基本可以确认这台机器之前被人手动切过版本你真正要做的就是把链接切回来而不是重新装一遍。2.2 版本对照18.04 上 gcc-5 / gcc-6 / gcc-7 / gcc-8 到底该选哪个Ubuntu 18.04 官方源里同时躺着 5、6、7、8 四个版本选错一个后续能烦你半个月。编译器包名常见版本C 头文件目录典型场景gcc-5gcc-5 / g-55.4.0/usr/include/c/516.04 遗留工程、老内核模块gcc-6gcc-6 / g-66.4.0/usr/include/c/6极少用多为过渡gcc-7gcc-7 / g-77.5.0/usr/include/c/718.04 默认4.15 内核模块gcc-8gcc-8 / g-88.4.0/usr/include/c/8需要更完整的 C17 支持这张表里最容易忽略的是“头文件目录”这一列。g-7真正提供的头文件在/usr/include/c/7这个目录是libstdc-7-dev包铺出来的不是gcc-7包。所以只装gcc-7不装libstdc-7-devgcc能用、g一编译 C 就找不到iostream这是最经典的半成品状态。版本选择上我一般这么定目标机器是 18.04、要编译的东西带着内核模块或者老驱动的锁 gcc-7跟着系统默认走兼容性最稳纯应用层、代码里用了std::optional、std::string_view、结构化绑定这些的gcc-7 也够-stdc17在 7.5 上支持度已经不错除非踩到某些库的实现缺陷才上 gcc-8。至于 gcc-5除非你接手的工程明确写了“必须 5.4”否则别往回退退回去以后GLIBCXX的符号版本会一路往下掉连带影响运行期。还有一层是运行库。18.04 的libstdc6实际是 8.x 系列对外导出的GLIBCXX_3.4.25是上限所以用 gcc-7 编出来的程序在这台机器上跑没有任何问题。真正出问题的是反过来gcc-8 编的程序拿到只装了旧libstdc6的机器上跑报GLIBCXX_3.4.25 not found。你把编译版本降回 7这个坑自然就绕开了这也是很多团队宁愿锁 7 不愿意升 8 的真实原因。2.3 环境清单架构、libc、多架构和磁盘动手之前把这四项抄在纸上后面每条报错都能对得上号# 主架构和开启的额外架构装错架构的包 dpkg 会直接拒 dpkg --print-architecture dpkg --print-foreign-architectures # 运行库版本用来判断 GLIBCXX 上限 ldd --version | head -1 # 空间gcc-7 全套解包后对 /usr 的占用不算小 df -h /usr /opt--print-foreign-architectures如果输出了i386说明这台机器开过多架构支持你在解包时就要留意别把 i386 的 deb 混进 amd64 的安装序列里dpkg -i遇到架构不符会直接报package architecture (i386) does not match system (amd64)虽然加--force-architecture能硬上但我不建议在编译器这种基础组件上硬来。磁盘这块给个经验值gcc-7、g-7、cpp-7、libgcc-7-dev、libstdc-7-dev这一套解包后落在/usr下大致是百来兆到两百兆的量级/usr剩不到 500M 的机器先清一清再装装到一半No space left on device会让包卡在iU状态比一开始就失败更麻烦。最后确认一件事目标机器上有没有/usr/local/bin/gcc这种从源码装的版本。有的话它优先级高于/usr/bin你把 alternatives 配得再漂亮gcc --version还是那份源码版。这种机器上要做的不是装是先决定这份源码版留不留留就把路径写死到项目配置里不留就make uninstall掉别留个黑匣子在那儿谁都说不清。3. dpkg 离线铺包gcc-7 与 g-7 的安装顺序和命令到了真正动手的环节。离线装 gcc-7 和联网apt install最大的差别是apt 会替你算依赖、按顺序解包配置dpkg 不会它老老实实按你给的顺序来缺一个就在配置阶段停住留下一堆iU状态的包。所以这一章的重点不是“敲哪条命令”而是把包按依赖链排好队再决定用哪种参数形态交给 dpkg。下面这套流程我在几台不通外网的机器上重复过顺序没变过。3.1 解包清点先看清手里这组 deb 是谁拿到压缩包先别急着dpkg -i *.deb先在临时目录里解开、列清单。原因很简单*.deb的通配顺序是按文件名字典序来的g-7排在gcc-7前面cpp-7排在gcc-7-base后面这个顺序跟真实依赖链基本对不上虽然 dpkg 会先解包后配置、顺序影响没那么绝对但一旦中间某个包配置失败你排查时面对的就是一团乱麻。# 解到独立目录别在下载目录里直接操作 mkdir -p /opt/gcc7-deb cd /opt/gcc7-deb unzip ~/ubuntu18.04gcc.zip -d ./pkg # 只列包名和版本确认这套包是不是同一个版本线 for f in pkg/*.deb; do dpkg-deb -f $f Package Version Architecture donedpkg-deb -f直接读控制信息不安装、不解包是最安全的清点手段。三个字段里Package告诉你这是哪个组件Version要确认它们属于同一个版本线比如都是7.5.0-3ubuntu1~18.04版本混装是离线安装最常见的暗坑——gcc-7是 7.5.0 而cpp-7是 7.3.0dpkg 会报依赖版本不满足且报错信息不会直说是版本对不上。顺手再看一眼每个包依赖谁# 打印 Depends 字段用来拼安装顺序 dpkg-deb -f pkg/gcc-7_*.deb Depends dpkg-deb -f pkg/libgcc-7-dev_*.deb Depends这一步的意义在于如果目标机器上缺libisl19、libmpfr6这类基础库你能在装之前就发现而不是装到一半被dependency problems打断。18.04 的完整系统里这些库基本都在因为默认就是 gcc-7最小化安装或容器镜像里则经常缺这是两套完全不同的处置方式。3.2 依赖链条gcc-7-base → cpp-7 → libgcc-7-dev → gcc-7 → g-7gcc-7 这套包的依赖关系是条清晰的链记不住就按下面这个顺序摆顺序包名作用依赖谁1gcc-7-base基础数据、license、arch 信息无2cpp-7C 预处理器提供/usr/lib/gcc/x86_64-linux-gnu/7/cc1gcc-7-base3libgcc-7-dev运行时支持库和开发文件crtbegin、libgcc.a 等gcc-7-base、libgomp1、libitm1、libatomic1、libasan4、libubsan0、liblsan0、libtsan0 等4gcc-7C 编译器驱动cpp-7、gcc-7-base、libgcc-7-dev、binutils5libstdc-7-devC 标准库头文件与静态库gcc-7-base、libgcc-7-dev、libstdc66g-7C 编译器驱动gcc-7、libstdc-7-dev顺序背后的道理只有一句话驱动gcc-7 / g-7依赖前端cpp-7和后端支持libgcc-7-dev / libstdc-7-dev所以支持类的包必须先到位。你把这个顺序倒过来装dpkg 会在配置gcc-7时找不到cc1报出来的错是dependency problems prevent configuration看着像缺包实际是顺序问题。按这个顺序落命令一次传多个包、按依赖先后排列cd /opt/gcc7-deb/pkg # 分两批先铺底座再上驱动 sudo dpkg -i gcc-7-base_*.deb cpp-7_*.deb libgcc-7-dev_*.deb sudo dpkg -i gcc-7_*.deb libstdc-7-dev_*.deb g-7_*.deb分两批而不是一口气全传是为了让第一批的失败在第二批之前暴露。第一批如果因为缺libisl19之类的基础库失败你补库就行不会牵连到后面四个包一口气全传的话失败信息会混在一起逐个dpkg -l | grep -E iU|iF去捞很费时间。第一批成功后别急着往下走先验一下预处理器能不能跑# 直接调 cpp-7能打印版本说明前端装好了 /usr/bin/cpp-7 --version # 看 cc1 是否落在正确位置这是 gcc-7 能不能干活的关键 ls -l /usr/lib/gcc/x86_64-linux-gnu/7/cc1cc1是真正的 C 编译器后端gcc只是个驱动负责解析参数、调cc1、再调as和ld。cc1缺失时gcc --version照样能输出版本号但一编译就报gcc: error: /usr/lib/gcc/x86_64-linux-gnu/7/cc1: No such file or directory这个报错在只装了gcc-7元包、没装cpp-7的机器上出现频率极高。3.3 dpkg 参数取舍一次装完、分开装还是 --force-depends三种形态各有适用场景选错会把小问题放大。第一种是上面用的“分批dpkg -i指定包名”推荐度和可控性最高失败点清晰也方便在中间插入检查。第二种是“dpkg -i *.deb一把梭”通配顺序不可控只在包数量多、你已确认它们之间没有版本冲突时用比如整套包都是同一个 build 出来的。第三种是--force-depends很多人一看到dependency problems就条件反射加上它# 应急可以别当常规手段 sudo dpkg -i --force-depends gcc-7_*.deb--force-depends的作用是“依赖不满足也强行配置”包会进入iF状态但文件已经铺到磁盘上gcc --version甚至能正常输出。问题是它跳过了依赖检查不保证libgcc-7-dev真的在你会在几周后编译某个需要crtbegin.o的工程时突然撞墙而且那时候已经想不起来当初装了什么。真要用它救急必须紧跟一步补依赖再复查状态# 有本地源或联网时让它把缺的依赖补齐 sudo apt --fix-broken install # 离线机器没有源只能手工把缺的 deb 找齐再 dpkg -i dpkg -l | grep -E iU|iFapt --fix-broken install在离线机器上会失败并提示找不到包这时候看它提示的包名去压缩包里翻对应 deb 手工补是唯一的路。补完再跑一遍dpkg --configure -a让处于iU状态的包走完配置流程。3.4 装完立刻做的两件事configure -a 和包状态复查装完最后一批 deb别急着写代码验证先做这两步收尾能挡掉后面一大半的玄学问题。# 把所有解包了没配置的包走完配置流程幂等多跑无害 sudo dpkg --configure -a # 复查确认六个包都是 ii且版本一致 dpkg -l gcc-7 g-7 cpp-7 gcc-7-base libgcc-7-dev libstdc-7-dev | grep -E ^(ii|iU|iF)dpkg --configure -a是幂等的已经配置好的包会被跳过所以它可以无脑跑。有些人装完deb直接gcc --version能出版本号就以为成了实际上包可能停在iU文件铺了、配置脚本没跑配置脚本负责的是建立/usr/bin/gcc-7之类的符号链接、更新alternatives、写dpkg的 trigger。停在iU的机器上/usr/bin/gcc-7可能根本不存在你后面配 alternatives 会直接报alternative path /usr/bin/gcc-7 doesnt exist。版本一致性这步也别省。六个包必须是同一版本线混装时dpkg -l的输出会整整齐齐全是ii但编译时会出各种诡异错误比如头文件和库 ABI 对不上导致的链接失败。dpkg -l那一行里第三列就是版本号扫一眼是否一致比事后查半天强。到这一步/usr/bin/gcc-7和/usr/bin/g-7应该都在了。但系统默认的gcc未必指向它们——这就是下一章要处理的事。4. 让 gcc-7 成为默认update-alternatives 的优先级与项目级锁定装完包和“用上 gcc-7”之间还隔着一层符号链接。Ubuntu 的编译器不是直接调/usr/bin/gcc-7而是走/usr/bin/gcc - /etc/alternatives/gcc - /usr/bin/gcc-X这条链。这条链存在的意义是让多个版本共存、随时切换它带来的麻烦是装完包之后链可能还指着别人。这一章讲怎么把链接配好、优先级数字怎么定、以及更重要的——什么时候不该动系统默认。4.1 update-alternatives 的三层结构主链接与 slave先把结构说清楚不然参数会一直写错。/usr/bin/gcc是主链接master link/usr/bin/g、/usr/bin/gcov这些是从链接slave。主链接切换时所有 slave 跟着一起切这就是为什么不能单独给g配一条 alternative——那样会出现gcc是 7、g是 5 的错位编 C 正常、编 C 报语法错误排查起来极其费劲。# 一条命令把主链接和从链接一起注册成组切换 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 \ --slave /usr/bin/g g /usr/bin/g-7 \ --slave /usr/bin/gcov gcov /usr/bin/gcov-7 \ --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-7 \ --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-7 \ --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-7参数逐个拆--install后面跟四段依次是“链接路径 / 链接名 / 实际二进制路径 / 优先级”。链接名gcc是这套 alternative 组的标识不是文件名后面--config gcc用的就是它。--slave后面跟三段链接路径 / 链接名 / 实际路径从链接没有独立优先级完全跟随主链接。gcc-ar、gcc-nm、gcc-ranlib这三个看着不起眼的 slave 值得带上。它们对应-flto链接时优化场景LTO 编译需要与编译器版本匹配的 ar/nm/ranlib不带的话开了 LTO 的工程会报plugin needed to handle lto object这个错在构建系统里藏得很深很多人查到最后才发现是 alternatives 少配了这三个。注册完之后看状态# 看当前指向、优先级、以及所有候选 update-alternatives --query gcc # 交互式选择多版本共存时用这个 sudo update-alternatives --config gcc--query的输出里Value:是当前生效的路径Best:是优先级最高的候选Priority那一列是各候选的数字。auto 模式下实际生效的是Best你手动--config选过之后会切到 manual 模式之后哪怕新装一个更高优先级的版本也不会自动切走。4.2 优先级数字怎么定别把别人的 gcc-5 顶掉优先级是个纯数字大的赢。系统自带的gcc-7注册时用的常见值是 60 上下你要让目标版本稳定生效把它设成 70 就够了没必要设 200 那么夸张。# 检查有没有更高优先级的候选在抢 update-alternatives --query gcc | grep -E ^(Value|Best|Priority)为什么不建议设成极大的数字这台机器上如果还有别人的工程依赖 gcc-5你把 gcc-7 的优先级拉到 999等于永久性地把默认编译器换掉了对方下次编译会莫名其妙失败而且他不一定会想到是 alternatives 被改了。优先级 70 的含义是“在常见候选里优先我”真有人手动--config选了别的版本也不会被一次apt upgrade之类的操作给悄悄切回来。设完优先级有一个必做的收尾# 清掉 shell 对命令路径的缓存 hash -r # 确认三个命令都指向同一版本别出现错位 type -a gcc g gcovbash会缓存命令的绝对路径你在另一个窗口改完 alternatives当前窗口里的gcc可能还指向旧路径表现就是“明明配了但版本没变”。hash -r清缓存type -a复查这两步花不了五秒能省掉大量自我怀疑。4.3 项目级锁定不改系统默认也能指定 gcc-7还有一种情况比改系统默认更常见这台机器上跑着好几个工程A 工程要 gcc-7B 工程要 gcc-5谁都不能动系统默认。这时候正确的做法是把版本锁在项目里而不是全局切。最直接的是 Makefile 层覆盖# 命令行覆盖 Makefile 里的 CC/CXX不用改文件 make CC/usr/bin/gcc-7 CXX/usr/bin/g-7 -j4前提是工程的 Makefile 用了CC ? gcc或CC gcc这种写法如果它硬编码了CC : /usr/bin/gcc命令行覆盖无效得去改文件。CMake 工程在 configure 阶段就把编译器路径记进CMakeCache.txt了后面改环境变量没用必须清缓存重来# 删掉旧缓存用指定编译器重新 configure rm -rf build mkdir build cd build cmake -DCMAKE_C_COMPILER/usr/bin/gcc-7 \ -DCMAKE_CXX_COMPILER/usr/bin/g-7 \ -DCMAKE_BUILD_TYPERelease ..这里有个高频翻车点CMakeCache.txt里已经写了编译器路径你只改-D参数不删缓存CMake 会忽略新值并提示CMAKE_C_COMPILER changed, but the cache is not cleared很多人不看这行提示编完发现还是旧版本在干活。rm -rf build是最省事的做法。纯脚本或 configure 类的工程用环境变量# 只对当前 shell 生效不污染系统 export CC/usr/bin/gcc-7 export CXX/usr/bin/g-7 ./configure --prefix/usr/localexport的作用域是当前 shell 和它派生的子进程关掉窗口就没了这正好符合“临时用一下”的需求。要注意的是有些 configure 脚本会用CC去探测编译器特性如果这个变量指向一个不存在的路径脚本不报错而是默默回退到cc最后编出来的东西版本不对还查不出原因。所以export之后紧跟一句$CC --version确认一下是个好习惯。5. 避坑排查五个「dpkg 装完了但编译不过」的现场离线装 gcc-7 的报错有个共同特征安装阶段的输出看着挺正常问题全在编译或者运行的时候才炸出来。下面五条是按我实际遇到的频率排的每条都按“现象 → 原因 → 解决”走一遍对着症状找就行。5.1 依赖未满足dependency problems prevent configuration现象dpkg -i输出里出现dpkg: dependency problems prevent configuration of gcc-7后面跟着一串gcc-7 depends on cpp-7 ( 7.5.0-3~); however: Package cpp-7 is not installed.命令返回非零dpkg -l里gcc-7停在iU。原因包没按依赖顺序给或者压缩包里压根没带齐依赖。最常见的是只解出gcc-7和g-7两个包就开始装cpp-7、libgcc-7-dev落在压缩包里没被一起处理。解决先看 dpkg 到底在抱怨谁别猜# 让它把所有未满足的依赖列全 sudo dpkg -i gcc-7_*.deb 21 | grep -E depends on|however # 按报出来的包名回压缩包里找补齐后重新配置 sudo dpkg -i cpp-7_*.deb libgcc-7-dev_*.deb sudo dpkg --configure -a补包之后--configure -a一定要跑因为之前失败的包会一直卡在iU不跑配置流程它不会自己好。离线机器上apt --fix-broken install会因找不到源而失败别在它身上耗时间。5.2 装到一半中断dpkg was interrupted现象再敲任何dpkg或apt命令第一行都是dpkg was interrupted, you must manually run dpkg --configure -a to correct the problem.后面的操作全部被拦住。原因上一次安装被 CtrlC 打断、SSH 断连、或者机器在装包过程中重启了dpkg 的锁文件和状态没清理干净。这个不是 gcc-7 特有的问题但只要装包中途出过岔子就一定会遇到。解决照它说的做别去手工删/var/lib/dpkg/lock# 先让 dpkg 把上次没干完的活干完 sudo dpkg --configure -a # 如果它卡在某个包的配置脚本上看是哪个包单独处理 dpkg -l | grep -E iU|iF删锁文件是很多人第一反应但那只解开了互斥锁包的状态还是半成品下次装照样出问题。真遇到配置脚本死循环某些第三方包会才考虑--force-remove掉那个包再重来而且要做之前先确认它不是你正在装的东西的依赖。5.3 装完版本没变hash 缓存和 /usr/local 里的旧 gcc现象dpkg -l | grep gcc-7显示iiupdate-alternatives --query gcc的Value也对但gcc --version还是 5.4.0。原因两种可能。一是当前 shell 的命令路径缓存没清二是/usr/local/bin/gcc存在PATH 里它排在/usr/bin前面alternatives 管不到它。解决按顺序排查这两个# 先清缓存再说 hash -r # 再看所有同名命令的落点重点看 /usr/local/bin 有没有货 type -a gcc ls -l /usr/local/bin/gcc* 2/dev/nulltype -a输出里排第一的就是真正执行的那个。如果/usr/local/bin/gcc存在且指向一个源码编译的版本你有两个选择把它删掉sudo make uninstall或者直接rm或者在项目里写死/usr/bin/gcc-7。我一般倾向后者因为源码装的版本可能还有人用删了容易引发新问题。5.4 cc1 找不到只装了 gcc-7 没装 cpp-7现象gcc --version正常输出 7.5.0但一编译就报gcc: error: /usr/lib/gcc/x86_64-linux-gnu/7/cc1: No such file or directory或gcc: internal compiler error: Killed。原因gcc-7只是驱动真正的编译器前端在cpp-7包里。有些从别处捞的 deb 集合只包含gcc-7和g-7两个元包cpp-7被漏掉了。解决确认并补装# 看 cc1 是不是真不在 ls -l /usr/lib/gcc/x86_64-linux-gnu/7/ dpkg -l cpp-7 # 补装 cpp-7然后重新验证 sudo dpkg -i cpp-7_*.deb gcc -v -E -x c /dev/null最后那条gcc -v -E -x c /dev/null是最轻量的验证手段只跑预处理、不做编译链接、不需要任何源文件能完整打出gcc调用cc1的搜索路径。如果cc1缺失这条命令会立刻报错比写个 hello world 再编译快得多。5.5 运行期 GLIBCXX 版本不够现象编译一切正常程序一运行就报./app: /usr/lib/x86_64-linux-gnu/libstdc.so.6: version GLIBCXX_3.4.24 not found。原因编译用的 g-7运行期链接的是更老的libstdc6。这在“编译机和运行机不是同一台”的场景里最常见比如交叉编译后拷到另一台机器上跑。解决先看运行机上现有的libstdc6支持到哪# 列出支持的最高 GLIBCXX 版本 strings /usr/lib/x86_64-linux-gnu/libstdc.so.6 | grep GLIBCXX_3.4.2 | sort -V | tail -3如果最高只到GLIBCXX_3.4.21说明运行机上的libstdc6是 5.x 时代的需要升级到 18.04 对应的 8.x 包。离线环境下升级基础运行库风险不小更稳妥的做法是编译时静态链接标准库# 静态链接 libstdc 和 libgcc摆脱运行机版本依赖 g -O2 -static-libstdc -static-libgcc -o app app.cpp-static-libstdc把标准库静态编进可执行文件代价是体积变大、跨模块共享标准库对象时有隐患。只在部署机版本不可控时用别一刀切全项目都加上。6. 验证跑通用三段代码确认 gcc-7 真的能干活装完不验证等于没装。gcc --version只能证明驱动在证明不了预处理器、后端、C 标准库、链接器这几环都通。我习惯用三段递进的代码收尾从纯 C 到 C17 再到符号检查任何一段挂了都能直接定位到是哪一层的问题。第一段验 C 编译器整条链路同时确认版本号真的是 7/* ver.c编出来的程序直接打印编译器主版本 */ #include stdio.h int main(void) { printf(GCC_MAJOR%d\n, __GNUC__); return 0; }gcc -O2 -o ver ver.c ./ver # 期望输出 GCC_MAJOR7输出别的数字说明驱动不对第二段验 C 标准库和 C17 支持这一段能同时暴露“只有 gcc 没有 g”和“libstdc-7-dev 没装”两种半成品状态// t17.cpp用到 optional 和结构化绑定都是 C17 的硬指标 #include iostream #include optional #include tuple int main() { std::optionalint v 42; auto [a, b] std::make_tuple(1, 2); std::cout *v a b \n; // 期望 45 }g -stdc17 -O2 -o t17 t17.cpp ./t17 # 报 unrecognized command line option -stdc17 说明用错了编译器 # 报 fatal error: optional: No such file 说明 libstdc-7-dev 没装第三段不写代码直接查符号和搜索路径这是最快的一眼判决检查项命令正常表现驱动落点readlink -f $(command -v gcc)指向/usr/bin/gcc-7主从一致type -a gcc g gcov三者版本号一致内建搜索路径gcc -v -E -x c /dev/null 21 | grep -A2 search starts here含/usr/include/c/7运行库上限strings /usr/lib/x86_64-linux-gnu/libstdc.so.6 | grep GLIBCXX_3.4.2 | sort -V | tail -1不低于GLIBCXX_3.4.24gcc -v -E -x c /dev/null这条值得单独记住-E只做预处理-x c指定语言因为/dev/null没有扩展名/dev/null当空输入所以它不产生任何实际输出只把gcc内部的搜索路径、cc1调用、版本信息全打出来。编译出问题时先跑这条能立刻分清是“找不到头文件”还是“根本调不到 cc1”。有次在一台改过 PATH 的机器上gcc --version显示 7.5.0type -a也正常但工程编译死活找不到string。跑了上面第三条才看到搜索路径里是/usr/include/c/5——/etc/profile.d里有个早年留下的CPLUS_INCLUDE_PATH把所有工程的 C 头文件路径都钉死在 5 上了。这个变量优先级高于编译器内建路径g --version显示什么都不影响它。从那以后我每次装完编译器都会先env | grep -E INCLUDE|LIBRARY_PATH看一眼有没有残留的路径变量再跑那三段验证代码一个不落地走完才把这台机器交给别人。这套流程烦是烦了点但比事后被人追着问“为什么我这儿编不过”省心多了。希望帮到你。本文还有配套的精品资源点击获取