Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#out参数的不安全版本_C#_Unsafe - Fatal编程技术网

C#out参数的不安全版本

C#out参数的不安全版本,c#,unsafe,C#,Unsafe,任务是编写一个方法来初始化声明的变量,就像使用out参数一样,但使用不安全的上下文。下面的不安全代码在C++中工作,但在C语言中打印0。有人能帮忙吗 UPD。代码在发布模式下工作。 static void InitBox(out Box box) { box = new Box(100); } unsafe static void InitBoxUnsafe(Box** box) { Box b = new Box(500

任务是编写一个方法来初始化声明的变量,就像使用out参数一样,但使用不安全的上下文。下面的不安全代码在C++中工作,但在C语言中打印0。有人能帮忙吗

UPD。代码在发布模式下工作。

    static void InitBox(out Box box)
    {
        box = new Box(100);
    }

    unsafe static void InitBoxUnsafe(Box** box)
    {
        Box b = new Box(500);
        (*box) = &b;
    }

    static void Main(string[] args)
    {
        //// The task is to write a code that does the same
        // Box box;
        // InitBox(out box);
        // box.Print();
        //// but using unsafe context

        //// I wrote this code, but it's not working (output is 0), can anyone tell why?
        unsafe
        {
            Box* pBox;
            Box** ppBox = &pBox;
            InitBoxUnsafe(ppBox);
            pBox->Print();
        }
    } 

    struct Box
    {
         private readonly int num;
         public Box(int number)
         {
              num = numberl
         }
         public void Print()
         {
              Console.WriteLine(num);
         }
    }

<>代码只在C++中工作,因为您只使用非常简单的应用程序来尝试它。

您正在捕获堆栈上的一个变量,当您进入另一个堆栈帧时,该变量将被覆盖,这提示了许多有趣且难以跟踪的错误

安全C#版本之所以有效,是因为它实际上并没有捕获局部变量的地址,而是将其值复制到上面的帧中

现在,如果显式地为结构分配非托管内存,您应该能够做到这一点。当然,如果您希望出于性能原因这样做,这可能会使您的性能变得更差,而且这必然会给您带来大量与使用越来越不安全的代码相关的问题:)

也可以简单地复制该值,而不是从不安全的方法中传播地址,但我也不想弄乱它

所以,主要的问题是:
out
ref
有什么问题?为什么您试图使用
不安全的
代码来做一些安全代码可以轻松、干净地完成的事情

编辑:

在处理不安全代码时,查看实际发出的汇编代码可能是值得的。这并不能给您任何保证,因为C#编译器和JIT编译器可能在不同的计算机上产生不同的结果(甚至可能在不同的时间在同一台计算机上),但它可以给您一些见解。在这种情况下,汇编中的代码可以简化为:

主要内容:

  • 将[ebp-8](当前为堆栈顶部)设置为零(基本上就是
    Box*pBox;
    行)。因此,在[ebp-8],我们有
    pBox
  • pBox
    的地址通过
    ecx
    传递到
    InitBoxUnsafe
    (因此
    ecx
    现在的值为
    ebp-8
InitBoxUnsafe(有点明显,但仍然很有趣-这不是内联的):

  • 我们在堆栈顶部创建一个新的
    结构(基本上归结为
    mov[esp],0
    mov[esp],500
    ——在堆栈上分配值类型非常简单)
  • 将值
    esp
    存储在
    eax
    中,这样
    eax
    现在就有了新的
    框的地址
    “实例”
  • 最后,将
    eax
    存储在
    [ecx]
    中,这样
    Main
    范围中的
    pBox
    变量现在在
    InitBoxUnsafe
    范围中具有局部变量的地址。此时,指针仍然有效,长方体仍然在堆栈上
  • 当我们从该方法返回时,它的所有堆栈都会弹出
返回Main:

  • 由于我们弹出了
    InitBoxUnsafe
    的堆栈,
    esp
    现在回到调用之前的位置,并且
    pBox
    现在在当前堆栈指针上方有一个地址。该值可能仍然存在,但指针现在是非法的。当然,我们在
    不安全的
    代码中,所以没有人可以拍我们的手腕
  • 发布版本和调试版本现在有了不同的路径:
    • 在调试版本中,像往常一样执行
      Print
      调用。这涉及到在堆栈上放置一些值,最重要的是返回地址。但是,这会覆盖
      *pBox
      的值,因为它指向堆栈中的同一点。因此,当实际调用
      Console.WriteLine
      时,它将打印返回地址,而不是
      500
      。我假设在64位上,这通常意味着
      0
      ,因为返回地址的那部分通常是零,而在32位上,它通常是垃圾
    • 在发行版中(并且没有附加调试器),
      Print
      调用是内联的。这意味着在
      控制台.WriteLine
      调用自身之前,堆栈实际上不会被覆盖,而
      *pBox
      的值在调用之前被捕获。但是,在
      InitBoxUnsafe
      pBox->Print()
      之间对堆栈执行任何操作都会破坏该值。事实上,只需连续调用
      pBox->Print()
      两次就足够了——例如,第一个将打印出
      500
      ,而第二个将打印出
      控制台
      流的地址。或者,如果只调用没有任何局部变量或参数的方法,它可能会打印出返回地址

正如你所看到的,代码与C++在等效代码上的差别并不大。这两种方法的输出之间的差异(或者说,在不同的计算机上运行这两种方法,或者在不同的编译器中编译这两种方法)是由于这样一个事实,即您所做的是非法的和未定义的行为,您不应该指望这种行为。永远

现在,如果你看一下你的第二个变量,你已经传递了
Main
的本地
box
变量的地址,而不是指向指针的指针,整个指针的混乱几乎完全被跳过了-
InitBoxUnsafe
现在只做
mov[&box],500直接-完全合法的操作。事实上,在没有调试器的发布模式下,
InitBoxUnsafe
现在可以安全地内联,完全摆脱调用-整个过程现在基本上编译成
Box=(Box)500。在
上调用
打印
现在也完全安全了,因为该值现在固定在范围内(直接在
[ebp-8]
中,而不是在
[[ebp-8]]]
中)
static unsafe void InitBoxUnsafe(Box* box)
{
    *(box) = new Box(500);
}

static unsafe void Main(string[] args)
{
    Box box;
    InitBoxUnsafe(&box);
    box.Print(); // (&box)->Print(); // in Reflector
}