Skip to content

[中文版本](/posts/c教程/zh-CN/09-可移植与现代 C/087-Using C with Other Languages (FFI))

C is often called the universal assembly language, nearly every modern language can call into it. This is made possible through the Foreign Function Interface (FFI), which defines how different languages talk to C code.

In this section, you’ll learn how to expose your C functions to Python, Rust, and Go, and how to call functions from those languages inside C.

Step 1. What Is an FFI? ​

An FFI (Foreign Function Interface) is a bridge that lets programs written in one language use code written in another.

Why FFI matters:

  • Reuse fast, low-level C libraries (e.g., OpenSSL, SQLite).
  • Integrate C modules into higher-level languages like Python or Go.
  • Extend existing programs without rewriting everything.
  • Combine system-level control with productivity.

Step 2. The Foundation: C ABI ​

The ABI (Application Binary Interface) defines how function calls, parameters, and data structures are represented in memory. The FFI works because C has a stable and simple ABI.

Rules include:

  • How arguments are passed (registers or stack).
  • How return values are handled.
  • How data types are aligned in memory.

That’s why almost every language provides a way to “speak” the C ABI.

Step 3. Exposing C Functions to Other Languages ​

You can make your C functions callable by other languages by marking them withextern "C"(if compiling as C++) or just regular C functions otherwise.

Tiny Code: Shared C Library

// file: mathlib.c
#include <stdio.h>
int add(int a, int b) {
    return a + b;
}
int multiply(int a, int b) {
    return a * b;
}

Compile it into a shared library:

gcc -shared -fPIC -o libmathlib.so mathlib.c

This creates a.so(Linux) or.dll(Windows) or.dylib(macOS) file you can load in other languages.

Step 4. Using C in Python (ctypes) ​

Python can call C functions directly using thectypes module.

import ctypes
lib = ctypes.CDLL("./libmathlib.so")
print(lib.add(2, 3))
print(lib.multiply(4, 5))

Output:

5
20

Python automatically converts standard types (int,float,char *) to C equivalents.

For more complex types, you can definectypes.Structure classes matching your C structs.

Step 5. Using C in Rust ​

Rust has a built-inextern "C" block for FFI.

Rust Example:

#[link(name = "mathlib")]
extern "C" {
    fn add(a: i32, b: i32) -> i32;
}
fn main() {
    unsafe {
        println!("{}", add(2, 3));
    }
}

Compile with:

rustc main.rs -L .

Rust enforcesunsafe because it can’t verify what happens inside the C function.

Step 6. Using C in Go ​

Go uses theimport "C" directive for seamless C integration.

Go Example:

/*
#include "mathlib.c"
*/
import "C"
import "fmt"
func main() {
    fmt.Println(C.add(2, 3))
}

Compile and run:

go run main.go

Go will compile your C code behind the scenes and link it automatically.

Step 7. Calling Foreign Code from C ​

You can also go the other way, call functions from another language inside C.

Example: C calling Python

#include <Python.h>
int main(void) {
    Py_Initialize();
    PyRun_SimpleString("print('Hello from Python in C!')");
    Py_Finalize();
}

Compile with:

gcc main.c -o main $(python3-config --cflags --ldflags)

This embeds a Python interpreter in your C program, powerful for scripting or AI integration.

Step 8. Data Structures Across Languages ​

FFI works best with simple, C-compatible types:

  • int,double,char *, and flat structs. Avoid C++ classes, pointers to complex structs, or variable-length arrays, they often don’t translate cleanly.

Example:

typedef struct {
    int id;
    double score;
} Record;

You can use this struct easily from Python (ctypes.Structure) or Rust (#[repr(C)] struct).

Step 9. Memory Ownership Rules ​

Always define who allocates and who frees memory.

If C allocates something:

char* greet(void) {
    char* s = malloc(32);
    sprintf(s, "Hello from C!");
    return s;
}

Then the caller (e.g., Python) must callfree() via FFI to avoid leaks. Never assume the garbage collector of another language will clean up C memory.

Step 10. Tiny Code: C Shared Library + Python ​

// greet.c
#include <stdio.h>
#include <stdlib.h>
char* greet(const char* name) {
    static char buf[64];
    snprintf(buf, sizeof(buf), "Hello, %s!", name);
    return buf;
}

Compile:

gcc -shared -fPIC -o libgreet.so greet.c

Python:

import ctypes
lib = ctypes.CDLL("./libgreet.so")
lib.greet.restype = ctypes.c_char_p
print(lib.greet(b"World"))

Output:

Hello, World!

Why It Matters ​

FFI turns C into the foundation of the software world, your C code can power systems written in any language. This is how databases, OS kernels, and AI frameworks expose APIs across ecosystems. Understanding FFI means you can build language bridges, not just programs.

Try It Yourself ​

  1. Write a simple C library (math, strings, or sorting).
  2. Load it in Python usingctypes and call its functions.
  3. Reuse the same library from Rust usingextern "C".
  4. Embed Python in C for a scripting layer.
  5. Think about which side should own and free memory.

Next, you’ll explore safer alternatives and modern C features, bounds checking, static assertions, and ways to make C code more reliable.

本站总访问 — 次