[Python]关于ctypes使用char*指针与bytes相互转换的问题

最近研究人脸识别,需要用python调用so动态库,涉及到c/c++中的指针字符串转Python的bytes对象的问题。
按照ctypes的文档,直观方式是先创建对应的类型数组,再将指针取地址一一赋值:

from ctypes import *
 
 
p=(c_char * 10)()
for i in range(10):
    p[i] = i
 
b=bytes(bytearray(p))
print(b)

搜寻了各种资料,都未能找到更好的。。。直到ctypes.string_at

_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
def string_at(ptr, size=-1):
    """string_at(addr[, size]) -> string
 
    Return the string at addr."""
    return _string_at(ptr, size)

于是char*转bytes可以直接用string_at方法,传入指针地址,以及字符串长度即可。

同样的问题,bytes对象需要传给c/c++代码。。。
直观方式同样是创建char数组array,拷贝bytes之后,再用cast强制转换成c_char_p

from ctypes import *
 
 
p=(c_char * 10)()
for i in range(10):
    p[i] = i
 
m=cast(p, c_char_p)
print(m)

比较奇葩的是cast得到的对象,如果我们直接用bytes对象cast。。。

from ctypes import *
 
 
b=b'0123456789'
m=cast(p, c_char_p)
print(m)

吼吼,奇迹出现了,bytes对象cast成了char*指针。。。用string_at转换看看

string_at(m)

总结一下:
1、bytes基于Buffer Protocol,查看其c实现https://hg.python.org/cpython/file/3.4/Objects/bytesobject.c
2、string_as的c代码https://hg.python.org/cpython/file/3717b1481d1b/Modules/_ctypes/_ctypes.c

static PyObject *
string_at(const char *ptr, int size)
{
	if (size == -1)
		return PyString_FromString(ptr);
	return PyString_FromStringAndSize(ptr, size);
}

3、cast的c代码同样在_ctypes.c(https://hg.python.org/cpython/file/3717b1481d1b/Modules/_ctypes/_ctypes.c)

static PyObject *
cast(void *ptr, PyObject *src, PyObject *ctype)
{
	CDataObject *result;
	if (0 == cast_check_pointertype(ctype))
		return NULL;
	result = (CDataObject *)PyObject_CallFunctionObjArgs(ctype, NULL);
	if (result == NULL)
		return NULL;
 
	/*
	  The casted objects '_objects' member:
 
	  It must certainly contain the source objects one.
	  It must contain the source object itself.
	 */
	if (CDataObject_Check(src)) {
		CDataObject *obj = (CDataObject *)src;
		/* CData_GetContainer will initialize src.b_objects, we need
		   this so it can be shared */
		CData_GetContainer(obj);
		/* But we need a dictionary! */
		if (obj->b_objects == Py_None) {
			Py_DECREF(Py_None);
			obj->b_objects = PyDict_New();
			if (obj->b_objects == NULL)
				goto failed;
		}
		Py_XINCREF(obj->b_objects);
		result->b_objects = obj->b_objects;
		if (result->b_objects && PyDict_Check(result->b_objects)) {
			PyObject *index;
			int rc;
			index = PyLong_FromVoidPtr((void *)src);
			if (index == NULL)
				goto failed;
			rc = PyDict_SetItem(result->b_objects, index, src);
			Py_DECREF(index);
			if (rc == -1)
				goto failed;
		}
	}
	/* Should we assert that result is a pointer type? */
	memcpy(result->b_ptr, &ptr, sizeof(void *));
	return (PyObject *)result;
 
  failed:
	Py_DECREF(result);
	return NULL;
}