# import ctypes
:Python 的标准库。
- 官方文档 (opens new window)
- 允许 Python 加载 C 的动态链接库,直接调用其中的函数,或者将 Python 函数作为回调函数传给 C 函数。
# 用法示例
将 C 代码文件编译成动态链接库:
gcc –o 1.so -shared –fPIC 1.c # 在 Linux 上用 gcc 编译 C 代码 g++ –o 1.so -shared –fPIC 1.cpp # 在 Linux 上用 g++ 编译 C++ 代码 cl /LD 1.c # 在 Windows 上用 Virtual Studio 的 cl 命令编译
如果想调用 C++ 代码,则要将待调用的函数用
extern "C"
声明,再编译成库文件。如下:extern "C" { Class1 obj; # 可以在 extern "C" 内调用 C++ 的方法,但不能定义 C++ 特有的对象 void fun1() { obj.method(); }
如果想在 Windows 上编译成库文件,则要将 C、C++ 代码中待调用的函数加上前缀 __declspec(dllexport)、extern "C" __declspec(dllexport)声明。
在 Python 中调用:
import ctypes lib = ctypes.CDLL("msvcrt.dll") lib.printf(b"hello world!") lib = ctypes.CDLL("./1.so") r = lib.fun1(ctypes.c_int(1))
# 创建 C 变量
ctypes 支持创建任意 C 语言数据类型的变量,包括数组、指针、结构体。
- 创建这些变量是为了传给 C 函数,不能在 Python 中使用。
- 例:
i = ctypes.c_int(1) # 创建一个 C 语言的 int 型变量 print(i.value) # 获取 C 语言变量的值 c = ctypes.c_char(b"A") # 创建一个 C 语言的 char 型变量 array = ctypes.c_char * 10 # 创建一个数组(格式为 数据类型名*整数 ) p = ctypes.c_char_p(id("hello")) # 创建一个 C 语言的 char 型指针 ctypes.byref(ctypes.c_int(1)) # 返回一个 C 语言变量实例的指针 ctypes.POINTER(ctypes.c_char) # 返回指向某种数据类型的指针类型(可用作声明函数原型)
- 调用动态库时,C 函数返回的变量可能会被内存回收,因此要注意及时在 Python 中拷贝它的值。
# 传递字符串
如果想让 C 函数返回一个字符串,可以传入一个字符数组作为实参,让 C 函数修改它。也可以让 C 函数在运行时创建一个字符数组或动态内存,返回其指针。
ctypes 提供了一种创建字符串缓存的方法,可以把它传给 C 函数作为实参。如下:
创建字符串缓存:
>>> p = ctypes.create_string_buffer(10) # 创建一个 char 型数组,用作缓存,长度为 10 >>> p = ctypes.create_string_buffer(b"hello\n") # 也可以根据 bytes 创建数组缓存 >>> p.value # 获取数组存储的字符串内容 b'hello\n' >>> p.raw # 获取数组存储的原始值(并且可通过 p.raw 修改这个值) b'hello\n\x00' >>> ctypes.create_unicode_buffer(10) # 创建一个 wchar 型数组缓存 <ctypes.c_wchar_Array_10 object at 0x000001F879539248>
编写 C 函数:
int fun1(char *buffer){ printf("%s", buffer); *buffer = 'A'; *(buffer+1) = 'a'; return 0; }
在 Python 中调用:
>>> lib.fun1(p) hello 0 >>> p.value b'Aallo\n'
下例是让 C 代码创建一个动态内存,存储字符串并返回:
定义 C 函数:
#include <stdlib.h> char *buffer = NULL; char *fun1(void){ buffer = (char *)malloc(1*1024); memset(buffer , 'a', 1*1024); return buffer; } int free_buffer(void){ free(buffer); return 0; }
在 Python 中调用:
>>> lib.fun1.restype = ctypes.c_char_p # 声明 C 函数的返回类型(默认采用 int 型) >>> a = lib.fun1(p) >>> a # 可以直接查看 a 的值,因为它被 ctypes 自动转换成了 bytes 类型 b'aaaaaaaaaaa…… >>> lib.free_buffer() 0
# 回调函数
可以在 Python 中写一个回调函数,作为参数传给 C 函数,让 C 程序调用。例:
在 Python 中定义:
# 声明回调函数的返回值类型、各个形参的类型 @ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int) def callback(a, b): print("hello world") return a ** b lib.fun1(ctypes.c_int(1), ctypes.c_int(2), callback)
在 C 中调用:
int fun1(int x,int y,int (*callback)(int,int)){ return callback(x,y); }