与硬件背靠背 · C++、C语言与汇编的协同

C++ 是一门"贴近硬件"的语言。它不仅能够无缝调用 C 语言代码,还能与汇编语言直接交互。这种能力使得 C++ 成为系统编程、嵌入式开发、性能优化领域的首选语言。

让我们探索 C++、C 语言与汇编之间的协同之道。


C++ 与 C 的血脉联系

C++ 起源于 C 语言,因此两者有着天然的兼容性。但直接混用时需要注意一些细节。

extern "C" 的作用

C++ 编译器会对函数名进行"名称修饰"(Name Mangling),以支持函数重载:

// C++ 中的这两个函数
void foo(int x);
void foo(double x);

// 编译后可能变成类似这样的名字
// _Z3fooi
// _Z3food

但 C 语言不支持函数重载,也不会进行名称修饰。为了让 C++ 调用 C 代码(或反过来),需要使用 extern "C"

// 声明一个 C 函数
extern "C" void c_function(int x);

// 或者包裹多个声明
extern "C"
{
    void c_func1(int x);
    void c_func2(double y);
    int c_func3(const char* str);
}

// 在 C++ 中定义一个供 C 调用的函数
extern "C" int cpp_for_c(int a, int b)
{
    return a + b;
}

头文件的双重兼容

编写同时供 C 和 C++ 使用的头文件:


混合编译实战

假设我们有一个 C 语言写的数学库,想在 C++ 项目中使用:

编译命令:


内联汇编

C++ 允许在代码中直接嵌入汇编指令。不同编译器的语法略有不同。

GCC/Clang 风格(AT&T 语法)

MSVC 风格(Intel 语法)

注意:MSVC 的内联汇编仅支持 32 位模式。64 位程序需要使用单独的汇编文件。


调用外部汇编函数

对于复杂的汇编代码,通常将其放在单独的 .asm.s 文件中。

编译命令(Linux):


调用约定

不同平台有不同的函数调用约定(Calling Convention),理解它们对于 C++/汇编混合编程至关重要。

x86-64 System V ABI(Linux、macOS)

  • 前 6 个整数参数:rdi, rsi, rdx, rcx, r8, r9

  • 前 8 个浮点参数:xmm0 - xmm7

  • 返回值:rax(整数)、xmm0(浮点)

x86-64 Windows ABI

  • 前 4 个参数:rcx, rdx, r8, r9(整数或指针)

  • 浮点数也用这些寄存器对应的 XMM 寄存器

  • 返回值:rax


实战:SIMD 优化

使用汇编或编译器内置函数进行 SIMD(单指令多数据)优化:


链接 C 库

C++ 程序经常需要链接系统级 C 库:


volatile 关键字

在与硬件交互或多线程编程中,volatile 防止编译器优化掉看似"无用"的读写:


裸函数(Naked Function)

某些编译器支持"裸函数",让你完全控制函数的汇编代码:


总结

C++、C 语言和汇编的协同使用,打开了通往系统底层的大门:

场景
技术

调用 C 库

extern "C"

性能关键代码

内联汇编、SIMD 内置函数

硬件交互

volatile、内存映射

底层系统调用

外部汇编文件

跨语言接口

遵循调用约定

掌握这些技术,你就能在保持 C++ 高级抽象的同时,在需要时直达硬件层面,实现极致的性能优化。

Last updated