假设我使用 ctypes module 定义了以下变量
i = c_int(4)
然后我尝试找出我使用的内存地址:
id(i)
或
ctypes.addressof(i)
目前,它产生不同的值。这是为什么?
最佳答案
您所建议的应该是 CPython 的实现细节。
id()
功能:
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime.
CPython implementation detail: This is the address of the object in memory.
虽然它们在 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字节。反复试验几次,我们可以看到情况总是如此。因此,我会说在整个 c_int
中实际 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 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;
};
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 对象使用 CDataObject
的默认 16 字节 b_value
成员。如上例所示,此默认缓冲区用于 c_int(4)
实例。我们可以打开 ctypes 自身以在 32 位进程中自省(introspection) 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
返回对象基地址的事实。
关于python - CPython 中的 id(obj) 和 ctypes.addressof(obj) 有什么区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23600052/