Hello

dotnet new console -h Hello

unsafe

using System.Diagnostics;
using System.Runtime.InteropServices;

/*
We have to add to ./Hello.csproj
  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
*/

public class UnsafeTest {
  class MyStruct {
    public int A;
    public char B;
  }

  public static void TestPointer() {
    // for a primitive (unmanaged type)
    int a = 3;
    unsafe {
      int*p = &a;
      *p = 10;
      Debug.Assert(a == 10);
      a = 20;
      Debug.Assert(p[0] == 20); // like C/C++
    }

    // now for an array. We have to ensure the address of the array
    // is not moved by using fixed. This is also called a pin operation.
    unsafe {
      byte[] b = new byte[5];
      fixed(byte*p = &b[0]) {
        // p is a constant inside this block due to the usage of fixed.
        // That is, p is a fixed variable
        //
        // Note: We can also use (byte*p = b)
        p[0] = 10;
        Debug.Assert(b[0] == 10);

        b[1] = 20;
        Debug.Assert(*(p+1) == 20);
        Debug.Assert(p[1] == 20);
        Console.WriteLine($"address of p: 0x{(long)p:X}");
        Console.WriteLine($"address of p+1: 0x{(long)(p+1):X}");
      }
    }

    // test struct
    MyStruct s = new MyStruct {A=10, B='B'};
    unsafe {
      MyStruct* p = &s;
      Debug.Assert(p->A == 10); // similar to C/C++
      Debug.Assert(p->B == 'B');
      p->B = 'C';
      Debug.Assert(s.B == 'C');
    }

    // from stackalloc
    unsafe {
      int*p = stackalloc int[3];
      // the content is uninitialized
      p[0] = 10;
      ++p;
      p[0] = 20;
      ++p;
      Debug.Assert(p[-2] == 10);
      Debug.Assert(p[-1] == 20);
    }


    IntPtr pp = Marshal.AllocHGlobal(sizeof(int));
    unsafe {
      int* p = (int*)pp.ToPointer();
      p[0] = 10;
    }
    int i = Marshal.ReadInt32(pp);
    Debug.Assert(i == 10);

    Marshal.FreeHGlobal(pp);


  }
}

References:

Span

using System.Diagnostics;
using System.Runtime.InteropServices;

class SpanTest {
  public static void Test() {
    TestByteArray();
    TestStackAlloc();
    TestGlobalAlloc();
  }
  public static void TestByteArray() {
    byte[] b = new byte[10];
    Span<byte> s = new Span<byte>(b);
    // s points to the memory holds by b
    s[0] = 10;
    Debug.Assert(b[0] == 10);

    // s2 = b[3:5]
    Span<byte> s2 = s.Slice(start:3, length: 2);
    s2[0] = 12;
    s2[1] = 13;
    Debug.Assert(b[3] == 12);
    Debug.Assert(b[4] == 13);
  }

  public static void TestStackAlloc() {
    Span<int> s = stackalloc int[10];
    s.Clear(); // set all contents to default(int);
    Debug.Assert(s.Length == 10);
  }

  public static void TestGlobalAlloc() {
    IntPtr p = Marshal.AllocHGlobal(sizeof(int) * 2);
    Span<int> s;
    unsafe {
      // p.ToPointer() returns void*, so it has to be used in void*
      s = new Span<int>(p.ToPointer(), 2);
      s[0] = 10;
      s[1] = 11;
    }

    int a = Marshal.ReadInt32(p);
    int b = Marshal.ReadInt32(p, sizeof(int));
    Debug.Assert(a == s[0]);
    Debug.Assert(b == s[1]);

    Marshal.FreeHGlobal(p);
  }
}

References:

File

TODO

  • stackalloc

  • unsafe code

  • unit test

  • Marshal, read/write memory

  • MemoryStream, FileStream

  • FileReader, BinaryWriter

  • directory exists, file exists