杰瑞科技汇

Windows Python如何调用C代码?

主要有三种主流方法,我会按照从最推荐到最特定场景的顺序进行详细讲解:

Windows Python如何调用C代码?-图1
(图片来源网络,侵删)
  1. 使用 CFFI (C Foreign Function Interface) - 现代、推荐的首选
  2. 使用 ctypes (Python 内置库) - 简单、无需编译,适合快速调用
  3. 使用 Cython - 将 Python 代码编译为 C 扩展,性能极高

使用 CFFI (C Foreign Function Interface)

CFFI 是一个现代、功能强大的 Python 包,用于与 C 代码交互,它提供了两种调用模式:ABI (Application Binary Interface) 和 API (Application Programming Interface)。

  • ABI 模式:直接编译和链接 C 代码,生成一个 DLL (.dll) 或共享库 (.so),Python 加载这个库,这是最常用、最简单的方式。
  • API 模式:在编译时就需要 C 编译器和 Python 的头文件 (Python.h),类似于 Cython,更复杂,但功能更强大。

这里我们重点讲解最常用的 ABI 模式。

步骤 1: 编写 C 代码

创建一个名为 my_c_functions.c 的文件。

// my_c_functions.c
#include <stdio.h>
// 一个简单的加法函数
int add(int a, int b) {
    printf("C 函数被调用: add(%d, %d)\n", a, b);
    return a + b;
}
// 一个打印字符串的函数
void print_message(const char* message) {
    printf("C 函数打印: %s\n", message);
}

步骤 2: 编译 C 代码为 DLL (Windows)

你需要一个 C 编译器,MinGW (GCC for Windows) 或 Visual Studio 的 cl.exe,这里以 MinGW 为例。

Windows Python如何调用C代码?-图2
(图片来源网络,侵删)
  1. 安装 MinGW: 你可以从 MSYS2MinGW-w64 安装,安装后,确保 gcc 命令在系统 PATH 中。

  2. 编译命令: 打开命令行 (CMD 或 PowerShell),进入到 my_c_functions.c 所在的目录,然后执行:

    # -o: 指定输出文件名
    # -shared: 生成共享库 (DLL)
    # -O2: 基本的优化选项
    gcc my_c_functions.c -o my_c_functions.dll -shared -O2

    执行成功后,你会得到 my_c_functions.dll 文件。

步骤 3: 在 Python 中使用 CFFI 调用 DLL

安装 CFFI 库:

Windows Python如何调用C代码?-图3
(图片来源网络,侵删)
pip install cffi

创建一个 Python 脚本,main.py

# main.py
from cffi import FFI
# 1. 定义 C 函数的接口
ffi = FFI()
# cdef() 用于声明 C 语言的函数和变量
ffi.cdef("""
    int add(int a, int.b);
    void print_message(const char *message);
""")
# 2. 加载编译好的 DLL 库
# Windows 下使用 'dllname',Linux/macOS 下使用 'libname.so'
try:
    # 假设 my_c_functions.dll 和 main.py 在同一目录
    c_lib = ffi.dlopen("./my_c_functions.dll")
except OSError as e:
    print(f"错误: 无法加载 DLL. 请确保 my_c_functions.dll 在同一目录下.")
    print(f"详细错误: {e}")
    exit()
# 3. 调用 C 函数
# CFFI 会自动处理 Python int 和 C int,以及 Python str 和 C char* 之间的转换
# 调用 add 函数
result = c_lib.add(10, 25)
print(f"Python 收到返回值: {result}")
# 调用 print_message 函数
# 注意:Python 字符串需要转换为 C 字符串
c_lib.print_message("你好,来自 Python 的世界!")

运行 python main.py,你将看到如下输出:

C 函数被调用: add(10, 25)
Python 收到返回值: 35
C 函数打印: 你好,来自 Python 的世界!

使用 ctypes (Python 内置库)

ctypes 是 Python 的标准库,允许你调用 DLL 中的函数,它非常简单,不需要额外的安装,但代码风格可能不如 CFFI 现代。

步骤 1 & 2: 与 CFFI 相同

确保你已经有了 my_c_functions.c 和编译好的 my_c_functions.dll

步骤 3: 在 Python 中使用 ctypes

创建 main_ctypes.py

# main_ctypes.py
import ctypes
# 1. 加载 DLL
try:
    # Windows 下使用 cdll.LoadLibrary
    c_lib = ctypes.CDLL("./my_c_functions.dll")
except OSError as e:
    print(f"错误: 无法加载 DLL. 请确保 my_c_functions.dll 在同一目录下.")
    print(f"详细错误: {e}")
    exit()
# 2. 定义函数的参数和返回值类型 (非常重要!)
# 如果不定义,ctypes 可能会错误地解释内存,导致程序崩溃
# 为 add 函数设置参数类型和返回类型
c_lib.add.argtypes = [ctypes.c_int, ctypes.c_int]
c_lib.add.restype = ctypes.c_int
# 为 print_message 函数设置参数类型和返回类型 (void -> None)
c_lib.print_message.argtypes = [ctypes.c_char_p] # C 的 const char* 对应 ctypes.c_char_p
c_lib.print_message.restype = None
# 3. 调用 C 函数
# 调用 add 函数
result = c_lib.add(100, 200)
print(f"Python 收到返回值: {result}")
# 调用 print_message 函数
# Python 字符串需要编码为 bytes (C 风格的字符串)
# 使用 .encode('utf-8') 是最安全的方式
message_bytes = "你好,ctypes!".encode('utf-8')
c_lib.print_message(message_bytes)

运行 python main_ctypes.py,输出如下:

C 函数被调用: add(100, 200)
Python 收到返回值: 300
C 函数打印: 你好,ctypes!

使用 Cython

Cython 的目标不是简单地“调用”C,而是让你用一种类似 Python 的语法(Cython 语言)来编写代码,Cython 将其编译成 C 代码,最后再编译成 Python 可以导入的模块(一个 .pyd 文件,在 Windows 上等价于 DLL),这种方法最适合将计算密集型的 Python 代码“翻译”成高性能的 C 代码。

步骤 1: 编写 Cython 代码 (.pyx 文件)

创建一个名为 my_module.pyx 的文件。

# my_module.pyx
# 这是一个 Cython 文件,语法是 Python + C 类型声明
# 导入 C 库中的函数
# 注意:这里我们声明 extern C 函数,就像在 C 中一样
cdef extern from "my_c_functions.h":
    int add(int a, int b)
    void print_message(const char *message)
# 定义一个 Python 可以调用的函数
def call_c_functions():
    print("--- 从 Cython 模块调用 C 函数 ---")
    result = add(5, 7)
    print(f"C 函数 add 返回: {result}")
    print_message("这条消息来自 Cython 调用。")
    return result

最佳实践:创建一个 C 头文件 为了让 Cython 知道 C 函数的签名,创建一个 my_c_functions.h 文件。

// my_c_functions.h
#ifndef MY_C_FUNCTIONS_H
#define MY_C_FUNCTIONS_H
int add(int a, int b);
void print_message(const char *message);
#endif

步骤 2: 编译 Cython 代码为 .pyd 文件

你需要安装 Cython 和一个 C 编译器。

  1. 安装 Cython:

    pip install cython
  2. 创建 setup.py 文件: 这是编译脚本。

    # setup.py
    from setuptools import setup
    from Cython.Build import cythonize
    import os
    # 告诉编译器去哪里找我们的 C 源文件
    extra_compile_args = [
        f'-I{os.path.abspath(".")}'  # 将当前目录包含在头文件搜索路径中
    ]
    setup(
        ext_modules = cythonize("my_module.pyx"),
        extra_compile_args=extra_compile_args
    )
  3. 执行编译:

    python setup.py build_ext --inplace

    执行后,你会得到一个 my_module.cp3x-win_amd64.pyd (版本号和架构可能不同) 的文件。

步骤 3: 在 Python 中导入并使用

创建 main_cython.py

# main_cython.py
# 像导入普通 Python 模块一样导入编译好的 .pyd 文件
import my_module
# 调用我们在 my_module.pyx 中定义的函数
my_module.call_c_functions()

运行 python main_cython.py,输出如下:

--- 从 Cython 模块调用 C 函数 ---
C 函数被调用: add(5, 7)
C 函数 add 返回: 12
C 函数打印: 这条消息来自 Cython 调用。

总结与对比

特性 CFFI ctypes Cython
易用性 (ABI模式) (非常简单) (需要学习 Cython 语法)
性能 (接近原生) (接近原生) 极高 (代码本身就是 C)
主要用途 调用外部 C 库 快速调用外部 C 库 将 Python 代码编译为高性能扩展
依赖 pip install cffi Python 内置 pip install cython, C 编译器
工作流 C -> DLL -> Python 调用 C -> DLL -> Python 调用 Cython (.pyx) -> C -> .pyd -> Python 导入
类型检查 在 Python 端定义 cdef 在 Python 端定义 argtypes 在 Cython 代码中静态声明
推荐场景 大多数情况下的首选,特别是当你的核心逻辑在 C 库中时。 快速原型验证或当无法安装 cffi 时。 优化现有 Python 代码,或开发需要极致性能的 Python 扩展。

最终建议

  • 如果你只是想调用一个现成的 C/C++ 库强烈推荐使用 CFFI,它的设计更现代,API 更友好,并且性能很好。
  • 如果你只是想快速测试一个简单的 C 函数,并且不想安装任何第三方包:ctypes 是你的不二之选。
  • 如果你的 Python 程序有一个计算瓶颈,你想让它运行得更快:学习并使用 Cython,这是将 Python 性能提升到 C 级别的标准方法。
分享:
扫描分享到社交APP
上一篇
下一篇