我出现了这段c#代码,它应该验证在早期阶段完成的内存分配。
for (int i = 0; i < Size; i++)
{
var b = *(BaseAddress + i); // type of BaseAddress is byte*
*(BaseAddress + i) = b;
}
在我看来,所有代码都是将原始内存中的一个字节复制到临时变量b,然后将其写回原始内存。
从写入内存位置读取是否使内存有效且可以安全访问? 是否存在实际上会在内存中发现损坏的情况?
答案 0 :(得分:0)
它实际上会验证从BaseAddress
到BaseAddress+(sizeof(ElementType))*(Size-1)
的内存可以从读取,从读取。
例如,假设BaseAddress的内存具有protection attributes PAGE_EXECUTE_READ
。
在这种情况下,读取失败,而写入会导致访问冲突(据我所知)。
但正如已经指出的那样,我们只能猜到为什么有人需要它。
理想情况下,应该不需要它,假设应用程序的托管和非托管部分正确实现它们之间的契约。而且,显然,假设合同是合理的(比如模块A承诺不分配缓冲内存,无论是不可读还是不可写)。
正如@AndrewMedico所指出的
假设编译器并不只是优化它(将... 循环在逻辑上是一种无操作的方式
我怀疑所有C \ C ++编译器都会优化类似代码,但我们在这里谈论C#,所以有两件事需要考虑
假设以下代码
private unsafe static void Test(Int32* ptr, Int32 size)
{
for (int i = 0; i < size; i++)
{
var a = * (ptr + i);
*(ptr + i) = a;
}
}
...
var array = new Int32[] { 1, 2, 3 };
fixed (Int32* ptr = array)
{
Test(ptr, array.Length);
}
测试方法将成为:
.method private hidebysig static void Test(int32* ptr,
int32 size) cil managed
{
// Code size 29 (0x1d)
.maxstack 3
.locals init ([0] int32 i,
[1] int32 a)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0018
IL_0004: ldarg.0
IL_0005: ldloc.0
IL_0006: conv.i
IL_0007: ldc.i4.4
IL_0008: mul
IL_0009: add
IL_000a: ldind.i4
IL_000b: stloc.1
IL_000c: ldarg.0
IL_000d: ldloc.0
IL_000e: conv.i
IL_000f: ldc.i4.4
IL_0010: mul
IL_0011: add
IL_0012: ldloc.1
IL_0013: stind.i4
IL_0014: ldloc.0
IL_0015: ldc.i4.1
IL_0016: add
IL_0017: stloc.0
IL_0018: ldloc.0
IL_0019: ldarg.1
IL_001a: blt.s IL_0004
IL_001c: ret
} // end of method Program::Test
不是无操作
但是现在我已经在我自己的机器上检查了发布模式反汇编,使用了simplest available method并且看似已经开启了优化
以下代码:
private unsafe static void Test(Int32* ptr, Int32 size)
{
Console.WriteLine("test");
for (int i = 0; i < size; i++)
{
var a = * (ptr + i);
*(ptr + i) = a;
}
}
我们有
Console.WriteLine("test");
00742E70 push ebp
00742E71 mov ebp,esp
00742E73 push edi
00742E74 push esi
00742E75 mov esi,ecx
00742E77 mov edi,edx
00742E79 mov ecx,dword ptr ds:[27B2310h]
00742E7F call 65BDD4B0
for (int i = 0; i < size; i++)
00742E84 xor edx,edx
for (int i = 0; i < size; i++)
00742E86 test edi,edi
00742E88 jle 00742E95
{
var a = * (ptr + i);
00742E8A mov ecx,dword ptr [esi+edx*4]
*(ptr + i) = a;
00742E8D mov dword ptr [esi+edx*4],ecx
for (int i = 0; i < size; i++)
00742E90 inc edx
00742E91 cmp edx,edi
00742E93 jl 00742E8A
00742E95 pop esi
00742E96 pop edi
00742E97 pop ebp
00742E98 ret
所以它没有优化,但是如果我删除Console.WriteLine,我就无法在Test
中找到断点。起初我虽然优化了,但该方法只进行了内存处理,但它实际上只是在Main
中内联:
Test(ptr, array.Length);
00482DFD mov ecx,dword ptr [ebp-20h]
00482E00 mov edi,dword ptr [edx+4]
00482E03 xor esi,esi
00482E05 test edi,edi
00482E07 jle 00482E14
00482E09 mov edx,dword ptr [ecx+esi*4]
00482E0C mov dword ptr [ecx+esi*4],edx
00482E0F inc esi
00482E10 cmp esi,edi
00482E12 jl 00482E09
00482E14 mov dword ptr [ebp-18h],0
00482E1B mov dword ptr [ebp-14h],0FCh
00482E22 push 482E36h
00482E27 jmp 00482E2E
00482E29 call 66DBBDC2
00482E2E xor edx,edx
00482E30 mov dword ptr [ebp-20h],edx
00482E33 pop eax
00482E34 jmp eax
00482E36 mov dword ptr [ebp-14h],0
00482E3D jmp 00482E4B