关于Ctypes(以及可能的python实现)如何处理数据类型的问题?

时间:2018-06-08 17:44:07

标签: 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()时计算) 提前感谢您的回复。

1 个答案:

答案 0 :(得分:1)

  

我注意到的第一件事是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_intint时需要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根本不要使用它们。或者你甚至可以将monkeypatch ctypes.c_int变成别的东西,如果你真的想要的话。因此,从这个意义上说,如果你想要的话,它肯定是动态的。 4

  

如果我想将类似的脚本移植到类型敏感的语言,例如C,这个设计是否有任何暗示。

嗯,设计的重点是尽可能地匹配C(特别是用于构建CPython解释器的C编译器的实现定义细节),同时解决一些问题。处理C的陷阱因此,用ctypes设计接口然后用C实现它是非常罕见的;通常它是反过来的。但偶尔会发生这种情况(通常与映射到numpy数组的多处理共享内存有关...)。

在这种情况下,请遵循相同的规则:确保在您的Python代码中保持c_intc_long,并将它们与intlong匹配你的C代码,事情会有效。您肯定希望在C编译器中启用(和读取)警告,以便在您将它们混合时尝试捕获。并且在调试期间为偶尔的段错误或内存损坏做好准备,但是你总是需要在C中做好准备。 5

  

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

当您访问struct成员,将参数传递给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,这会持续到你第一次碰到{{1}之一时1}}&#39; s怪癖......