On Sat, Apr 14, 2018 at 1:57 AM, Jach Fong <jf...@ms4.hinet.net> wrote: > eryk sun at 2018/4/14 PM 05:27 wrote: > >> The simple types c_void_p, c_char_p, and c_wchar_p are pointers. >> However, since they subclass _SimpleCData instead of _Pointer, they >> inherit the behavior of simple types. > > The ctypes document says: > "Pointer instances have a contents attribute which returns the object to > which the pointer points" > >>>> buf0 = ctypes.create_string_buffer(b'spam') > >>>> pvoid = ctypes.c_void_p(ctypes.addressof(buf0)) >>>> pvoid.contents > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > AttributeError: 'c_void_p' object has no attribute 'contents' >>>> pvoid.value > 35425816 > >>>> pp = ctypes.pointer(buf0) >>>> pp.contents > <ctypes.c_char_Array_5 object at 0x021C8F30> >>>> pp.value > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > AttributeError: 'LP_c_char_Array_5' object has no attribute 'value' > > It looks like the c_void_p is not of a pointer type:-)
c_void_p is a `void *` pointer type, but it's a simple pointer type that subclasses _SimpleCData instead of _Pointer, so it doesn't have a `contents` property but instead has a `value` property. >>> hasattr(ctypes._SimpleCData, 'contents') False >>> hasattr(ctypes._SimpleCData, 'value') True >>> hasattr(ctypes._Pointer, 'contents') True >>> hasattr(ctypes._Pointer, 'value') False Don't take the Python class hierarchy so literally that you overlook what these types ultimately are in C. Just because they don't subclass _Pointer, that doesn't mean they're not pointer types. c_void_p, c_char_p, and c_wchar_p are unquestionably pointer types. This is what the "_p" suffix means in their names. It's just these particular pointer types were implemented as simple types to get the convenience of implicit conversion. That said, sometimes the implicit conversion is a problem, in which case we use, for example, POINTER(c_char) instead of c_char_p. Look in Lib/ctypes/__init__.py to review the definitions for yourself. Here they are without the __repr__ methods: class c_void_p(_SimpleCData): _type_ = "P" class c_char_p(_SimpleCData): _type_ = "z" class c_wchar_p(_SimpleCData): _type_ = "Z" This doesn't tell you much. You have to go looking for what it means to be a simple "P" type, and in particular we're concerned with how conversion to and from native Python types is implemented. You'll find the get and set C implementations for types "P", "z", and "Z" defined in Modules/_ctypes/cfield.c. For example, for type "P", it's P_get() and P_set(). For P_get(), we use PyLong_FromVoidPtr to convert the pointer value to a Python integer, except we return None for a NULL pointer. For P_set(), we require an integer value or None (NULL). Note that the function to convert a Python integer to an integral address in C depends on the size of a C `long` or `long long` compared to the size of a C `void *` pointer. In particular, this design accommodates 64-bit Windows, on which a `long` is 32-bit and thus too small for a 64-bit pointer value, so we call PyLong_AsUnsignedLongLongMask instead of PyLong_AsUnsignedLongMask. -- https://mail.python.org/mailman/listinfo/python-list