最简可行命令是emcc hello.cpp -o hello.js -o2 --no-entry,生成hello.wasm和hello.js;-o2为硬性要求,否则默认输出asm.js而非wasm。

emcc 编译 C++ 源码生成 wasm 的基本命令是什么
直接用 emcc 替代 g++ 即可,但必须加 -O2 或更高优化级,否则默认输出的是 asm.js(不是 wasm),且体积巨大、无法运行。
最简可行命令:
emcc hello.cpp -o hello.js -O2 --no-entry
这会生成 hello.wasm 和 hello.js 两个文件。注意:--no-entry 表示不生成默认的 main() 入口胶水代码,适合导出函数供 JS 调用;如果 C++ 有 int main(),去掉该参数即可。
-
-O2是硬性要求,-O0默认禁用 wasm 后端 - 输出名用
.js后缀,Emscripten 才会同时生成配套的 wasm 和加载 glue code - 不要手动指定
-o hello.wasm—— 这样只会生成一个空壳 wasm,没导入/导出定义,JS 根本没法调用
怎么让 C++ 函数被 JavaScript 正确调用
关键在两处:C++ 端显式导出,JS 端用 ccall 或 cwrap 调用。Emscripten 不会自动导出所有函数,必须标记。
C++ 示例(add.cpp):
extern "C" {
int add(int a, int b) {
return a + b;
}
}
编译时加 -s EXPORTED_FUNCTIONS='["_add"]'(注意下划线前缀)和 -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]':
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
emcc add.cpp -o add.js -O2 -s EXPORTED_FUNCTIONS='["_add"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
- 函数名必须加下划线
_add,这是 Emscripten 的符号约定,C++ 函数名会被 mangling,extern "C"是为了禁用 mangling -
EXPORTED_FUNCTIONS只控制哪些函数进入 wasm 的export表,不加就不可见 -
EXPORTED_RUNTIME_METHODS是为了让Module.ccall在 JS 端可用,漏掉会报Module.ccall is not a function
常见报错:wasm validation error 或 missing function export
典型现象是浏览器控制台报 LinkError: WebAssembly.instantiate(): Import #0 module="env" error: function import requires a callable,或 JS 调用 ccall 时报 cannot call _add due to undefined。
根本原因通常是环境未初始化完成就调用 wasm 函数。Emscripten 输出的 JS 胶水代码是异步加载 wasm 的,Module 对象要等 onRuntimeInitialized 触发后才就绪。
- 不要在脚本顶部直接
Module.ccall(...),此时Module还是空对象 - 正确做法是监听
Module.onRuntimeInitialized = () => { /* 调用逻辑 */ } - 或者用 Promise 方式:
Module().then(() => Module.ccall(...))(前提是编译时加了-s MODULARIZE=1) - 若用
MODULARIZE=1,还必须加-s EXPORT_NAME="createModule"避免全局污染
为什么有时候生成的 wasm 文件里看不到导出的函数
用 wabt 的 wabt/wat2wasm 或 wasmdump 查看 wasm 导出表时,发现 _add 没出现,但编译没报错 —— 很可能被 LTO(Link Time Optimization)删掉了。
Emscripten 默认开启 -flto(尤其 -O2 及以上),如果函数没被 main() 或其他导出函数调用,会被当作 dead code 移除。
- 加
-s EXPORTED_FUNCTIONS只影响 export 表,不阻止 LTO 删除函数体 - 解决方案:加
--no-lto关闭 LTO,或确保该函数被至少一处“可达调用”引用 - 调试时可加
-s LINKABLE=1强制保留符号,但仅用于开发,上线务必关掉 - 用
llvm-nm --defined-only add.wasm(需安装llvm工具链)可快速验证符号是否还在
真正麻烦的从来不是编译命令本身,而是那些没出现在错误信息里、却让导出函数彻底消失的静默优化行为。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










