如果需要背景,请参见here。简而言之,问题是:“bracket (mallocBytes n) free
和 allocaBytes
与ojit_a之间的实际区别是什么?”
通常在C语言中,Foreign.Marshall.Alloc
在堆栈上分配,而alloca
在堆上分配。我不确定在Haskell中发生了什么,但是除了速度以外,我不希望上述方程之间有差异。但是,如果您单击背景链接,您就会知道,使用编译后的代码malloc
会导致“双重释放或损坏”,而bracket (mallocBytes n) free
可以正常工作(在GHCi中根本看不到问题,在两种情况下都可以正常工作)。
到目前为止,我已经花了两天时间进行痛苦的调试,并且我非常确信allocaBytes
在某种程度上是不稳定的,其余代码是可靠的。我想找出bracket (mallocBytes n) free
有什么问题。
最佳答案
bracket (mallocBytes size) free
将使用C的malloc
和free
,而allocaBytes size
将使用由GHC垃圾回收管理的内存。这本身已经是一个巨大的差异,因为Ptr
的allocaBytes
可能被未使用(但已分配)的内存包围着:
import Control.Exception
import Control.Monad (forM_)
import Foreign.Marshal.Alloc
import Foreign.Ptr
import Foreign.Storable
-- Write a value at an invalid pointer location
hammer :: Ptr Int -> IO ()
hammer ptr = pokeElemOff ptr (-1) 0 >> putStrLn "hammered"
main :: IO ()
main = do
putStrLn "Hammer time! Alloca!"
forM_ [1..10] $ \n ->
print n >> allocaBytes 10 hammer
putStrLn "Hammer time! Bracket"
forM_ [1..10] $ \n ->
print n >> bracket (mallocBytes 10) free hammer
结果:
Hammer time! Alloca!
1
hammered
2
hammered
3
hammered
4
hammered
5
hammered
6
hammered
7
hammered
8
hammered
9
hammered
10
hammered
Hammer time! Bracket
1
hammered
<program crashes>
如您所见,尽管我们使用了
arr[-1] = 0
,但allocaBytes
高兴地忽略了该错误。但是,如果您写位置free
,-1
(通常)会在您的脸上炸毁。如果另一个分配的内存区域出现内存损坏,它也会在您的脸上炸毁*。另外,对于
allocaBytes
,指针很可能指向已分配的内存中的某个位置,而不是指向某个内存的开头,例如nursery = malloc(NURSERY_SIZE);
// ...
pointer_for_user = nursery + 180;
// pointer_for_user[-1] = 0 is not as
// much as a problem, since it doesn't yield undefined behaviour
这意味着什么?好吧,
allocaBytes
不太可能在您的脸上炸毁,但是代价是您没有注意到C代码变体是否会导致内存损坏。更糟糕的是,一旦您在allocaBytes
返回的范围之外书写,您可能会无声地破坏其他Haskell值。但是,我们在这里谈论的是不确定的行为。上面的代码可能会或可能不会在您的系统上崩溃。它也可能在
allocaBytes
部分崩溃。如果我是你,我会trace the
malloc
and free
calls。*我曾经在程序中间出现“双重使用免费”错误。调试了所有内容,重写了大多数“不良”例程。不幸的是,该错误在调试版本中消失了,但在发行版本中再次出现。原来,在
main
的前十行中,我不小心将b[i - 1]
写入了i = 0
。关于haskell - "bracket (mallocBytes n) free"和 "allocaBytes"有什么区别?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41410805/