假设我使用ctypes module
定义以下变量i = c_int(4)
然后我尝试使用以下方法找出i的内存地址:
id(i)
或
ctypes.addressof(i)
目前,产生不同的价值。那是为什么?
答案 0 :(得分:18)
您建议的情况应该是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'
我们看到addressof
结果仅比id()
的结果高0x28字节。玩了几次,我们可以看到情况总是这样。因此,我要说在整个int
的实际c_int
值之前有0x28字节的Python对象元数据。
在上面的例子中:
c_int
___________
| | 0x22940d0 This is what id() returns
| metadata |
| |
| |
| |
| |
|___________|
| value | 0x22940f8 This is what addressof() returns
|___________|
修改强>
在ctypes的CPython实现中,基类CDataObject
(2.7.6源)有一个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;
};
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;
}
小C对象使用b_value
的默认16字节CDataObject
成员。如上例所示,此默认缓冲区用于c_int(4)
实例。我们可以将ctypes自身转换为在32位进程中内省c_int(4)
:
>>> 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
这个技巧利用了CPython中id
返回对象基址的事实。