我在 CPython 上使用 Python 3.9.6。
我试图理解 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)应该指向存储int值的内存位置。当该地址被解释为 int 并且其值被转储时,这一点得到确认。我假设 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?
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