You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用ctypes调用C共享库时出现参数类型错误(ArgumentError)的问题求助

使用ctypes调用C共享库时出现参数类型错误(ArgumentError)的问题求助

我在尝试用Python的ctypes调用C共享库时遇到了参数类型错误,以下是完整的重现步骤和问题详情,希望有人能帮忙分析原因并给出解决建议,谢谢!

问题重现步骤

  1. 创建shared.c文件:
#include <stdio.h>

void hello(char *name){
    printf("Hello");
}
  1. 创建loader.c文件(注:这个文件后续未实际用到,仅作为我当时的尝试文件列出):
void hello(char *name);

void main(){
    hello(char *name);
}
  1. 编译生成共享库文件:
gcc -shared shared.c -o test.so
  1. 创建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")
  1. 运行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

我已尝试的解决思路

  1. 曾参考过Windows平台下用wintypes的解决方案,但我当前使用Ubuntu 20.04,该方案不适用;
  2. 尝试用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

火山引擎 最新活动