使用ctypes调用C共享库时出现参数类型错误(ArgumentError)的问题求助
使用ctypes调用C共享库时出现参数类型错误(ArgumentError)的问题求助
我在尝试用Python的ctypes调用C共享库时遇到了参数类型错误,以下是完整的重现步骤和问题详情,希望有人能帮忙分析原因并给出解决建议,谢谢!
问题重现步骤
- 创建
shared.c文件:
#include <stdio.h> void hello(char *name){ printf("Hello"); }
- 创建
loader.c文件(注:这个文件后续未实际用到,仅作为我当时的尝试文件列出):
void hello(char *name); void main(){ hello(char *name); }
- 编译生成共享库文件:
gcc -shared shared.c -o test.so
- 创建Python脚本
reproduce_error.py:
import ctypes lib_name = './test.so' lib=ctypes.cdll.LoadLibrary(lib_name) init_hello=lib.hello init_hello.argtypes=[ctypes.c_char_p] init_hello("Test")
- 运行Python脚本:
python reproduce_error.py
报错信息
执行后触发如下错误栈:
Traceback (most recent call last): File "reproduce_error.py", line 8, in <module> init_hello('Test') ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
我已尝试的解决思路
- 曾参考过Windows平台下用wintypes的解决方案,但我当前使用Ubuntu 20.04,该方案不适用;
- 尝试用
create_string_buffer将字符串转为字节缓冲,但调用时仍传原字符串,结果还是报同样错误,修改后的脚本如下:
import ctypes from ctypes import c_bool lib_name = './test.so' lib=ctypes.cdll.LoadLibrary(lib_name) init_hello=lib.hello init_hello.argtypes=[ctypes.c_char_p] init_hello.restype = c_bool s = ctypes.create_string_buffer(lib_name.encode("utf-8")) init_hello('Test')
我现在的核心困惑是:Python中的字符串"Test"为什么无法被ctypes.c_char_p正确识别?希望能找到Python与C之间字符串参数传递的正确方式。
问题分析与解决方法
这个问题是Python3与C交互时的常见小坑,核心原因很直白:
Python3的普通字符串是Unicode类型(str),而ctypes.c_char_p对应C语言的char*,它仅接受字节类型(bytes),不兼容Unicode字符串。
你用create_string_buffer的思路方向是对的,但最后调用函数时还是传入了原Unicode字符串,没用到转换后的字节对象,所以错误依然存在。这里给你两种简单有效的解决方法:
方法1:直接传递编码后的字节串
调用函数时,用encode()将Unicode字符串转为字节串(默认用UTF-8编码即可):
import ctypes lib_name = './test.so' lib=ctypes.cdll.LoadLibrary(lib_name) init_hello=lib.hello init_hello.argtypes=[ctypes.c_char_p] # 将字符串编码为字节串后传递 init_hello("Test".encode())
方法2:用ctypes.c_char_p显式包装字节参数
也可以先编码字符串,再用c_char_p包装后传入:
import ctypes lib_name = './test.so' lib=ctypes.cdll.LoadLibrary(lib_name) init_hello=lib.hello init_hello.argtypes=[ctypes.c_char_p] # 直接使用字节字面量包装 init_hello(ctypes.c_char_p(b"Test")) # 或者先编码再包装 init_hello(ctypes.c_char_p("Test".encode("utf-8")))
额外小提示
你创建的loader.c文件实际上没有参与任何编译或调用流程,完全可以删除,不会影响测试或实际使用。如果之后需要处理Unicode字符场景,也可以考虑用ctypes.c_wchar_p对应C的wchar_t*,此时Python可直接传普通str字符串,但你的C代码需要改为宽字符处理逻辑(比如将printf替换为wprintf,参数类型改为wchar_t*)。
备注:内容来源于stack exchange,提问作者Diogo




