CPython中id(obj)和ctypes.addressof(obj)有什么区别 [英] What is the difference between id(obj) and ctypes.addressof(obj) in CPython

查看:393
本文介绍了CPython中id(obj)和ctypes.addressof(obj)有什么区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我使用 ctypes模块

i = c_int(4)

然后,我尝试使用以下方法找出i的内存地址:

and afterwards I try to find out the memory address of i using:

id(i)

ctypes.addressof(i)

,目前产生不同的值.为什么会这样?

which, at the moment, yield different values. Why is that?

推荐答案

您所建议的情况是CPython的实现细节.

What you are suggesting should be the case is an implementation detail of 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实现细节:这是对象在内存中的地址.

CPython implementation detail: This is the address of the object in memory.

尽管它们在CPython中可能是等效的,但不能保证在其他Python实现中也是如此.

While they might be equivalent in CPython, this is not guaranteed to be true in other implementations of Python.

为什么即使在CPython中它们也有不同的值?

请注意,c_int:

  • 一个Python对象. CPython的id()将返回它的地址.

  • is a Python Object. CPython's id() will return the address of this.

包含 4字节与C兼容的int值. ctypes.addressof()将返回它的地址.

contains a 4-byte C-compatible int value. ctypes.addressof() will return the address of this.

Python对象中的元数据占用空间.因此,这个4字节的值可能不会在Python对象的最开始就存在.

The metadata in a Python object takes up space. Because of this, that 4-byte value probably won't live at the very beginning of the Python object.

看这个例子:

>>> import ctypes
>>> i = ctypes.c_int(4)
>>> hex(id(i))
'0x22940d0'
>>> hex(ctypes.addressof(i))
'0x22940f8'

我们看到addressof结果仅比id()结果高0x28字节.反复试验几次,我们可以看到情况总是如此.因此,我想说的是,整个c_int中实际int值之前的Python对象元数据有0x28字节.

We see that the addressof result is only 0x28 bytes higher than the result of id(). Playing around with this a few times, we can see that this is always the case. Therefore, I'd say that there are 0x28 bytes of Python object metadata preceding the actual int value in the overall c_int.

在我上面的示例中:

   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数据的存储块:

In the CPython implementation of ctypes, the base CDataObject (2.7.6 source) has a b_ptr member that points to the memory block used for the object's C data:

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整数返回:

addressof returns this pointer as a Python integer:

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)实例.我们可以在32位进程中打开ctype以自省c_int(4):

Small C objects use the default 16-byte b_value member of the CDataObject. As the example above shows, this default buffer is used for the c_int(4) instance. We can turn ctypes on itself to introspect c_int(4) in a 32-bit process:

>>> 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返回对象的基地址的事实.

This trick leverages the fact that id in CPython returns the base address of an object.

这篇关于CPython中id(obj)和ctypes.addressof(obj)有什么区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆