Python ctypes 将 bytes 转换为任意 C 类型
1. 问题起因
C++ 数组初始化,对于不同类型的数组,可以使用 memset() 来初始化。
#include <cstring>
int a[10001];
double d[10001];
int main() {
// 初始化为 0
memset(a, 0, sizeof(a));
// 初始化为很大的数(0x7f7f7f7f = 2139062143)
memset(a, 0x7f, sizeof(a));
// 初始化为很小的数(0xafafafaf = -1347440721)
memset(a, 0xaf, sizeof(a));
// 初始化为 0
memset(d, 0, sizeof(d));
// 初始化为很大的数(1.38242e+306)
memset(d, 0x7f, sizeof(d));
}
我们想知道最后赋值出来的结果到底是多少。其实这个问题就是 C 类型的数据底层二进制表示是什么,以及我们修改二进制表示后,C 类型的值是多少。
如果学过计算机原理,
int类型的值很容易计算,但是float和double很难计算,因为需要应用 IEEE 754 等计算方法,如果不遵循这些标准的数据类型就更难以表示了。
Python 对于 C 类型有完善支持,我们使用 Python 快速将二进制表示和其值进行互相转换。
2. bytes 转换为任意 C 类型
下面的方法实现了 bytes 转换到任意类型的方式。
from ctypes import POINTER, c_char_p, c_double, cast, sizeof
from typing import TypeVar
_T = TypeVar('_T')
def cast_bytes(data_bytes: bytes, ctype: type[_T]) -> _T:
"""Cast bytes to a ctype"""
assert len(data_bytes) >= sizeof(ctype)
return cast(
c_char_p(data_bytes),
POINTER(ctype)
).contents
v = cast_bytes(b'\x7f' * 8, c_double)
print(v.value)
这里使用了 Python 的 泛型注解,可以删去。这样我们就获得了一个 c_double 类型的 v 变量,使用 v.value 很容易获得其值。
以上的代码相当于:
#include <iostream>
using namespace std;
int main() {
char a[] = "\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f";
double value = *(double*)a;
cout << value << endl;
return 0;
}
如果需要使用 Python 将 C 类型转换为二进制表示,使用
memoryview即可。