ctypes 将 c_uint64 转换为 c_char_p

发布于 2024-11-19 22:50:41 字数 948 浏览 5 评论 0原文

from ctypes import *

In [27]: sizeof(c_char_p)
Out[27]: 8

In [28]: sizeof(c_uint64)
Out[28]: 8

In [29]: cast(c_uint64(0), c_char_p)
---------------------------------------------------------------------------
ArgumentError                             Traceback (most recent call last)

/Users/az/Programmierung/PyCPython/<ipython console> in <module>()

/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ctypes/__init__.pyc in cast(obj, typ)
    479 _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
    480 def cast(obj, typ):
--> 481     return _cast(obj, obj, typ)
    482 
    483 _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)

ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong type

为什么 cast 失败?

如果 sizeof 两种类型相同,是否有任何替代 ctypes.cast 的方法?

from ctypes import * and

In [27]: sizeof(c_char_p)
Out[27]: 8

In [28]: sizeof(c_uint64)
Out[28]: 8

In [29]: cast(c_uint64(0), c_char_p)
---------------------------------------------------------------------------
ArgumentError                             Traceback (most recent call last)

/Users/az/Programmierung/PyCPython/<ipython console> in <module>()

/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ctypes/__init__.pyc in cast(obj, typ)
    479 _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
    480 def cast(obj, typ):
--> 481     return _cast(obj, obj, typ)
    482 
    483 _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)

ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong type

Why does the cast fail?

Is there any alternative to ctypes.cast which always works if sizeof both types is the same?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

够钟 2024-11-26 22:50:42

c_uint64 是内存地址吗?如果是这样,那么您可以这样做:(

>>> n = c_uint64(1234567890) #assume this is a valid memory address...beware of segfaults
>>> p = c_char_p(n.value)
>>> #or..
>>> p.value = n.value #special semantics for c_char_p - accepts both addresses and strings

请参阅 http://docs.python。 org/library/ctypes.html#ctypes.c_char_p)

或者,如果您想要做的是将 c_uint64 中存储的值重新解释为 8 字节以 null 结尾的字符缓冲区,那么您需要将指向 c_uint64 的指针转换为 c_char_p...

>>> n = c_uint64(ord("A"))
>>> n
c_ulong(65L)
>>> p = cast(pointer(n), c_char_p)
>>> p
c_char_p(47101614291216)
>>> p.value
'A'

看起来 ctypes 会保护您免受缓冲区溢出(如果它不是空终止的话):

>>> n2 = c_uint64(0xFFFFFFFFFFFFFFFF)
>>> p2 = cast(pointer(n2), c_char_p)
>>> p2.value[0]
'\xff'
>>> p2.value
'\xff\xff\xff\xff\xff\xff\xff\xff'
>>> p2.value[9]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

更新以响应 Albert 的评论:

我想知道为什么演员在这里不起作用

原因的答案在文档和代码中 - http://docs.python.org/library/ctypes.html#ctypes.cast

ctypes.cast(obj, 类型)
该函数类似于C中的强制转换运算符。它返回一个新的类型实例,该实例与obj指向相同的内存块。
type 必须是指针类型,并且 obj 必须是可以
解释为指针。

合约在 代码如下:

_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
def cast(obj, typ):
    return _cast(obj, obj, typ)

因此,强制转换的第一个参数(对象)必须可转换为 c_void_p。在中,检查输出 c_void_p_from_param()。这是到 c_void_p 的转换完成的地方。 Python 整数、Python 字符串、Python unicode 字符串、c_void_p、ctypes 数组/指针、byref 结果、函数指针、c_char_p/c_wchar_p 以及定义了 _as_parameter_() 方法的任何对象都有转换器。

ctypes 整数对象没有转换器。我只查看 2.6 代码(因为这就是您正在使用的代码),因此 2.7 或 3.x 中的情况可能并非如此。

至于理由 - 这个问题必须向开发人员提出。

...如果有一个始终有效的通用版本(对于所有情况,不是
只是 c_char_p)。

据我所知,解决方案正如我在第一个示例中所示的那样。使用 Python 整数构造指针或将其分配给指针对象的值成员(转换器知道如何从 Python 整数进行转换)。 cast() 不会起作用,因为它就是这样实现的。

Is the c_uint64 a memory address? If so, then you can do this:

>>> n = c_uint64(1234567890) #assume this is a valid memory address...beware of segfaults
>>> p = c_char_p(n.value)
>>> #or..
>>> p.value = n.value #special semantics for c_char_p - accepts both addresses and strings

(see http://docs.python.org/library/ctypes.html#ctypes.c_char_p)

Or, if what you want to do is reinterpret the value stored in the c_uint64 as an 8 byte null-terminated character buffer then you need to cast a pointer to the c_uint64 as the c_char_p...

>>> n = c_uint64(ord("A"))
>>> n
c_ulong(65L)
>>> p = cast(pointer(n), c_char_p)
>>> p
c_char_p(47101614291216)
>>> p.value
'A'

It looks like ctypes will protect you from buffer overruns if it's not null-terminated:

>>> n2 = c_uint64(0xFFFFFFFFFFFFFFFF)
>>> p2 = cast(pointer(n2), c_char_p)
>>> p2.value[0]
'\xff'
>>> p2.value
'\xff\xff\xff\xff\xff\xff\xff\xff'
>>> p2.value[9]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

UPDATE in response to Albert's comment:

I want to know why cast doesn't work here

The answer to why is in the documentation and the code - http://docs.python.org/library/ctypes.html#ctypes.cast

ctypes.cast(obj, type)
This function is similar to the cast operator in C. It returns a new instance of type which points to the same memory block as obj.
type must be a pointer type, and obj must be an object that can be
interpreted as a pointer.

The contract is enforced in the code as such:

_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
def cast(obj, typ):
    return _cast(obj, obj, typ)

So the first argument to cast (the object) must be convertible to a c_void_p. In the source, check out c_void_p_from_param(). This is where conversion to c_void_p is done. There are converters for Python integers, Python strings, Python unicode strings, c_void_p, ctypes array/pointer, byref results, function pointers, c_char_p/c_wchar_p, and any object that has the _as_parameter_() method defined.

There are no converters for ctypes integer objects. I am only looking at 2.6 code (as that's what you're using), so that might not be the case in 2.7 or 3.x.

As for the rationale - that question would have to be presented to the developers.

...if there is a generic version which always works (for all cases, not
just c_char_p).

As far as I know, the solution is just as I showed with the first example. Construct pointers with or assign to the value member of pointer objects with Python integers (the converter knows how to convert from Python integers). cast() will not work because that's how it was implemented.

扶醉桌前 2024-11-26 22:50:42

您必须获取指向该值的指针,请检查以下内容:

In [29]: i = c_uint32(0x30313233)

In [30]: cast(pointer(i), c_char_p).value
Out[30]: '3210'

You have to obtain a pointer to the value, check this:

In [29]: i = c_uint32(0x30313233)

In [30]: cast(pointer(i), c_char_p).value
Out[30]: '3210'
晌融 2024-11-26 22:50:42

我现在要这样:

def isPointerType(t):
    if issubclass(t, _ctypes._Pointer): return True
    return False

def getValueOf(obj):
    if isPointerType(obj.__class__):
        return cast(obj, c_void_p).value
    else:
        return obj.value

def static_cast(obj, t):
    newObj = t()
    if isPointerType(t):
        cast(pointer(obj), POINTER(c_void_p)).contents.value = getValueOf(obj)
    else:
        newObj.value = getValueOf(obj)
    return newObj

看起来有点复杂,但我还没有真正找到更简单的方法。

I'm going this way now:

def isPointerType(t):
    if issubclass(t, _ctypes._Pointer): return True
    return False

def getValueOf(obj):
    if isPointerType(obj.__class__):
        return cast(obj, c_void_p).value
    else:
        return obj.value

def static_cast(obj, t):
    newObj = t()
    if isPointerType(t):
        cast(pointer(obj), POINTER(c_void_p)).contents.value = getValueOf(obj)
    else:
        newObj.value = getValueOf(obj)
    return newObj

Looks somewhat complicated but I haven't really figured out a simpler way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文