use*_*137 9 python ctypes memory-management
假设我使用ctypes模块定义以下变量
i = c_int(4)
Run Code Online (Sandbox Code Playgroud)
然后我试着找出我使用的内存地址:
id(i)
Run Code Online (Sandbox Code Playgroud)
要么
ctypes.addressof(i)
Run Code Online (Sandbox Code Playgroud)
目前,它产生不同的价值.这是为什么?
Jon*_*art 19
您的建议应该是CPython的实现细节.
该id()
函数:
返回对象的"标识".这是一个整数,在该生命周期内保证该对象是唯一且恒定的.
CPython实现细节:这是内存中对象的地址.
虽然它们在CPython中可能是等效的,但在其他Python实现中并不能保证这一点.
为什么他们的价值观不同,即使在CPython中也是如此?
请注意c_int
:
是一个Python对象.CPython id()
将返回此地址.
包含一个4字节的C兼容int
值.ctypes.addressof()
将返回此地址.
Python对象中的元数据占用空间.因此,该4字节值可能不会出现在Python对象的最开头.
看看这个例子:
>>> import ctypes
>>> i = ctypes.c_int(4)
>>> hex(id(i))
'0x22940d0'
>>> hex(ctypes.addressof(i))
'0x22940f8'
Run Code Online (Sandbox Code Playgroud)
我们看到addressof
结果只比结果高出0x28字节id()
.玩了几次,我们可以看到情况总是这样.因此,我会说int
在整体实际值之前有0x28字节的Python对象元数据c_int
.
在上面的例子中:
c_int
___________
| | 0x22940d0 This is what id() returns
| metadata |
| |
| |
| |
| |
|___________|
| value | 0x22940f8 This is what addressof() returns
|___________|
Run Code Online (Sandbox Code Playgroud)
编辑:
在ctypes的CPython实现中,base CDataObject
(2.7.6 source)有一个b_ptr
成员,指向用于对象的C数据的内存块:
union value {
char c[16];
short s;
int i;
long l;
float f;
double d;
#ifdef HAVE_LONG_LONG
PY_LONG_LONG ll;
#endif
long double D;
};
struct tagCDataObject {
PyObject_HEAD
char *b_ptr; /* pointer to memory block */
int b_needsfree; /* need _we_ free the memory? */
CDataObject *b_base; /* pointer to base object or NULL */
Py_ssize_t b_size; /* size of memory block in bytes */
Py_ssize_t b_length; /* number of references we need */
Py_ssize_t b_index; /* index of this object into base's
b_object list */
PyObject *b_objects; /* dictionary of references we need
to keep, or Py_None */
union value b_value;
};
Run Code Online (Sandbox Code Playgroud)
addressof
将此指针作为Python整数返回:
static PyObject *
addressof(PyObject *self, PyObject *obj)
{
if (CDataObject_Check(obj))
return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr);
PyErr_SetString(PyExc_TypeError,
"invalid type");
return NULL;
}
Run Code Online (Sandbox Code Playgroud)
小C对象使用默认的16字节b_value
成员CDataObject
.如上例所示,此默认缓冲区用于c_int(4)
实例.我们可以将ctypes本身转换c_int(4)
为32位进程内省:
>>> i = c_int(4)
>>> ci = CDataObject.from_address(id(i))
>>> ci
ob_base:
ob_refcnt: 1
ob_type: py_object(<class 'ctypes.c_long'>)
b_ptr: 3071814328
b_needsfree: 1
b_base: LP_CDataObject(<NULL>)
b_size: 4
b_length: 0
b_index: 0
b_objects: py_object(<NULL>)
b_value:
c: b'\x04'
s: 4
i: 4
l: 4
f: 5.605193857299268e-45
d: 2e-323
ll: 4
D: 0.0
>>> addressof(i)
3071814328
>>> id(i) + CDataObject.b_value.offset
3071814328
Run Code Online (Sandbox Code Playgroud)
这个技巧利用了id
CPython中返回对象基址的事实.