Array initialization

Hi,

I am using an array of strings with a fixed size. When i try to access the Length property of one of the elements that has not been explicitly set, it throws a NullReferenceException. In the full .NET documentation it says that the elements are initialized with the default value, “”. It is not documented how this should behave in TinyCLR. Is this the expected behaviour, or is it a bug?

Testcode:

            // Creates and initializes the one-dimensional target Array.
            var myTargetArray = new string[15];
            myTargetArray[0] = "The";
            myTargetArray[1] = "quick";
            myTargetArray[2] = "brown";
            myTargetArray[3] = "fox";
            myTargetArray[4] = "jumps";
            myTargetArray[5] = "over";
            myTargetArray[6] = "the";
            myTargetArray[7] = "lazy";
            myTargetArray[8] = "dog";

            var newString = ""; // No exception
            var temp2 = newString.Length; // No exception
            var temp = myTargetArray[8].Length; // No exception 
            Debug.Write($"{' '}{myTargetArray[8]}"); // No exception 
            temp = myTargetArray[9].Length; // NullReference exception thrown
            Debug.Write($"{' '}{myTargetArray[9]}"); // NullReference exception thrown

String is reference type therefore will initialized to null. myTargetArray[9] is null. Calling null.Length will give you the NullReference exception.

For more detail see this answer here: c# - What is the default value of a member in an array? - Stack Overflow

Yes, you are absolutely right, my mistake. The thing is though that this is true for the full .NET, but not for TinyCLR. It looks like arrays of strings are actually initialized to “” in TinyCLR. But they still throw NullReference exceptions when accessed.

Testcode:

            // Creates and initializes the one-dimensional target Array.
            var myTargetArray = new string[15];
           
            string temp1; // This is null
            temp1 = myTargetArray[9].ToString(); // No exception, temp1 is set to ""
            var temp2 = string.IsNullOrEmpty(myTargetArray[9]); // Returns true
            temp2 = (myTargetArray[9] == null); // Returns false
            if (myTargetArray[9] != null) {
                var temp3 = myTargetArray[9].Length; // The application will step into this and throw a NullReference exception
            }

            Debug.Write($"{' '}{myTargetArray[9]}"); // Exception is thrown
            myTargetArray[9] = null;
            Debug.Write($"{' '}{myTargetArray[9]}"); // No exception

Picture of what the array looks like just after initialization:

So it is clear that the strings are initialized to something, not null.

This is partially true, I tried this small experiment -

public static void Main()
      {
            string value = null;
            string value2;

            string[] arr = new string[10];
            arr[0] = null; 
     }

and got these values:

I also checked against an array of a different reference type and those do initialize as null in an array.

So while standalone strings do initialize as null, for string arrays, I wonder if there is a third uninitialized state under the hood which is treated as null until the string is explicitly defined. Strings are just pointers to bytes, after all, and maybe it just points to gibberish at first until you properly initialize it. I’m a little out of my depth as to why, but the behavior is as expected so I’m not super worried about it.

Often in C and C++, in debug mode, uninitialized pointers are set to a specific value outside of the program’s address space. If the pointer is used, there will be an address fault. When you look at the pointer value, in the debugger, it is easy to recognize it as an uninitialized pointer.

A reference is a pointer.

Maybe something like this is happening…

This is a fascinating little bug. I do think that @Mike is right here and that the array cells are getting set to a non-zero, but invalid location. The if-not-null check that succeeds but then throws when that same value is de-referenced is a pretty clear hint that there’s an interpreter bug (along with the fact that it works with other reference (boxed) types. The only user-level defense is to initialize the array to nulls immediately after allocation.

As for this:

In C, yes, but in the TinyCLR interpreter, strings and all other references are pointers to pointers to blocks of bytes. That’s how the GC keeps track of heap-allocated values. I suspect that either the root pointer or boxed pointer value is not being properly initialized, but that’s just speculation because I don’t think we can inspect those values without native-code debugging.

Thanks for the correction. I didn’t think about the interactions with the interpreter, in the back of my head C# is “C/C++ with better social skills”.

1 Like

That would be Rust :grinning: