多语言混合编程

GNU C Compiler 其实现在更准确的应该叫 GNU Compiler Collection

C Pre-Processor->C Compiler->Assembler->Linker
其中的前两个步骤通常可以替换 例如
C++ Pre-Processor->C++ Compiler
Objective C Pre-Processor->Objective-C Compiler
Cfortran Fortran Compiler
Go Compiler

GCC能支持多种语言。

我们可以写C ,写汇编.s文件,C代码中调用汇编函数,.s由GCC Assembler处理,汇编+C代码形成我们想要的可执行文件。

当然可以使用其他现代语言来生成汇编得到目标文件。

gfortran -c is_prime.f90 编译汇编fortran文件。
gcc -c count_primes.c 编译汇编C文件。
gfortran count_primes.o is_prime.o -o count_primes 把两个目标文件,链接成一个可执行文件。

不同语言共存同一个可执行文件中 ,关键在于链接器

rustc --create-type=staticlib is_prime.rs

在C代码中声明rust中的函数 编译C代码,并将其与Rust编译好的库进行链接

gcc main.c libis_prime.a -o count_primes

反过来rust调用C也类似

仅仅互调函数,但不一定可以链接成功,不同语言出来的机器码可能底层指定不同,例如add(int a, int b) 返回 a+b ,这两个临时变量放在哪里,不同语言可能有自己的处理方式,放在不同的寄存器中,另一个语言可以从不是调用放放的位置取数据,导致行为未定义。传参和返回值 引用传递 值传递都有很大的不确定性。

即使两个编译器生成了有效可执行的汇编代码,最终链接二进制文件也会不一致,除非双方在通信方式上达成一致。

这类底层规则就是由所谓的应用二进制接口ABI Application Binary Interface 来定义的。

A语言调用B语言,则调用应该符合B语言的ABI预期。A调整去适应B,或者B调正去适应A。

C语言中用 extern

extern int is_prime(int n);

Go语言中用

#[no_mangle]
pub extern "C" fn is_prime(n: i32) -> i32

Fortran用

function add(a, b) bind (C, name="add")

在Go中甚至可以在import "C"前,用一段特殊的注释块来包含C头文件,甚至还能直接在Go源文件中写内联C代码

package main
/*
#cgo CFLAGS -I.
#cgo LDFLAGS -L. -ladd
#include "add.h"
*/
import "C"
import "fmt"
func main(){
    result := C.add(3,4)
}
package main
/*
int add(int a, int b){
    return a+b;
}
*/
import "C"
import "fmt"
func main(){
    result := C.add(3,4)
}

每种语言都有自己的声明方式,但在编译时,这些声明都是这个函数会和其他语言写的代码交互,请确保生成的汇编代码遵循预期的ABI。