c# - 了解不安全代码及其用途

标签 c# arrays memory-address unsafe

我目前正在按照一位以​​编程为生的 friend 的建议阅读 ECMA-334。我在处理不安全代码的部分。虽然,我对他们在说什么感到有点困惑。

The garbage collector underlying C# might work by moving objects around in memory, but this motion is invisible to most C# developers. For developers who are generally content with automatic memory management but sometimes need fine-grained control or that extra bit of performance, C# provides the ability to write “unsafe” code. Such code can deal directly with pointer types and object addresses; however, C# requires the programmer to fix objects to temporarily prevent the garbage collector from moving them. This “unsafe” code feature is in fact a “safe” feature from the perspective of both developers and users. Unsafe code shall be clearly marked in the code with the modifier unsafe, so developers can't possibly use unsafe language features accidentally, and the compiler and the execution engine work together to ensure 26 8 9BLanguage overview that unsafe code cannot masquerade as safe code. These restrictions limit the use of unsafe code to situations in which the code is trusted.

例子

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();
    }
}

shows an unsafe block in a method named WriteLocations that fixes an array instance and uses pointer manipulation to iterate over the elements. The index, value, and location of each array element are written to the console. One possible example of output is:

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

but, of course, the exact memory locations can be different in different executions of the application.

为什么知道这个数组的确切内存位置对我们开发人员有好处?有人可以在简化的上下文中解释这个理想吗?

最佳答案

fixed 语言特性并不完全是“有益的”,因为它是“绝对必要的”。

通常 C# 用户会将引用类型想象为等同于单间接指针(例如,对于 class Foo,这样:Foo foo = new Foo();等同于此 C++:Foo* foo = new Foo();

实际上,C# 中的引用更接近于双向间接指针,它是一个指针(或者更确切地说,一个句柄)指向一个庞大的对象表中的条目,然后存储对象的实际地址。 GC 不仅会清理未使用的对象,还会清理 also move objects around in memory以避免内存碎片。

如果您只在 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)
}

因此,通过将 fixed 应用于 safeArray 对象,指针 p 将始终是有效指针,不会导致崩溃或句柄垃圾数据。

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

关于c# - 了解不安全代码及其用途,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30064801/

相关文章:

C++:将int的地址作为参数传递

c# - 使用 Amazon.AspNetCore.Identity.Cognito 时,有什么方法可以解决ConfirmEmailAsync(TUser user, string token) NullReferenceException?

python - 如何结束带有 for 循环的 while 循环?

c - 在 C 中使用结构数组的意外输出

c++ - 在 C++ 中获取地址的内容

c - 如何编写一个宏来访问 C 中内存中的数据?

c# - 检测列表框的滚动事件?

c# - 有没有一种简单的方法可以在 C# 中使用内存存储来支持 OData 搜索?

c# - "The type ' 系统.Windows.Forms.TreeNodeCollection ' has no constructors defined"

c# - 如何制作环形阵列?