了解不安全的代码及其用途

Jes*_*ver 5 c# arrays unsafe memory-address

我正在阅读ECMA-334,正如一位以编程为生的朋友所建议的那样.我在处理不安全代码的部分.虽然,我对他们所谈论的内容感到有点困惑.

C#下面的垃圾收集器可能通过在内存中移动对象来工作,但这种移动对于大多数C#开发人员来说是不可见的.对于通常满足于自动内存管理但有时需要细粒度控制或额外性能的开发人员,C#提供了编写"不安全"代码的能力.这样的代码可以直接处理指针类型和对象地址; 但是,C#要求程序员修复对象以暂时阻止垃圾收集器移动它们.从开发人员和用户的角度来看,这种"不安全"的代码功能实际上是一种"安全"功能.代码中的不安全代码应使用修饰符unsafe清楚地标记,因此开发人员不可能意外地使用不安全的语言功能,并且编译器和执行引擎一起工作以确保不安全的代码不能伪装成安全代码26 8 9BLanguage概述.这些限制将不安全代码的使用限制在代码受信任的情况下.

这个例子

using System;
class Test
{
    static void WriteLocations(byte[] arr)
    {
        unsafe
        {
            fixed (byte* pArray = arr)
            {
                byte* pElem = pArray;
                for (int i = 0; i < arr.Length; i++)
                {
                    byte value = *pElem;
                    Console.WriteLine("arr[{0}] at 0x{1:X} is {2}",
                    i, (uint)pElem, value);
                    pElem++;
                }
            }
        }
    }
    static void Main()
    {
        byte[] arr = new byte[] { 1, 2, 3, 4, 5 };
        WriteLocations(arr);
        Console.ReadLine();
    }
}
Run Code Online (Sandbox Code Playgroud)

在名为WriteLocations的方法中显示一个不安全的块,它修复了一个数组实例,并使用指针操作迭代元素.每个数组元素的索引,值和位置都写入控制台.一个可能的输出示例是:

arr[0] at 0x8E0360 is 1
arr[1] at 0x8E0361 is 2
arr[2] at 0x8E0362 is 3
arr[3] at 0x8E0363 is 4
arr[4] at 0x8E0364 is 5
Run Code Online (Sandbox Code Playgroud)

但是,当然,在应用程序的不同执行中,确切的存储器位置可能不同.

为什么知道确切的内存位置,例如,这个数组对开发人员有益吗?有人可以在简化的背景下解释这个理想吗?

Dai*_*Dai 6

fixed语言的功能是不完全的"有利",因为它是"绝对必要的".

通常,C#用户会将Reference-types视为等同于单个间接指针(例如,for class Foo:this:Foo foo = new Foo();相当于这个C++ : Foo* foo = new Foo();.

实际上,C#中的引用更接近双间接指针,它是指向大型对象表中的条目的指针(或者更确切地说是句柄),然后存储对象的实际地址.GC不仅会清理未使用的对象,还会在内存中移动对象以避免内存碎片.

如果您在C#中专门使用对象引用,那么这一切都很好.一旦你使用指针,你就会遇到问题,因为GC可以在任何时间运行,即使在紧密循环执行期间,当GC运行时程序的执行被冻结(这就是为什么CLR和Java不是适用于硬实时应用程序 - 在某些情况下,GC暂停可持续几百毫秒).

...由于这种固有行为(在代码执行期间移动对象),您需要阻止该对象被移动,因此fixed关键字指示GC不要移动该对象.

一个例子:

unsafe void Foo() {

    Byte[] safeArray = new Byte[ 50 ];
    safeArray[0] = 255;
    Byte* p = &safeArray[0];

    Console.WriteLine( "Array address: {0}", &safeArray );
    Console.WriteLine( "Pointer target: {0}", p );
    // These will both print "0x12340000".

    while( executeTightLoop() ) {
        Console.WriteLine( *p );
        // valid pointer dereferencing, will output "255".
    }

    // Pretend at this point that GC ran right here during execution. The safeArray object has been moved elsewhere in memory.

    Console.WriteLine( "Array address: {0}", &safeArray );
    Console.WriteLine( "Pointer target: {0}", p );
    // These two printed values will differ, demonstrating that p is invalid now.
    Console.WriteLine( *p )
    // the above code now prints garbage (if the memory has been reused by another allocation) or causes the program to crash (if it's in a memory page that has been released, an Access Violation)
}
Run Code Online (Sandbox Code Playgroud)

因此,通过不是施加fixedsafeArray对象,指针p永远是一个有效的指针,而不是导致崩溃或处理垃圾数据.

附注:使用的替代fixed方法stackalloc,但是将对象生命周期限制在函数范围内.