python - 有关Ctypes(可能还有python实现)如何处理数据类型的问题?

标签 python ctypes

以下是我的源代码及其在Win10命令行中的输出。

from ctypes import *
class eH():
    x= c_uint(0)
    print (type(x) == c_uint)
print (type(eH.x) == c_uint)
print (eH.x)
print (type(eH.x))
print (type(eH.x) == c_ulong)
print (c_uint == c_ulong)
print (c_int == c_long)
print ("\nEnd of eH prints\n#####")

class eHardware(Structure):
    _fields_= [("xyz", c_uint)]
a= eHardware()
a.xyz= eH.x
print (a.xyz)
print (a.xyz == eH.x)
print (type(a.xyz))
print (type(c_uint(a.xyz)))


命令行输出在链接中:https://pastebin.com/umWUDEuy

我注意到的第一件事是c_uint == c_ulong输出True。这是否意味着ctypes动态分配类型并在内存中将它们视为相同?如果我想将类似的脚本移植到类型敏感的语言(例如C),这种设计是否会有任何含义?

其次,在第17行中,我分配a.xyz = eH.x,但在第19行中,a.xyz == eH.x求值为False。另外,a.xyz的类型已转换为int,而eH.x的类型为c_uint(或c_ulong,因为它总是在调用type()时求值)
预先感谢您的答复。

最佳答案

我注意到的第一件事是c_uint == c_ulong输出True。


这在the docs的顶部进行了解释:


  注意:一些代码示例引用了ctypes c_int类型。在sizeof(long) == sizeof(int)的平台上,它是c_long的别名。因此,如果希望使用c_long,则不要混淆是否打印了c_int —它们实际上是同一类型。


如果您想知道为什么要这样做,那就是改善与C代码的交互。

C是一种弱类型的语言,intlong始终是不同的类型,但是您始终可以通过复杂的整数提升和缩小规则在它们之间隐式转换。1在许多平台上,intlong碰巧都是32位,所以这些规则并不重要,但是在其他平台上,long是64位,2确实如此。这使得编写可以在您的计算机上运行的代码真的很容易,但是通过破坏堆栈(甚至可能以一种可以被攻击者利用的方式)对其他人进行隔离。

ctypes试图通过明确定义c_intc_long的别名(且仅当它们具有相同的大小)来统治这一点。所以:


如果要小心地在调用的C函数需要c_int时始终使用int,而在需要c_long时始终使用long,则代码将是可移植的,就像在C语言中一样。
如果您随意混合和匹配它们,并且在您的计算机上碰巧是安全的,它将在您的计算机上正常工作,就像在C语言中一样。
如果您随意混合和匹配它们,然后尝试在不安全的计算机上运行它们,则应该从ctypes中获取一个异常,而不是一个段错误。





  这是否意味着ctypes动态分配类型并在内存中将它们视为相同?


我想这取决于您“即时”的意思。如果查看the source,您会看到在编译模块时,它会执行以下操作:

if _calcsize("i") == _calcsize("l"):
    # if int and long have the same size, make c_int an alias for c_long
    c_int = c_long
    c_uint = c_ulong
else:
    class c_int(_SimpleCData):
        _type_ = "i"
    _check_size(c_int)

    class c_uint(_SimpleCData):
        _type_ = "I"
    _check_size(c_uint)


当然,通常,当您使用import ctypes时,会得到一个预编译的ctypes.pyc文件,3因此,将c_int的一种定义或另一种定义冻结在该pyc中。因此,从这个意义上讲,您不必担心它是动态的。但是您始终可以删除.pyc文件,或者告诉Python根本不要使用它们。或者,如果您确实愿意,甚至可以将Monkeycc ctypes.c_int变成其他名称。因此,从这个意义上讲,如果您愿意的话,它绝对是动态的。4




  如果我想将类似的脚本移植到类型敏感的语言(例如C),这种设计是否会有任何含义?


好吧,设计的重点是尽可能地匹配C(尤其是用于构建CPython解释器的C编译器的实现定义的细节),同时解决一些陷阱因此,很少用ctypes设计一个接口,然后用C实现它。通常情况正好相反。但是偶尔也会发生(通常与映射到numpy数组的多处理共享内存有关)。

在这种情况下,只需遵循相同的规则:确保在Python代码中将c_intc_long保持直线,并在C代码中将它们与intlong匹配,一切都会正常进行。您肯定希望在C编译器中启用(并读取)警告,以在混合它们时尝试捕获。并为调试期间偶尔出现的段错误或内存损坏做好准备,但是在C.5中,您始终需要为此做好准备




  此外,a.xyz的类型已转换为int,而eH.x的类型为c_uint


当您访问结构成员,将参数传递给C函数并返回值等时,到本机类型的转换非常复杂。 95%的时间它可以满足您的要求,最好不要担心。

第一次达到其他5%的水平时(通常是因为您有一个c_char_p,您想将其视为指针而不是字符串……),实际上没有替代品来阅读文档并学习默认转换和_as_parameter__CData类以及restypeerrcheck等。然后在交互式解释器中进行一些实验,也许还会阅读源代码。6



1.大多数现代编译器都会警告您缩小转换范围,甚至让您有选择地将其转换为错误。

2.在过去,当最初设计ctypes时,int通常为16位,但是效果是相同的。

3.如果您使用Windows或Mac Python安装程序,RPM或DEB二进制软件包或系统上预装的Python,则在构建二进制软件包时,stdlib几乎总是在其他人的机器上编译的。如果是从源代码构建的,则通常是在构建或安装时在计算机上进行编译的。如果不是,通常在您第一次import ctypes时进行编译。

4.尽管我不知道你为什么要这么做。只需用其他名称定义自己的类型就更容易了……

5.您可能想考虑使用一种静态类型的语言,并且与C兼容,但是具有比C更严格和更强的类型系统,例如Rust,或者至少是C ++或D。然后,编译器可以做更多的事情来帮助您确保正确无误。但是,这里的权衡与在C语言和另一种语言之间进行选择时始终相同。没有任何涉及ctypes的特定内容。

6.最终举手示意,宣布从现在开始,您将只使用cffi而不是ctypes,这种情况一直持续到您第一次遇到cffi的一个怪癖时…

关于python - 有关Ctypes(可能还有python实现)如何处理数据类型的问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50765955/

相关文章:

python - 将 UV 更改从 .fbx 文件合并回 Maya

python - 在 ctypes.Structure 中动态设置 _fields_

python - Windows错误: [Error 127] The specified procedure could not be found

python - 通过 FFI 调用 Rust 函数时发生访问冲突

python - 没有名为 slack 的模块

python - 排除周末的两天之间的差异(以小时为单位)

python - pickle 定义为具有 PyObject* 成员的 C 结构的 Python 扩展类型

用于在多个多边形之间构建成本最低的路径的 Python 脚本 : How to speed up it?

Python - 如何获取内存地址的值?

python - 如何将 C 头文件转换为 Python ctypes 代码?