Search code examples
c#pointersunsafe

C# - Unsafe and Pointers Basics


I am a C++/Java amateur but completely new to C#. My goal is to make a Tree where Nodes are pointers instead of entire objects that are constantly and slowly copied. That way I can simply pass a memory address instead of copying entire objects byte by byte. However, I need to first understand how to use pointers in C#.

  1. Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object. At Program.$(String[] args) on line 19 [*p1 = 45;].

Do pointers have to be tied to objects, not allowed to be static (free roaming)? Does the compiler not know the pointer is pointing to an int? 2. Why can I not use Main() here? Does Main() need to be inside a class/be tied to an object? 3. How do I know where (inside the code, not the compilation file) to put the unsafe{} block? 4. What all needs classes and what all can act as stand-alone code? I understand that code reusability screams "Make this a class!" but how do you make code not be tied to an object (i.e. make static but functional code) in C#?

    using System;
    
    //unsafe{} is necessary to use pointers.
    //In addition to this block, add the compiler flag "-unsafe" or set the flag to True
    //FOR VSCode:    Add the following to ProjectName.csproj:
    //                      <PropertyGroup>  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>  </PropertyGroup>
    //static void Main()
    //{
        unsafe
        {
            //The Pointer Data Types will contain a memory address of the variable value.
            //ampersand (&): The Address Operator.     It is used to determine the address of a variable.
            //asterisk  (*): The Indirection Operator. It is used to access the value at an address.
            int* p1=(int*)130, p2;      //Valid   syntax for making 2 pointers
            //int *p3, *p4;             //Invalid syntax for making 2 pointers.
            //Maybe the compiler thinks it's:  int* p3; int** p4; but in 1 statement.
            //Declaring different types in 1 statement is illegal.   int a, (float)b; is illegal
            p2 = (int*)145;     //location p2 is 145 in memory;
            *p1 = 45;   //value at location p1 = 45;
            *p2 = 50;   //value at location p2 = 50;
            Console.WriteLine(  $"adrs{(int)p1}-val{*p1},  adrs{(int)p2}-val{*p2}"  );
            
            int num = 10;     //declare variable
            int* p = &num;    //store variable num's address location in pointer variable p
            Console.WriteLine("Value :{0}", num);
            Console.WriteLine("Address :{0}\n\n\n", (int)p);
        }
    //}

In regards to my questions, please tell me what to elaborate on because I am very clueless about what I don't know, meaning I don't know how to word my questions for easy comprehension.

Also, any DETAILED and understandable sources are greatly appreciated.

######################################################

Update Aug 21, 2024: I now understand that objects are accessible in memory via pointers that point to the start (in memory) of the object in Java and C++, and presumably C# as well. I am still unsure if a language exists that easily deep copies (byte-by-byte copying in memory) objects (due to the complexity of doing so) instead of passing a reference (memory address) to the object.

Previously, I did not understand that a pointer could exist without having allocated any memory to whatever the pointer points to. I thought that int* ptr; *ptr = 50; would be okay because of the 4 (or 6 or 8) bytes allocated to the pointer variable, not knowing that the location the pointer points to also needs extra memory (in this case, however much memory an int takes, which should be 4 Bytes).


Solution

    1. You have an exception because you don't allocate memory for integers. Actually (int*)130 means that 130 is an address, which means p1 points to nowhere. You need to allocate memory to write and read values:
    int v1 = 130, v2 = 45; // allocation
    int* p1=&v1, p2; // or U can use stackalloc to allocate int:
                     // int* p1 = stackalloc int[1] { 130 }, p2;  
    *p1 = 50; // it's ok because we have already allocated memory for an integer,
              // here we just overwrite the value.
    p2 = &v2;  // ok
    
    //p2 = &130; // not ok.
    //*p2 = 50;  // not ok. You are trying to dereference null
    //p2 = stackalloc int[1] { 45 };  // not ok, You can use a stackalloc expression only in a 
                                      // local variable declaration to initialize the variable.
    
    1. Starting in C# 9, you don't have to explicitly include a Main method in a console application project. Instead, you can use the top-level statements feature to minimize the code you have to write. In this case, the compiler generates a class and Main method entry point for the application.

    2. If You want to use unsafe features (pointers), you put an unsafe block or unsafe modifier on method/class/struct :) read more . Most of the time you don't want to use unsafe code, it won't give you performance (if you don't know what you're doing) but it will make your code less readable.

    3. You can't. Everything in C# is an object (nearly)(actually you can use static methods (but it's better not to overuse it)).