我需要获取NULL无效指针的地址。如果我在Python中使用NULL 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)
。
如果我将字段类型更改为指向任何ctype类型的指针
class Effect(structure):
_fields_ = [("ptr", POINTER(c_double)]
# get effect instance from C shared library
print(addressof(effect.ptr))
我检查了C端堆上的地址是否正确
140530973811664
不幸的是,不能从c_void_p更改字段类型。我该怎么办?
说明
这是@CristiFati之后的C代码,适合我的具体情况。 struct是在C中分配的,我在Python中得到了ptr的信息,现在我需要在struct中传递对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_ptraddress]:ARG0:[指针地址:],ARG1:0x00007FC2E1AD3770 从C-[pointers.c](11)-[print_ptraddress]:ARG0:[指针的地址:],ARG1:0x00007FC2E1AD3770
但是如果我将double *更改为void *和c_void_p,则会收到错误消息,因为python中的c_void_p设置为None
答案 0 :(得分:1)
ctypes ([Python 3]: ctypes - A foreign function library for Python)旨在能够从 Python 与“ C ”对话,从而使其< em> Python 友好,这意味着没有指针,内存地址……任何东西(至少,尽可能精确些)。
因此,在幕后,它起到了一些“魔术作用”,在这种情况下,它位于您与目标之间。
@ EDIT0 :更新了答案以更好地解决(已阐明的)问题。
示例:
>>> 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'
这是两种在理论上是相同的类型之间的平行点:
ctypes.c_char_p
:如您所见, s0 已自动转换为 bytes 。这是有道理的,因为它是 Python ,这里不需要使用指针。每次使用它时,都必须将每个成员从 ctypes 转换为普通的 Python (反之亦然),这也很烦人。
当前场景不是“快乐流程”的一部分,它只是一个极端情况,没有任何功能(或者至少我不知道有什么功能)
ctypes.POINTER(ctypes.c_char)
(命名为 char_p ):它更接近 C ,并提供了您需要的功能,但是看起来还很多(从 Python 角度来看)更难使用
问题在于ctypes.c_void_p
与 #1。 类似,因此您没有想要的 OOTB 功能,而且没有ctypes.c_void
与 #2。 一起使用。但是,可以做到这一点,但是还需要其他工作。
众所周知的( C )规则是:
AddressOf(Structure.Member)= AddressOf(Structure)+ OffsetOf(Structure,Member) ((提防注意内存对齐,他们可以“玩弄脏记”)。
在这种情况下,事情再简单不过了。这是一个示例:
dll.c :
#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);
}
code.py :
#!/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
可以看到, effect 的地址与 effect 的 ptr 的地址相同。但这又是最简单的情况。但是,如所解释的,一般的解决方案是优选的。但是,这不可能,但是可以解决:
Effect *get_effect(void **ptr)
,并将地址存储在参数中ctypes.c_void_p
字段包含涉及 POINTER 的内容(例如:{ {1}})。定义与 C 不同,从语义上讲,它们不是 OK ,但最后它们都是指针注意:请不要忘记拥有一个破坏 get_effect 返回的指针的函数(以避免内存泄漏)
答案 1 :(得分:1)
因此,在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;
}
在Python中(忽略上述效果定义):
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: