I'm on Python 3.9.6 on CPython.
I am trying to understand the difference in the internal representation of PyObject and c_int for a number.
>>> 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
>>>
Here, c1 is a PyObject itself and has it's address as 4339302720. It represents a ctypes.c_int object, and this addressof(c1) should point to the memory location where the value of the int is stored. This is confirmed when that address is interpreted as an int and it's value dumped. I assume c1.value is doing the same internally.
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
Here, c2 is also a PyObject and has it's address as 4337584448. Does it represent another ctypes.py_object object?
Since this a ctypes object, it supports addressof(c2).
What does addressof(c2) point to, since it does not point to the integer 1234? Could you also supplement your answer with a pointer to a good refernce for this so I could understand from "first principles"?
Also, how does c2.value internally evaluate to 1234?
The
ctypes.addressofof actypes.c_intis the address of the 4 bytes that store the C integer.The
ctypes.addressofof actypes.py_objectis the address of the 8 bytes that store thePyObject*the object refers to. That's why in the second case you get a strange number.If that PyObject* is treated as a C int* instead:
The value of a
py_object(1234)will never match thec_intvalue because it was never ac_int-wrapped value to begin with. but you can get it out as Python object: