代码之家  ›  专栏  ›  技术社区  ›  Dan

ctypes如何获取空c_void_p字段的地址?

  •  1
  • Dan  · 技术社区  · 7 年前

    我需要得到一个空指针的地址。如果我在Python中创建一个空c_void_p,那么获取它的地址没有问题:

    ptr = c_void_p(None)
    print(ptr)
    print(ptr.value)
    print(addressof(ptr))
    

    给予

    c_void_p(None)
    None
    4676189120
    

    class Effect(structure):
        _fields_ = [("ptr", c_void_p)]
    

    其中ptr在C中被初始化为NULL。当我用python访问它时

    myclib.get_effect.restype = POINTER(Effect)
    effect = myclib.get_effect().contents
    print(effect.ptr)
    

    给予 None addressof(effect.ptr) .

    class Effect(structure):
        _fields_ = [("ptr", POINTER(c_double)]
    
    # get effect instance from C shared library
    print(addressof(effect.ptr))
    

    我已经检查了我是否在C端的堆上获得了正确的地址

    140530973811664
    

    澄清

    下面是@CristiFati后面的C代码,以了解我的具体情况。结构是在C中分配的,我在Python中得到一个ptr,现在我需要在结构中传递一个对ptr的引用。首先,如果我把ptr设为双倍,就没问题了!

    #include <stdio.h>
    #include <stdlib.h>
    
    #define PRINT_MSG_2SX(ARG0, ARG1) printf("From C - [%s] (%d) - [%s]:  ARG0: [%s], ARG1: 0x%016llX\n", __FILE__, __LINE__, __FUNCTION__, ARG0, (unsigned long long)ARG1)
    
    typedef struct Effect {
        double* ptr;
    } Effect;
    
    void print_ptraddress(double** ptraddress){
        PRINT_MSG_2SX("Address of Pointer:", ptraddress);
    }
    
    Effect* get_effect(){
        Effect* pEffect = malloc(sizeof(*pEffect));
        pEffect->ptr = NULL;
        print_ptraddress(&pEffect->ptr);
        return pEffect;
    }
    

    在Python中

    from ctypes import cdll, Structure, c_int, c_void_p, addressof, pointer, POINTER, c_double, byref
    clibptr = cdll.LoadLibrary("libpointers.so")
    
    class Effect(Structure):
        _fields_ = [("ptr", POINTER(c_double))]
    
    clibptr.get_effect.restype = POINTER(Effect)
    pEffect = clibptr.get_effect()
    
    effect = pEffect.contents
    clibptr.print_ptraddress(byref(effect.ptr))
    

    从C-[pointers.C](11)-[print_ptradress]:ARG0:[指针地址:],ARG1:0x00007FC2E1AD3770 从C-[pointers.C](11)-[print_ptradress]:ARG0:[指针地址:],ARG1:0x00007FC2E1AD3770

    2 回复  |  直到 7 年前
        1
  •  1
  •   CristiFati    7 年前

    ctypes ( [Python 3]: ctypes - A foreign function library for Python )意味着能够“与人交谈” 从…起 python ,这就是 python 没有指针,内存地址。。。不管怎样

    @编辑0 :更新答案以更好地适应(澄清)问题。

    例子:

    >>> import ctypes
    >>> s0 = ctypes.c_char_p(b"Some dummy text")
    >>> s0, type(s0)
    (c_char_p(2180506798080), <class 'ctypes.c_char_p'>)
    >>> s0.value, "0x{:016X}".format(ctypes.addressof(s0))
    (b'Some dummy text', '0x000001FBB021CF90')
    >>>
    >>> class Stru0(ctypes.Structure):
    ...     _fields_ = [("s", ctypes.c_char_p)]
    ...
    >>> stru0 = Stru0(s0)
    >>> type(stru0)
    <class '__main__.Stru0'>
    >>> "0x{:016X}".format(ctypes.addressof(stru0))
    '0x000001FBB050E310'
    >>> stru0.s, type(stru0.s)
    (b'Dummy text', <class 'bytes'>)
    >>>
    >>>
    >>> b = b"Other dummy text"
    >>> char_p = ctypes.POINTER(ctypes.c_char)
    >>> s1 = ctypes.cast((ctypes.c_char * len(b))(*b), char_p)
    >>> s1, type(s1)
    (<ctypes.LP_c_char object at 0x000001FBB050E348>, <class 'ctypes.LP_c_char'>)
    >>> s1.contents, "0x{:016X}".format(ctypes.addressof(s1))
    (c_char(b'O'), '0x000001FBB050E390')
    >>>
    >>> class Stru1(ctypes.Structure):
    ...     _fields_ = [("s", ctypes.POINTER(ctypes.c_char))]
    ...
    >>> stru1 = Stru1(s1)
    >>> type(stru1)
    <class '__main__.Stru1'>
    >>> "0x{:016X}".format(ctypes.addressof(stru1))
    '0x000001FBB050E810'
    >>> stru1.s, type(stru1.s)
    (<ctypes.LP_c_char object at 0x000001FBB050E6C8>, <class 'ctypes.LP_c_char'>)
    >>> "0x{:016X}".format(ctypes.addressof(stru1.s))
    '0x000001FBB050E810'
    

    这是理论上相同的两种类型之间的平行关系:

    1. ctypes.c_char_p s0 已自动转换为 . 这是有道理的,因为它是 python ,这里不需要使用指针;另外,必须将每个成员从 ctypes python (和viceversa),每次使用它时。

    2. ctypes.POINTER(ctypes.c_char) (命名为 查鲁普 ):这更接近于 ,并提供您所需的功能,但正如所见,它也更难(从 python

    问题是 ctypes.c_void_p 类似于 #1. ,所以没有 OOTB 功能满足您的需求,而且没有 ctypes.c_void 配合 #2. . 但是,

    C )规则是:
    AddressOf(Structure.Member)=AddressOf(Structure)+OffsetOf(Structure,Member) ( 当心 属于 内存对齐

    对于这个特殊的案例,事情再简单不过了。下面是一个例子:

    :

    #include <stdio.h>
    #include <stdlib.h>
    
    #if defined(_WIN32)
    #  define DLL_EXPORT __declspec(dllexport)
    #else
    #  define DLL_EXPORT
    #endif
    
    #define PRINT_MSG_2SX(ARG0, ARG1) printf("From C - [%s] (%d) - [%s]:  ARG0: [%s], ARG1: 0x%016llX\n", __FILE__, __LINE__, __FUNCTION__, ARG0, (unsigned long long)ARG1)
    
    
    static float f = 1.618033;
    
    typedef struct Effect {
        void *ptr;
    } Effect;
    
    
    DLL_EXPORT void test(Effect *pEffect, int null) {
        PRINT_MSG_2SX("pEffect", pEffect);
        PRINT_MSG_2SX("pEffect->ptr", pEffect->ptr);
        PRINT_MSG_2SX("&pEffect->ptr", &pEffect->ptr);
        pEffect->ptr = !null ? NULL : &f;
        PRINT_MSG_2SX("new pEffect->ptr", pEffect->ptr);
    }
    

    :

    #!/usr/bin/env python3
    
    import sys
    from ctypes import CDLL, POINTER, \
        Structure, \
        c_int, c_void_p, \
        addressof, pointer
    
    
    DLL = "./dll.dll"
    
    
    class Effect(Structure):
        _fields_ = [("ptr", c_void_p)]
    
    
    def hex64_str(item):
        return "0x{:016X}".format(item)
    
    
    def print_addr(ctypes_inst, inst_name, heading=""):
        print("{:s}{:s} addr: {:s} (type: {:})".format(heading, "{:s}".format(inst_name) if inst_name else "", hex64_str(addressof(ctypes_inst)), type(ctypes_inst)))
    
    
    def main():
        dll_dll = CDLL(DLL)
        test_func = dll_dll.test
        test_func.argtypes = [POINTER(Effect), c_int]
    
        effect = Effect()
        print_addr(effect, "effect")
        test_func(pointer(effect), 1)
        print(effect.ptr, type(effect.ptr))  # Not helping, it's Python int for c_void_p
        try:
            print_addr(effect.ptr, "effect.ptr")
        except:
            print("effect.ptr: - wrong type")
        print_addr(effect, "effect", "\nSecond time...\n    ")
        print("Python addrs (irrelevant): effect: {:s}, effect.ptr: {:s}".format(hex64_str(id(effect)), hex64_str(id(effect.ptr))))
    
    
    if __name__ == "__main__":
        print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
        main()
    

    输出

    (py35x64_test) e:\Work\Dev\StackOverflow\q053531795>call "c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64
    
    (py35x64_test) e:\Work\Dev\StackOverflow\q053531795>dir /b
    code.py
    dll.c
    
    (py35x64_test) e:\Work\Dev\StackOverflow\q053531795>cl /nologo /DDLL /MD dll.c  /link /NOLOGO /DLL /OUT:dll.dll
    dll.c
       Creating library dll.lib and object dll.exp
    
    (py35x64_test) e:\Work\Dev\StackOverflow\q053531795>dir /b
    code.py
    dll.c
    dll.dll
    dll.exp
    dll.lib
    dll.obj
    
    (py35x64_test) e:\Work\Dev\StackOverflow\q053531795>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py
    Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
    
    effect addr: 0x000001FB25B8CB10 (type: <class '__main__.Effect'>)
    From C - [dll.c] (21) - [test]:  ARG0: [pEffect], ARG1: 0x000001FB25B8CB10
    From C - [dll.c] (22) - [test]:  ARG0: [pEffect->ptr], ARG1: 0x0000000000000000
    From C - [dll.c] (23) - [test]:  ARG0: [&pEffect->ptr], ARG1: 0x000001FB25B8CB10
    From C - [dll.c] (25) - [test]:  ARG0: [new pEffect->ptr], ARG1: 0x00007FFFAFB13000
    140736141012992 <class 'int'>
    effect.ptr: - wrong type
    
    Second time...
        effect addr: 0x000001FB25B8CB10 (type: <class '__main__.Effect'>)
    Python addrs (irrelevant): effect: 0x000001FB25B8CAC8, effect.ptr: 0x000001FB25BCC9F0
    

    如图所示,地址为 影响 与的地址相同 影响 . 但同样,这是最简单的可能情况。但是,正如一般解决方案所解释的,它是首选的。然而,这是不可能的,但可以解决:

    • [SO]: Getting elements from ctype structure with introspection? (时间很长,我很难找到当前的解决方案,特别是因为有两种容器类型( 结构 排列 )筑巢可能性;希望它没有bug(或者尽可能接近):)
    • C 接口类似于: Effect *get_effect(void **ptr)
    • python ) 结构,而不是 ctypes.c\u void\u p 指针 (例如: ("ptr", POINTER(c_ubyte)) ). 定义将不同于 C ,从语义上来说,事物不是 好啊 但最后他们都是指针

    :不要忘记使用一个函数来销毁 取得效果 (避免 内存泄漏 )

        2
  •  1
  •   Dan    7 年前

    因此,在python bug跟踪器中提出这个问题后,Martin Panter和Eryk Sun提供了一个更好的解决方案。

    确实有一个无文件记录的文件 offset

    offset = type(Effect).ptr.offset
    ptr = (c_void_p).from_buffer(effect, offset)
    

    通过使用私有字段并添加属性,我们可以更优雅地将其包装到类中:

    class Effect(Structure):
        _fields_ = [("j", c_int),
                    ("_ptr", c_void_p)]
        @property
        def ptr(self):
            offset = type(self)._ptr.offset
            return (c_void_p).from_buffer(self, offset)
    

    我在指针之前添加了一个整数字段,所以偏移量不仅仅是零。为了完整起见,下面是与此解决方案相适应的代码,显示了它的工作原理。在C中:

    #include <stdio.h>
    #include <stdlib.h>
    
    #define PRINT_MSG_2SX(ARG0, ARG1) printf("%s : 0x%016llX\n", ARG0, (unsigned long long)ARG1)
    
    typedef struct Effect {
        int j;
        void* ptr;
    } Effect;
    
    void print_ptraddress(double** ptraddress){
        PRINT_MSG_2SX("Address of Pointer:", ptraddress);
    }
    
    Effect* get_effect(){
        Effect* pEffect = malloc(sizeof(*pEffect));
        pEffect->ptr = NULL;
        print_ptraddress(&pEffect->ptr);
        return pEffect;
    }
    

    from ctypes import cdll, Structure, c_int, c_void_p, POINTER, byref
    clibptr = cdll.LoadLibrary("libpointers.so")
    
    clibptr.get_effect.restype = POINTER(Effect)
    effect = clibptr.get_effect().contents
    clibptr.print_ptraddress(byref(effect.ptr))
    

    产量

    Address of Pointer: : 0x00007F9EB248FB28
    Address of Pointer: : 0x00007F9EB248FB28
    

    here