在python当中不能单独地传一个字符(可以通过对string的下标访问渠道),C中和整形相关的类型全部转成
int
。
对于
c_char
和
c_wchar
分别对应python的
1-character bytes object
和
1-character string
,前者存放一个
ASCII
字符,后者存放一个
utf-32
。
除了上述基础类型外,我们还可以给ctypes传
回调函数
、
数组
、
结构体
等。
传递数字参数
我们通过
c_int
和
c_float
传递float和int,c_int()返回一个类对象,该类的
value
成员为所传的值
v = c_int(101)
print(v.value)
下面的代码中我们在
TestCtypesNumber
函数中打印该函数的参数,并在python代码中调用
!FILENAME testctypesnumber.cpp
#include <stdio.h>
#ifdef _WIN32
#define XLIB __declspec(dllexport)
#else
#define XLIB
#endif
extern "C" XLIB void TestCtypesNumber(int x,float y,bool isNum)
printf("In C TestCtypesNumber makefile %d %f %d\n",x,y,isNum);
if (isNum){
printf("true");
else {
printf("false");
print("Test Ctypes Number")
from ctypes import *
lib = CDLL("testctypesnumber")
lib.TestCtypesNumber(101, 99.1, True)
input()
我们将python的代码和dll放在一个路径下面,在VS中将项目属性——>链接器——>常规——>输出文件设置成和testctypesnumber.cpp同一目录。
为了能在vs中调试dll文件,我们进入到项目属性——>调试——>命令,选择浏览,找到安装的python.exe所在路径,命令参数填写testctypesnumber.py
现在我们在VS中运行代码,发现程序直接退出,说明python代码中发生了异常。为了能显示错误,我们要捕获异常。更改python代码如下
print("Test Ctypes Number")
from ctypes import *
lib = CDLL("testctypesnumber")
try:
lib.TestCtypesNumber(101, 99.1, True)
except Exception as ex:
print("TestCtypeNumber error ",ex)
input()
再次运行代码,可以看到打印的错误如下
TestCtypeNumber error argument 2: <class 'TypeError'>: Don't know how to convert parameter 2
可以看出,int类型可以直接转换(不用调用c_int()),而float类型是不能直接转换的。我们更改一下代码
lib.TestCtypesNumber(101, c_float(99.1), True)
再次运行,程序可以正常输出结果
In C TestCtypesNumber makefile 101 99.099998 1
字符串的传递
有两种字符串string和byte,分别用c_wchar_p()和c_char_p()进行传递。
string和byte都是只读类型,在内部不可修改。如果想传一个字符串由C语言进行修改,可以用create_string_buffer创建一个可修改的字符串的缓冲,传给C语言,再由C语言进行写入。
为了让我们的代码在C和C++中均能编译通过,我们定义宏XEXT。定义一个函数TestStringA来测试byte的传递
#include <stdio.h>
#ifdef __cplusplus
#define XEXT extern "C"
#else
#define XEXT
#endif
#ifdef _WIN32
#define XLIB XEXT __declspec(dllexport)
#else
#define XLIB XEXT
#endif
XLIB void TestStringA(const char* str, int size)
printf("TestStringA : %s (%d)\n", str, size);
更改我们的python代码如下
print("Test Ctypes String")
from ctypes import *
try:
lib = CDLL("testctypesstring")
str1 = "test string in python"
lib.TestStringA(str1, len(str1))
except Exception as ex:
print("TestCtypestring error ",ex)
input()
在VS中调试执行testctypesstring.cpp代码,发现程序报错并且立刻退出,无法定位程序错误在哪一行。
我们在控制台中执行testctypes.py代码,发现lib.TestStringA(str1, len(str1))这一行代码报错
IndentationError: unindent does not match any outer indentation level
提示是缩进的错误!然而代码明明是对齐的。这里有一个技巧,在Notepad++中可以显示出所有字符:
视图—>显示符号—>显示空格与制表符。发现是空格和Tab键混用了。
将缩进全部改用Tab后,再次在命令行中运行程序,发现传给TestStringA函数的是string类型而非byte
将testctypes.py第5行代码改为str1 = b"test string in python"进行转换,程序正常执行。
上面测试的是普通字符,为了测试宽字符,我们在testctypesstring.cpp中再添加函数TestStringW
XLIB void TestStringW(const wchar_t* str, int size)
wprintf(L"TestStringW : %s (%d)\n", str, size);
在testctypes.py中增加下面代码
!FILENAME testctypes.py
wstr = "wide string in python"
lib.TestStringW(wstr,len(wstr))
运行程序,输出如下
Test Ctypes String
TestStringA : test string in python (21)
TestStringW : wide string in python (21)
普通字符与宽字符均不需要显示转换
Python传给C的字符串在C中是可以修改的,但是建议不要这么做,这会造成语法的不一致(应为string类型在python中不可更改)。
如果我们需要修改,可以通过create_string_buffer()函数。
buf = create_string_buffer(100)
我们在testctypesstring.cpp中再添加函数TestStringBuffer
XLIB void TestStringBuffer(char* str, int size)
str[0] = '@';
str[1] = '#';
str[2] = '\0';
printf("TestStringBuffer : %s (%d)\n", str, size);
在testctypes.py中增添如下代码
!FILENAME testctypes.py
buf = create_string_buffer(10)
lib.TestStringBuffer(buf)
print("in python buf = ",buf.raw, buf.value, len(buf), sep = '\n')
运行代码结果如下
TestStringBuffer : @# (1447823194)
in python buf =
b'@#\x00\x00\x00\x00\x00\x00\x00\x00'
b'@#'
如有有些字符串需要在C中算完了再传给python,可以使用`create_string_buffer`