Pointer arithmetic with differing types and understanding the results?

86 Views Asked by At

Consider the following code:

static void Main(string[] args)
{
    int max = 1024;
    var lst = new List<int>();
    for (int i = 1; i <= max; i *= 2) { lst.Add(i); }

    var arr = lst.ToArray();

    IterateInt(arr);
    Console.WriteLine();
    IterateShort(arr);
    Console.WriteLine();
    IterateLong(arr);
}

static void IterateInt(int[] arr)
{
    Console.WriteLine("Iterating as INT ({0})", sizeof(int));
    Console.WriteLine();

    unsafe
    {
        fixed (int* src = arr)
        {
            var ptr = (int*)src;
            var len = arr.Length;
            while (len > 0)
            {
                Console.WriteLine(*ptr);
                ptr++;
                len--;
            }
        }
    }
}

static void IterateShort(int[] arr)
{
    Console.WriteLine("Iterating as SHORT ({0})", sizeof(short));
    Console.WriteLine();

    unsafe
    {
        fixed (int* src = arr)
        {
            var ptr = (short*)src;
            var len = arr.Length;
            while (len > 0)
            {
                Console.WriteLine(*ptr);
                ptr++;
                len--;
            }
        }
    }
}

static void IterateLong(int[] arr)
{
    Console.WriteLine("Iterating as LONG ({0})", sizeof(long));
    Console.WriteLine();

    unsafe
    {
        fixed (int* src = arr)
        {
            var ptr = (long*)src;
            var len = arr.Length;
            while (len > 0)
            {
                Console.WriteLine(*ptr);
                ptr++;
                len--;
            }
        }
    }
}

Now, by no means do I have a full understanding in this arena. Nor did I have any real expectations. I'm experimenting and trying to learn. However, based off what I've read thus far, I don't understand the results I got for short and long.

It is my understanding that the original int[], when read 1 location at a time (i.e. arr + i), reads 4 bytes at a time because of the data types size and thus the value *ptr is of course the integral value.

However, with short I don't quite understand why every even iteration is 0 (or arguably odd iteration depending on your root reference). I mean I can see the pattern. Every time I iterate 4 bytes I get the real integral value in memory (just like iterating the int*), but why 0 on every other result?

Then the long iterations is even further outside my understanding; I don't even know what to say or assume there.

Results

Iterating as INT (4)

1
2
4
8
16
32
64
128
256
512
1024

Iterating as SHORT (2)

1
0
2
0
4
0
8
0
16
0
32

Iterating as LONG (8)

8589934593
34359738372
137438953488
549755813952
2199023255808
-9223372036854774784
96276819136
32088581144313929
30962698417340513
32370038935650407
23644233055928352

What is actually happening with the short and long iterations?

1

There are 1 best solutions below

6
On BEST ANSWER

When you say pointer[index] it gives you sizeof(type) bytes at location pointer + index * sizeof(type). So by changing the type that you "iterate with" you change the stride.

  • With short you read halves of the original int's. Small ints have all zeros in their upper half.
  • With long you read two int's at the same time, forced into one long. At ptr[0] you are reading, for example, (1L << 32 | 2L) which is a big number.

You are still using the original Length measured in int-units, though, which is a bug. In the long-case you are reading outside the bounds of the array, in the short case you are reading too little.