Pointers in C#

using System;

class Pointers
{
    // We'll show how pointers work in contrast to values with
    // 2 methods: ZeroVal and ZeroPtr. ZeroVal has an
    // int parameter, so arguments will be passed to it by
    // value. ZeroVal will get a copy of ival distinct
    // from the one in the calling method.
    static void ZeroVal(int ival)
    {
        ival = 0;
    }

    // ZeroPtr in contrast has an int* parameter, meaning
    // that it takes an int pointer. The *iptr code in the
    // method body then dereferences the pointer from its
    // memory address to the current value at that address.
    // Assigning a value to a dereferenced pointer changes the
    // value at the referenced address.
    static unsafe void ZeroPtr(int* iptr)
    {
        *iptr = 0;
    }

    static unsafe void Main()
    {
        int i = 1;
        Console.WriteLine($"initial: {i}");

        ZeroVal(i);
        Console.WriteLine($"ZeroVal: {i}");

        // The &i syntax gives the memory address of i,
        // i.e. a pointer to i.
        fixed (int* ptr = &i)
        {
            ZeroPtr(ptr);
        }
        Console.WriteLine($"ZeroPtr: {i}");

        // Pointers can be printed too.
        fixed (int* ptr = &i)
        {
            Console.WriteLine($"pointer: {(IntPtr)ptr}");
        }
    }
}

To run this program, you need to compile it with unsafe code enabled:

$ csc /unsafe Pointers.cs
$ ./Pointers.exe
initial: 1
ZeroVal: 1
ZeroPtr: 0
pointer: 1245184

ZeroVal doesn’t change the i in Main, but ZeroPtr does because it has a reference to the memory address for that variable.

Note that in C#, pointers are only allowed in unsafe contexts. The unsafe keyword is used to denote code blocks where pointer operations are permitted. Additionally, you need to use the fixed statement when taking the address of a managed variable to prevent it from being moved by the garbage collector during the execution of the unsafe code.

Unlike Go, where pointers are more commonly used, C# typically uses reference types and the ref keyword for similar purposes in safe code. The use of raw pointers in C# is generally reserved for interop scenarios or performance-critical code.

When compiling, make sure to use the /unsafe flag to allow unsafe code. When running the program, the output will be similar to the Go version, demonstrating how value types and reference types (via pointers) behave differently.