ref 关键字如何工作(就内存而言) [英] How does the ref keyword work (in terms of memory)

查看:67
本文介绍了ref 关键字如何工作(就内存而言)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C# 有一个 ref 关键字.使用 ref 您可以通过引用将 int 传递给方法.当您调用通过引用接受 int 的方法时,堆栈帧上会发生什么?

C# has a ref keyword. Using ref you can pass an int to a method by reference. What goes on the stack frame when you call a method that accepts an int by reference?

public void SampleMethod(ref int i) { }

推荐答案

传递局部变量作为引用

在底层,引用的本地int变量将被放入堆栈(大多数情况下整数存储在寄存器中),并且指向堆栈的指针将被传递给被调用的函数(指针本身最有可能在寄存器中传递).考虑以下示例:

Passing a local variable as a reference

At low level, the referenced local int variable will be put on the stack (most of the time integers get stored in registers), and a pointer to the stack will be passed to the invoked function (the pointer itself is most likely to be passed in a register). Consider the following example:

var i = 7;
Console.WriteLine(i);
inc(ref i);
Console.WriteLine(i);

这将是类似这样的 JIT-et(目标架构是 x86):

This will be JIT-et to something like this (target architecture is x86):

    17:             var i = 7;
    # allocate space on the stack for args and i
00482E3B  sub         esp,8  
    # initialize i to 0
00482E3E  xor         eax,eax  
00482E40  mov         dword ptr [ebp-8],eax  
    # args saved to stack (could be optimised out)  
00482E43  mov         dword ptr [ebp-4],ecx  
00482E46  cmp         dword ptr ds:[3ACAECh],0  
00482E4D  je          00482E54  
00482E4F  call        7399CB2D  
    # i = 7
00482E54  mov         dword ptr [ebp-8],7  
    18:             Console.WriteLine(i);
    # load the value of i into ecx, and call cw
00482E5B  mov         ecx,dword ptr [ebp-8]  
00482E5E  call        72E729DC  
    19:             inc(ref i);
    # load the address of i into ecx, and call inc
00482E63  lea         ecx,[ebp-8]  
00482E66  call        dword ptr ds:[4920860h]  
    20:             Console.WriteLine(i);
    # load the value of i into ecx, and call cw
00482E6C  mov         ecx,dword ptr [ebp-8]  
00482E6F  call        72E729DC  
    21:         }
00482E74  nop  
00482E75  mov         esp,ebp  
00482E77  pop         ebp  
00482E78  ret  

传递数组项或对象成员作为引用

这里发生的事情几乎相同,获取字段或元素的地址,并将指针传递给函数:

Passing an array item or an object member as a reference

Pretty much the same thing happens here, the address of the field or element is obtained, and the pointer is passed to the function:

var i = new[]{7};
Console.WriteLine(i[0]);
inc(ref i[0]);
Console.WriteLine(i[0]);

编译为(没有无聊的部分):

Compiles to (without the boring part):

    18:             Console.WriteLine(i[0]);
00C82E91  mov         eax,dword ptr [ebp-8]  
00C82E94  cmp         dword ptr [eax+4],0  
00C82E98  ja          00C82E9F  
00C82E9A  call        7399BDC2  
00C82E9F  mov         ecx,dword ptr [eax+8]  
00C82EA2  call        72E729DC  
    19:             inc(ref i[0]);
    # loading the reference of the array to eax
00C82EA7  mov         eax,dword ptr [ebp-8]  
    # array boundary check is inlined
00C82EAA  cmp         dword ptr [eax+4],0  
00C82EAE  ja          00C82EB5  
    # this would throw an OutOfBoundsException, but skipped by ja
00C82EB0  call        7399BDC2  
    # load the address of the element in ecx, and call inc
00C82EB5  lea         ecx,[eax+8]  
00C82EB8  call        dword ptr ds:[4F80860h]  

请注意,在这种情况下不必固定数组,因为框架知道 ecx 中的地址指向数组内的一个项目,所以如果堆压缩发生在leacall之间或inc函数内部,它可以直接重新调整ecx的值.

Note that the array doesn't have to be pinned in this case, because the framework knows about the address in ecx is pointing an item inside the array, so if a heap compression happens between lea and call or inside the inc function, it can readjust the value of ecx directly.

您可以通过打开反汇编窗口 (Debug/Windows/Disassembly) 使用 Visual Studio 调试器自己调查 JIT 化的程序集

You can investigate the JIT-ed assembly yourself using Visual Studio debugger by opening the Disassembly window (Debug/Windows/Disassembly)

这篇关于ref 关键字如何工作(就内存而言)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆