当前位置:  开发笔记 > 编程语言 > 正文

ref关键字是如何工作的(就内存而言)

如何解决《ref关键字是如何工作的(就内存而言)》经验,为你挑选了2个好方法。

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

public void SampleMethod(ref int i) { }

Tamas Hegedu.. 18

传递局部变量作为参考

在低级别,引用的局部int变量将被放在堆栈上(大多数时候整数存储在寄存器中),并且指向堆栈的指针将被传递给调用的函数(指针本身最有可能被传入登记册).请考虑以下示例:

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

这将是JIT-et之类的东西(目标架构是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  

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

这里发生了同样的事情,获得​​了字段或元素的地址,并将指针传递给函数:

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

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

    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是指向数组内的项目,因此如果在inc函数之间lea和之call内发生堆压缩,它可以重新调整值的ecx直接.

您可以通过打开Disassembly窗口(Debug/Windows/Disassembly)使用Visual Studio调试器自行调查JIT-ed程序集



1> Tamas Hegedu..:

传递局部变量作为参考

在低级别,引用的局部int变量将被放在堆栈上(大多数时候整数存储在寄存器中),并且指向堆栈的指针将被传递给调用的函数(指针本身最有可能被传入登记册).请考虑以下示例:

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

这将是JIT-et之类的东西(目标架构是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  

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

这里发生了同样的事情,获得​​了字段或元素的地址,并将指针传递给函数:

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

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

    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是指向数组内的项目,因此如果在inc函数之间lea和之call内发生堆压缩,它可以重新调整值的ecx直接.

您可以通过打开Disassembly窗口(Debug/Windows/Disassembly)使用Visual Studio调试器自行调查JIT-ed程序集



2> Jakub Lortz..:

局部变量或字段的地址.在IL中,ldloca.s指令用于局部变量.

将特定索引处的局部变量的地址加载到评估堆栈中

stind指令用于将值存储回变量中

将类型(...)的值存储到地址的内存中

地址为32/64位,具体取决于目标架构.

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有