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

2024-04-26 09:43:59 发布

您现在位置:Python中文网/ 问答频道 /正文

下面是我的源代码及其在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\u uint==c\u 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()时总是计算为) 提前感谢您的回复。你知道吗


Tags: 命令行from类型源代码typectypesclassint
1条回答
网友
1楼 · 发布于 2024-04-26 09:43:59

First thing I noticed is that c_uint == c_ulong outputs True.

这在the docs的顶部解释:

Note: Some code samples reference the ctypes c_int type. On platforms where sizeof(long) == sizeof(int) it is an alias to c_long. So, you should not be confused if c_long is printed if you would expect c_int — they are actually the same type.

如果您想知道它为什么这样做,那么它是为了改进与C代码的交互。你知道吗

C是一种弱类型语言intlong总是不同的类型,但是您可以通过复杂的整数提升和缩小规则在它们之间隐式地进行强制转换。1在许多平台上,intlong恰好都是32位的,因此这些规则并不重要,但在其他平台上,long是64位的,2他们就是这样做的。这使得编写在你的机器上运行的代码变得非常容易,但是通过破坏堆栈(甚至可能以攻击者可以利用的方式)在其他人的机器上产生错误。你知道吗

ctypes试图通过明确定义c_intc_long的别名来控制这一点,当且仅当它们的大小相同时。所以:

  • 如果您在调用的C函数需要int时总是小心地使用c_int,在它需要long时总是使用c_long,那么您的代码将是可移植的,就像在C中一样
  • 如果你随意地混合和匹配它们,而这恰好在你的机器上是安全的,它将在你的机器上工作,就像在C中一样
  • 如果您任意地混合和匹配它们,然后尝试在不安全的机器上运行它们,那么您应该从ctypes得到一个异常,而不是segfault。你知道吗

Does that mean ctypes dynamically assign types on the fly and treat them as same in memory?

我想这取决于你所说的“在飞”是什么意思。如果您查看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变成别的东西。所以,从这个意义上说,如果你想它是动态的,它肯定是动态的


Would this design has any implication if I want to port the similar script to a type-sensitive language, say C.

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

在这种情况下,只需遵循相同的规则:确保在Python代码中保持c_intc_long,并将它们与C代码中的intlong相匹配,这样就可以了。您肯定希望在C编译器中启用(并读取)警告,以便在将它们混合在一起时尝试捕获它们。在调试过程中,要为偶尔出现的segfaults或内存损坏做好准备,但在C中,您始终需要做好准备。5


Also, the type of a.xyz is been converted to int, while eH.x is of type c_uint

当您访问结构成员、将参数传递到C函数和返回值等时,转换为本机类型是相当复杂的。95%的时候它只是做你想做的,最好不要担心。你知道吗

当你第一次点击另外5%的时候(通常是因为你有一个c_char_p你想把它当作一个指针而不是一个字符串……),没有什么可以替代阅读文档和学习默认转换和_as_parameter__CData类和restypeerrcheck等等。在交互式口译员中做一些实验,也许可以阅读源代码。6


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

2。在过去,当ctypes首次被设计时,它更常见于int为16位,但效果相同。

3。如果您使用Windows或macpython安装程序,或者RPM或DEB二进制软件包,或者系统中预装的Python,那么stdlib几乎总是在构建二进制软件包时在其他机器上编译的。如果您是从源代码构建的,它通常是在您的计算机上构建或安装时编译的。如果没有,它通常在您第一次import ctypes时被编译。

4。虽然我不知道你为什么要这样。用不同的名称定义自己的类型更容易…

5。您可能想考虑使用静态类型和C兼容的语言,但它比C更像是一个更严格和更强大的类型系统,比如RIST,或者至少C++或D.,那么编译器可以做更多的事情来帮助您确定事情的进展。但是这里的折衷和在C语言和另一种语言之间进行选择时的折衷是一样的;这里没有任何特定的东西。

6。最后把你的手举在空中,宣布从现在起你只会使用cffi而不是ctypes,这一直持续到你第一次遇到cffi的怪癖…

相关问题 更多 >