ctypes.c_int和ctypes.py_object之间的区别
我在使用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