C# 关于是否使用带有不安全代码和stackalloc的fixed的混淆
我在下面有一段代码,其中有一行被注释掉了。C# 关于是否使用带有不安全代码和stackalloc的fixed的混淆,c#,arrays,types,unsafe,C#,Arrays,Types,Unsafe,我在下面有一段代码,其中有一行被注释掉了。CreateArray方法中发生的事情与注释掉的行所做的事情相同。我的问题是,当行b->ArrayItems=d未注释时,为什么它会工作,但当注释掉时返回垃圾?我不认为我必须“修复”任何东西,因为所有的信息都是非托管的。这个假设不正确吗 class Program { unsafe static void Main(string[] args) { someInstance* b = stackalloc someInst
CreateArray
方法中发生的事情与注释掉的行所做的事情相同。我的问题是,当行b->ArrayItems=d
未注释时,为什么它会工作,但当注释掉时返回垃圾?我不认为我必须“修复”任何东西,因为所有的信息都是非托管的。这个假设不正确吗
class Program
{
unsafe static void Main(string[] args)
{
someInstance* b = stackalloc someInstance[1];
someInstance* d = stackalloc someInstance[8];
b->CreateArray();
// b->ArrayItems = d;
*(b->ArrayItems)++ = new someInstance() { IntConstant = 5 };
*(b->ArrayItems)++ = new someInstance() { IntConstant = 6 };
Console.WriteLine((b)->ArrayItems->IntConstant);
Console.WriteLine(((b)->ArrayItems - 1)->IntConstant);
Console.WriteLine(((b)->ArrayItems - 2)->IntConstant);
Console.Read();
}
}
public unsafe struct someInstance
{
public someInstance* ArrayItems;
public int IntConstant;
public void CreateArray()
{
someInstance* d = stackalloc someInstance[8];
ArrayItems = d;
}
}
我的问题是,为什么当行未注释时它会工作,而当注释掉时它会返回垃圾
注释行是用来掩盖CreateArray引起的错误的。注释掉它会暴露错误。但不管怎样,这个bug还是存在的
规范明确规定:
在函数成员执行期间创建的所有堆栈分配内存块在该函数成员返回时自动丢弃
CreateArray函数分配一个块,存储一个指向该块的指针,该块被丢弃,现在有一个指向垃圾块的指针。您需要永远不要存储指向stackalloc’d块的指针,以便在该块无效后可以访问该存储。如果需要存储对该块的引用,请堆分配该块,并记住在完成后取消分配该块
请记住,在不安全代码中,您需要完全了解托管内存模型的所有内容一切。如果您不了解托管内存的所有内容,请不要编写不安全的代码
这就是说,让我们来解决您更大的困惑,即“何时必须修复内存才能获得指针?”答案很简单当且仅当内存是可移动内存时,您必须修复内存。修复将可移动内存转换为不可移动内存;这就是修理的目的
你只能取某个固定的东西的地址;如果你把某个可以移动的东西的地址记下来,然后它移动了,那么显然这个地址是错的。您需要确保在获取其地址之前内存不会移动,并且需要确保在地址再次移动后不会使用该地址。Stackalloc在调用堆栈上分配一些空间,当您从当前上下文级别向上移动时(例如,离开一个方法),该空间将丢失。您的问题是,当stackalloc位于一个方法内时,当您离开该方法时,该堆栈区域就不再是您可以使用的 因此,如果您这样做:
foo()
{
stuff = stackalloc byte[1]
Do something with stuff
}
foo()
{
byte* allocate()
{
return stackalloc[1]
}
stuff = allocate()
do something with stuff
}
“stuff”仅在foo内部有效,一旦离开foo,堆栈将被卷回,这意味着如果执行此操作:
foo()
{
stuff = stackalloc byte[1]
Do something with stuff
}
foo()
{
byte* allocate()
{
return stackalloc[1]
}
stuff = allocate()
do something with stuff
}
当您离开allocate方法时,allocate的返回值变成垃圾,这意味着“stuff”永远没有任何意义。您的假设部分正确,但理解不正确。这里有一句话: 在不安全模式下,您可以分配 堆栈上的内存,不在堆栈中 接受垃圾收集和处理 因此不需要固定。 有关更多信息,请参阅 有些语句会自动在堆栈上分配变量(即方法中的值类型),其他语句则需要使用
stackalloc
专门指定
堆栈分配的内存在方法结束后被丢弃,这就是你的问题(请参阅Eric Lipperts的答案,他在我之前写过这篇文章)。这是一个陷阱:如果你不开始胡闹并犯错误,你将永远无法了解托管内存模型的所有内容,但是,如果你必须在开始不安全的游戏之前先了解所有事情……”“永远不要存储指向……”可以说有点强?只要确保存储时间不超过堆栈分配的块,您当然可以存储它。@Marc:当然,我同意您的观点。我所说的“存储”是指超出当前范围的持久性内容。@Eric-干杯。奇怪的是,尽管在一些涉及堆栈折磨的真正疯狂的
ILGenerator
代码中有很多乱七八糟的事情,stackalloc
(和IL对应物)是我(在生产代码中)从未使用过的东西。@Dested:或者通过制作一个数组并将其固定到位,将其从托管堆中分配出去,或者通过您喜欢的任何非托管分配器将其从非托管堆中分配出去。我相信Marshal类有一堆非托管内存分配器。然后,您需要(1)在完成内存后取消锁定或释放内存,以及(2)在取消锁定/释放操作后不要保留指针。请记住,您需要正确访问内存。通过关闭安全系统,您可以管理内存模型通常负责的所有事情。