当我发现Addr#
类型时,我最近一直在寻找各种Haskell怪癖,如未装箱类型和诸如此类的东西。
GHC.Prim
包是这样描述的:
假定指向垃圾收集堆外部的任意机器地址。
这对我来说意义不大。
此外,我不断发现像这样使用类型的函数:
readIntOffAddr# :: Addr# -> Int# -> State# s -> (#State# s, Int##)
这是什么类型?我能用它做什么?为什么有必要?
作为Michael回答的补充:
Addr#
是Ptr a
下面的未装箱类型,同理Int#
是Int
下面的未装箱类型。它的内容可能被解释为机器地址,尽管就编译器和GC而言,它只是另一种整型(无论系统上指针的大小如何)。因为它是一个任意的机器地址,而不是GC管理的指针,它应该不会指向Haskell堆,因为从Haskell的层面来看,Haskell堆对象的地址是不稳定的(GC可能发生在你的程序中的任何一点,然后你的Addr#
指向的对象现在在其他地方,或者根本不在任何地方)。
通常Ptr a
/Addr#
将包含从malloc
/mmap
/等返回的指针。或者是指向C全局变量的指针,或者是C程序中指针可能指向的任何类型的东西。当与返回或修改传递的HsInt *
的内容的C函数接口时,通常使用readIntOffAddr#
。(嗯,你不会直接使用它,你会使用Int
的peekElemOff
Storable
方法,我认为这是根据readIntOffAddr#
实现的,或者你会使用peekArray
这样的更高级的函数)。
等效的* C代码为:
long readIntOffAddr(long *ptr, long offset) {
return ptr[offset];
}
Addr#
和void *
一样。该函数具有类似IO
的签名,因为它不是"纯净"的。对函数的多次调用可能返回不同的值(很明显)。
* Update (2018):我刚刚了解到将C的int
类型等同于Haskells的Int#
类型是错误的。因此,我在上面的代码片段中将int
更改为long
。这也(也许)不是100%正确,但至少对于我所见过的所有GHC实现都是如此。在GHC版本6-8中(没有检查其他版本),Int#
在32位平台上是32位宽,在64位平台上是64位宽。这与我所知道的32位和64位平台上所有C/c++实现的long
的GCC行为相匹配,因此我认为将Int#
与long
等同是一个很好的近似。在过去的3年里,没有人注意到这个小错误(或者足够关心编辑/评论)。我怀疑是否有任何Haskell/Platform/C组合,其中HsInt
!= long
,其中Haskell实现具有readIntOffAddr#
功能。