ctypes.c_int和ctypes.py_object之间的区别

0 投票
1 回答
48 浏览
提问于 2025-04-13 20:36

我在使用Python 3.9.6的CPython版本。

我想搞清楚PyObject和c_int在内部表示数字时有什么区别。

>>> x = 1234
>>> c1 = c_int(x)
>>> id(c1)
4339302720
>>> type(c1)
<class 'ctypes.c_int'>
>>> c1.value
1234
>>> cast(addressof(c1),POINTER(c_int)).contents.value
1234
>>>

这里,c1是一个PyObject,它的地址是4339302720。它表示一个ctypes.c_int对象,这个addressof(c1)应该指向存储整数值的内存位置。当我们把这个地址当作整数来解释并查看它的值时,可以确认这一点。我猜c1.value在内部做的也是同样的事情。

2.


>>> c2 = cast(id(x),py_object)
>>> id(c2)
4337584448
>>> type(c2)
<class 'ctypes.py_object'>
>>> c2.value
1234
>>> cast(addressof(c2),POINTER(c_int)).contents.value
43544656

这里,c2也是一个PyObject,它的地址是4337584448。它表示另一个ctypes.py_object对象吗?

因为这是一个ctypes对象,所以它支持addressof(c2)。

那么,addressof(c2)指向哪里呢?因为它并不指向整数1234。你能不能给我推荐一个好的参考资料,让我从基础知识开始理解这个问题?

另外,c2.value是如何在内部计算出1234的呢?

1 个回答

2

ctypes.addressof 用于获取一个 ctypes.c_int 的地址,这个地址指向存储 C 整数的 4 个字节。

>>> from ctypes import *
>>> x = 1234
>>> a = c_uint(x)
>>> hex(addressof(a))  # int*
'0x231c2135498'
>>> string_at(addressof(a),4).hex()  # little-endian 4-byte integer
'd2040000'
>>> 0x04d2  # byte-swapped and converted to int
1234

ctypes.addressof 用于获取一个 ctypes.py_object 的地址,这个地址指向存储 PyObject* 的 8 个字节。这就是为什么在第二种情况下你会得到一个奇怪的数字。

>>> from ctypes import *
>>> x = 1234
>>> y = py_object(x)
>>> hex(addressof(y))  # PyObject*
'0x231c2135498'
>>> string_at(addressof(y),8).hex()  # little-endian 8-byte pointer (64-bit OS)
'300713c231020000'  # note lower bytes of this matches the "bad" value of cast below.
>>> hex(id(x))  # In CPython id(x) is the PyObject* address of x.
'0x231c2130730'

如果把那个 PyObject* 当作 C int* 来处理:

>>> cast(addressof(y), POINTER(c_int)).contents
c_long(-1038940368)
>>> import struct
>>> struct.pack('<l',-1038940368).hex() # convert to little-endian 4-byte integer
'300713c2'  # See that it the lower 4 bytes of the little-endian 8-byte PyObject*.

一个 py_object(1234) 的值永远不会和 c_int 的值相匹配,因为它从一开始就不是一个被 c_int 包裹的值。不过,你可以把它当作 Python 对象取出来:

>>> x=1234
>>> y=cast(id(x),py_object)  # better, just y=py_object(x)
>>> y
py_object(1234)
>>> y.value
1234

撰写回答