首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >前端开发者快速掌握 C++:编译、产物与跨平台工程体系

前端开发者快速掌握 C++:编译、产物与跨平台工程体系

原创
作者头像
骑猪耍太极
发布2026-05-18 15:10:49
发布2026-05-18 15:10:49
1120
举报
文章被收录于专栏:AI编程之旅AI编程之旅

这是系列第五篇。前四篇讲了语法、概念、Bug 和代码模式,都是"怎么读代码"。这一篇讲工程层面——C++ 代码是怎么变成可运行产物的,以及为什么很多跨平台底层选择用 C++。

配套了一个可编译运行的示例工程,建议边看边跑:cpp-build-demo


一、编译流程

前端 vs C++

代码语言:bash
复制
前端:源码 → 打包器(webpack/vite) → bundle.js → 浏览器解释执行

C++: 源码 → 预处理 → 编译 → 链接 → 机器码产物(直接运行,无需解释器)

C++ 编译四阶段

阶段

做了什么

前端类比

预处理

展开 #include(复制粘贴头文件)、替换 #define

Babel 转译 JSX → 纯 JS

编译

把 C++ 源码翻译成机器码(与 CPU 架构相关)

TypeScript → JavaScript

链接

把多个 .o 文件和库文件合并成最终产物

webpack 把各 chunk 合并为 bundle

动手体验

代码语言:bash
复制
cd docs/articles/cpp-build-demo
make step1   # 预处理:看 #include 被展开成什么
make step2   # 编译:生成 .o 目标文件
make step3a  # 打包静态库 .a
make step3b  # 打包动态库 .so
make step4a  # 链接(静态)→ 可执行文件
make step4b  # 链接(动态)→ 可执行文件

二、产物类型

一览表

产物

扩展名

前端类比

说明

目标文件

.o / .obj

单个源文件的编译结果,不能独立运行

静态库

.a / .lib

打进 bundle 的依赖

多个 .o 打包,链接时复制进最终产物

动态库

.so / .dll / .dylib

CDN 外链的 JS

运行时加载,多个程序共用

可执行文件

无 / .exe

node 本身

直接运行的程序

开源项目中的真实产物

你日常使用的很多工具底层都是 C++ 动态库:

开源项目

产物

你间接用到它的场景

SQLite

libsqlite3.so / .a

移动端数据库、浏览器 IndexedDB 底层

OpenSSL

libssl.so / libcrypto.so

HTTPS 通信、Node.js crypto 模块

FFmpeg

libavcodec.so / libavformat.so

音视频处理、各种播放器

V8

libv8.so

Node.js、Chrome 的 JS 引擎

libuv

libuv.so

Node.js 的事件循环和异步 I/O

skia

libskia.so

Chrome、Flutter 的 2D 渲染引擎

curl

libcurl.so

各种 HTTP 客户端底层

当你 npm install sqlite3 时,实际是下载了预编译好的 .node 文件(本质就是一个 .so/.dylib),Node.js 在运行时 dlopen 加载它。


三、静态库 vs 动态库

维度

静态库 (.a)

动态库 (.so)

链接时机

编译时复制进产物

运行时加载

部署

只需一个文件

必须带上 .so 文件

体积

大(库代码被复制)

小(库代码共享)

更新库

必须重新编译程序

替换 .so 即可

前端类比

import lodash 打进 bundle

<script src="cdn/lodash.js"> 外链

动态库的依赖链

动态库本身也可能依赖其他动态库,形成链式依赖:

代码语言:bash
复制
你的程序
  └─ 依赖 libcurl.so(HTTP 库)
       ├─ 依赖 libssl.so(加密)
       ├─ 依赖 libz.so(压缩)
       └─ 依赖 libpthread.so(多线程)

可以用 ldd 命令查看一个动态库或可执行文件的所有依赖:

代码语言:bash
复制
$ ldd ./build/app_dynamic
    libmath.so => ./build/libmath.so
    libstdc++.so.6 => /usr/lib/libstdc++.so.6
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6

$ ldd /usr/bin/curl
    libcurl.so.4 => /usr/lib/libcurl.so.4
    libssl.so.3 => /usr/lib/libssl.so.3
    libz.so.1 => /usr/lib/libz.so.1
    ...

当你遇到这种运行时错误:

代码语言:bash
复制
error while loading shared libraries: libxxx.so: cannot open shared object file

说明依赖链上某个 .so 找不到。解决方式:

  • 安装缺失的库(apt install libxxx-dev
  • 或者把 .so 文件放到系统能找到的路径(/usr/libLD_LIBRARY_PATH 指定的目录)

前端类比: 就像你的 JS 代码 import A from 'a',但 a 这个包内部又 import B from 'b',如果 b 没装,运行时就报 Cannot find module 'b'


四、CPU 架构与交叉编译

这是前端开发者完全陌生的领域——JS 运行在虚拟机上不关心 CPU,但 C++ 编译出的是特定 CPU 的机器码。

常见 CPU 架构

架构

用在哪里

说明

x86_64 (amd64)

PC、Mac(Intel)、服务器

桌面主流

arm64 (aarch64)

手机、Mac(M 系列)、新服务器

移动端主流

armv7

老手机、IoT 设备

32 位 ARM

wasm

浏览器

WebAssembly,C++ 也能编译到这个目标

关键概念:交叉编译

在你的 x86_64 电脑上编译出 arm64 手机能运行的程序,就叫交叉编译

代码语言:bash
复制
# 本地编译(产物只能在当前机器跑)
g++ main.cpp -o app

# 交叉编译(在 x86 电脑上编译给 ARM 手机用的产物)
aarch64-linux-gnu-g++ main.cpp -o app_arm64

前端类比: 你在 Mac 上 npm run build 出的 JS 哪里都能跑,因为有 JS 引擎兜底。但 C++ 编译出的机器码只能在目标 CPU 上跑——在 Mac 上编译给手机用,就得用交叉编译器。

为什么一个开源库会提供多个产物?

以 SQLite 为例:

代码语言:bash
复制
sqlite-android-arm64.so    ← 安卓手机用
sqlite-android-x86_64.so   ← 安卓模拟器用
sqlite-ios-arm64.a         ← iPhone 用
sqlite-macos-arm64.dylib   ← Mac 用
sqlite-linux-x86_64.so     ← Linux 服务器用

同一份 C 源码,交叉编译了 5 次,产出 5 种产物。


五、跨平台原理

JS vs C++ 的跨平台方式

核心区别: JS 依赖各平台的 JS 引擎来解释执行。C++ 直接编译为各平台的机器码,不依赖任何 runtime。

为什么很多跨平台底层选 C++?

原因

说明

性能

编译为机器码直接执行,无解释器开销

无 runtime 依赖

不需要 JVM / JS 引擎,产物自身就是完整代码

全平台编译器支持

Android NDK / iOS Clang / Linux GCC 等都支持 C++

与系统层零开销交互

操作系统底层就是 C/C++,无需桥接

典型案例:

  • Flutter → 渲染引擎 skia 是 C++
  • React Native → JSI 桥接层是 C++
  • Chrome → 浏览器本身是 C++
  • Node.js → V8 引擎 + libuv 都是 C++

六、构建系统

C++ 没有统一的构建系统和包管理器(不像前端有 npm + webpack 的事实标准)。

工具

角色

前端类比

CMake

项目配置(生成构建文件)

vite.config.ts

Makefile

构建执行(调用编译器)

npm scripts

Ninja

快速构建执行器

esbuild(追求速度)

Conan / vcpkg

包管理

npm / yarn

GN

Google 的构建配置

Chromium / V8 在用

大型项目一般用 CMake,小项目直接用 Makefile。示例工程用的是 Makefile。


七、常见编译错误

前端开发者遇到 C++ 编译报错时,先判断属于哪个阶段:

阶段

典型错误

前端类比

预处理

fatal error: xxx.h: No such file or directory

Module not found: Can't resolve './xxx'

编译

error: 'xxx' was not declared in this scope

TS 的 Cannot find name 'xxx'

编译

error: no matching function for call to 'xxx'

TS 的 Argument type mismatch

链接

undefined reference to 'xxx'

运行时 xxx is not a function(声明了但没实现)

链接错误是前端开发者最不熟悉的——每个 .cpp 单独编译没问题,但合并时发现某个函数"只有声明没有实现"。类比:你在 .d.ts 里声明了类型,但忘了写 .ts 实现。


八、总结:C++ 产物的全景图


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、编译流程
    • 前端 vs C++
    • C++ 编译四阶段
    • 动手体验
  • 二、产物类型
    • 一览表
    • 开源项目中的真实产物
  • 三、静态库 vs 动态库
    • 动态库的依赖链
  • 四、CPU 架构与交叉编译
    • 常见 CPU 架构
    • 关键概念:交叉编译
    • 为什么一个开源库会提供多个产物?
  • 五、跨平台原理
    • JS vs C++ 的跨平台方式
    • 为什么很多跨平台底层选 C++?
  • 六、构建系统
  • 七、常见编译错误
  • 八、总结:C++ 产物的全景图
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档