C# 如何在c中访问结构数组中的元素成员#
假设我们有一个结构数组条目[2],它在内存中的布局如下:[hashcode(4字节)、next(4字节)、hashcode(4字节)、next(4字节)]C# 如何在c中访问结构数组中的元素成员#,c#,C#,假设我们有一个结构数组条目[2],它在内存中的布局如下:[hashcode(4字节)、next(4字节)、hashcode(4字节)、next(4字节)] [StructLayout(LayoutKind.Explicit, Size = 8, Pack = 1)] struct Entry { [FieldOffset(0)] public int hashCode; [FieldOffset(4)] pu
[StructLayout(LayoutKind.Explicit, Size = 8, Pack = 1)]
struct Entry
{
[FieldOffset(0)]
public int hashCode;
[FieldOffset(4)]
public int next;
}
现在我要访问这个条目[0]。接下来是代码中的
class Test
{
Entry[] entries = new Entry[2];
void AccessElement()
{
int n = entries[0].next;
}
}
因此,在AccessElement方法中,我只有一行代码,即:条目[0]。接下来,我想知道CLR如何帮助我们访问此元素。我现在有两个想法,我将给你伪代码。
1)。想法1
int n=(&entries+0)->下一步
获取entries对象的地址,获取第一个元素(偏移量0),然后访问下一个成员的值(相对于元素基址的4字节) 想法2
Entry entry = Copy(&entries[0],8);
int n = entry.next;
将entries对象的第一个元素(偏移量0)复制到当前堆栈中,并返回当前堆栈对象的下一个成员
这两个想法是我对CLR访问结构数组中元素成员的想法,这与我是否在某些情况下使用struct有关,大量的数据mov有一些开销。那么有人能告诉我CLR如何访问结构数组中元素成员的真实模式吗?调试和发布之间有什么区别吗
多谢各位
这是一个接收代码。有人能帮我读一下吗
Entry[] entries = new Entry[2];
00007FFB4B6D47F0 mov rcx,7FFB4B96361Ah
00007FFB4B6D47FA mov edx,2
00007FFB4B6D47FF call 00007FFBAA902630
00007FFB4B6D4804 mov qword ptr[rbp + 20h], rax
00007FFB4B6D4808 mov rax,qword ptr[rbp + 20h]
00007FFB4B6D480C mov qword ptr[rbp + 30h], rax
int n = entries[1].next;
00007FFB4B6D4810 mov rax,qword ptr[rbp + 30h]
00007FFB4B6D4814 mov edx,1
00007FFB4B6D4819 cmp rdx,qword ptr[rax + 8]
00007FFB4B6D481D jb 00007FFB4B6D4824
00007FFB4B6D481F call 00007FFBAADA2660
00007FFB4B6D4824 lea rax,[rax+rdx*8+10h]
00007FFB4B6D4829 mov eax,dword ptr[rax + 4]
00007FFB4B6D482C mov dword ptr[rbp + 2Ch], eax
也许我想得太多了?我只是看了一下IL代码:当条目类型为reference时,这是IL
// Entry1[] entries = new Entry1[2];
IL_0001 ldc.i4.2
IL_0002 newarr ConsoleApp9.Entry1
IL_0007 stloc.0
// int n = entries[1].next;
IL_0008 ldloc.0
IL_0009 ldc.i4.1
IL_000A ldelem.ref
IL_000B ldfld System.Int32 ConsoleApp9.Entry1::next
IL_0010 stloc.1
如果条目类型为结构,则这是IL
// Entry[] entries = new Entry[2];
IL_0001 ldc.i4.2
IL_0002 newarr ConsoleApp9.Entry
IL_0007 stloc.0
// int n = entries[1].next;
IL_0008 ldloc.0
IL_0009 ldc.i4.1
IL_000A ldelema ConsoleApp9.Entry
IL_000F ldfld System.Int32 ConsoleApp9.Entry::next
IL_0014 stloc.1
对于结构化数组,指令是ldelema:将指定数组索引处数组元素的地址作为类型&(托管指针)加载到计算堆栈的顶部
对于引用数组,指令是ldelem.ref:将在指定数组索引处包含对象引用的元素作为类型O(对象引用)加载到计算堆栈的顶部
那么真相就出来了?你可以自己研究一下:编译代码并查看生成的IL(例如使用ildasm)。@KlausGütter确实应该自己看看。只是不在IL。他们需要查看零售构建中的JIT代码,以了解他们的要求。IL不必匹配程序集级指令,因为几乎所有优化都是通过JIT进行的,而不是在编译过程中进行的。在访问数组时,肯定不会将数组元素复制到任何局部变量。不需要这样做,因为数组是下面的连续内存块,因此其元素可以通过指令级的偏移量进行访问,而复制会根据数组的大小及其元素的大小造成严重的性能影响。根据反汇编代码:1)
lea-rax[rax+rdx*8+10h]
-加载索引为1
的数组元素地址(1加载到rdx
下部的edx
,rax
包含数组实例地址,10h是x64系统上CLR实例头的长度)到rax
寄存器,2)mov-eax,dword-ptr[rax+4]
-将索引为1
(由rax
寻址)的元素的next
字段的值移动到eax
寄存器和3)mov-dword-ptr[rbp+2Ch],eax
将该值存储在堆栈上某个寻址的局部变量中。正如您所看到的,不会复制整个数组元素。