Sign up ×
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other. Join them, it only takes a minute:

In the code below, when I examine the Chars variable while stepping through the code in the debugger, the size of the char array is 0 before the return line in the last iteration, but after the return line its 1 and continues growing back to the original size.

Why is this happening? thanks for any help in advance.

static void Main(string[] args)
{
    string str = "Hello";
    PrintReverse(str.ToArray()); // prints "olleH"
    Console.Read();
}

static void PrintReverse(char[] Chars)
{
    Console.Write(Chars[Chars.Length - 1]);
    Array.Resize(ref Chars, Chars.Length - 1);
    if (Chars.Length == 0) return;
    PrintReverse(Chars);
}
share|improve this question
1  
Which char array? Your question is unclear. – Jon Skeet Jun 13 '12 at 15:34
    
What are you trying to do? – Ryan Jun 13 '12 at 15:34
    
I tested this out and it simply printed the string in reverse. I had to add the System.Linq namespace to my project, because you are calling str.ToArray() instead of str.ToCharArray(). – Jon Senchyna Jun 13 '12 at 15:38

3 Answers 3

up vote 1 down vote accepted

Think about the chain of execution. You are recursively calling the method that shrinks the array until you get to 0, then you return to the caller (the same method) so you are seeing the grow back to the original size as you traverse back up the call stack.

No extra logic is happening as a result of this, as the recursive call is the last call in the method, but you get to see the debugger end each call, which in turn had an array size 1 bigger than the call before it.

If you were to pass the array by reference instead, however, the array size would remain at size 0 as it came up the call stack because each successive call to Array.Resize would create a new array and update all of the references to the new array instead of only the reference local to that call. (where as if you don't pass by reference it updates only a copy of the reference, and does not update those in the calls before it).

This is because Array.Resize creates a new array and updates the reference to point to the new array instead of the old array, and by not passing by reference, you are sending a copy of the reference to the original array instead of the actual reference to the array, thus the calls to Array.Resize do not update the old references.

static void PrintReverse(ref char[] Chars)
{
    Console.Write(Chars[Chars.Length - 1]);
    Array.Resize(ref Chars, Chars.Length - 1);
    if (Chars.Length == 0) return;
    PrintReverse(ref Chars);
}

Thanks Groo for correcting me, hopefully I have it right this time

share|improve this answer
1  
But the reason to this is that, internally, Array.Resize always creates a new array instance, then copies elements, and then replaces the reference. This is why previous array instances are left on the stack. If Array.Resize operated differently and mutated the original array, it would remain changed. – Groo Jun 13 '12 at 15:42
1  
Your comment is not correct. First, Array.Resize is neither "mutating elements", nor mutating the array itself. It creates a new instance in memory, copies elements, and then switches the reference so that Chars is pointing to the new instance. Old array is not collected only because the previous stack frame still holds its reference. Second, passing an array by value doesn't "create a new instance" of the array, it only creates a copy of the reference to the array. When a method is entered, no copying is being done. – Groo Jun 14 '12 at 7:16
    
@Groo You are correct, did some testing on this, quite a complex little problem. Hopefully my answer is correct now, thanks for correcting me. – Kevin DiTraglia Jun 14 '12 at 14:16

Try adding ref to the parameter declaration, this should now work the way you expected it to.

Without ref the call to Array.Resize can only modify the local array reference and not the reference passed in from Main.

    static void Main(string[] args)
    {
        string str = "Hello";
        var array = str.ToArray();
        PrintReverse(ref array);
        Console.Read();
        Debug.Assert(array.Length == 0);
    }
    static void PrintReverse(ref char[] Chars)
    {
        Console.Write(Chars[Chars.Length - 1]);
        Array.Resize(ref Chars, Chars.Length - 1);
        if (Chars.Length == 0) return;
        PrintReverse(ref Chars);
    }

Edit:

I was mistaken about ref causing a shallow clone, this is the proof:

    static void Main(string[] args)
    {
        var array = new[] { new object() };
        TestRef(ref array, array);
    }

    static void TestRef(ref object[] arrayByRef, object[] arrayByValue)
    {
        Debug.Assert(ReferenceEquals(arrayByRef, arrayByValue)); //no difference whether passed by ref or value, if there was a shallow clone happening, this would fail
        Array.Resize(ref arrayByRef, 2);
        Debug.Assert(!ReferenceEquals(arrayByRef, arrayByValue)); //only now do they differ
    }
share|improve this answer
    
He was asking why the array was growing in size in the debugger, AFAIK the program is working as he intended – Kevin DiTraglia Jun 13 '12 at 15:48
    
@KDiTraglia It appears to grow from 0 to 5 as the stack unwinds, but actually he is looking at 6 different arrays. My solution explains how he could avoid this if he needs to and also trys to explain why it is happening. – weston Jun 13 '12 at 15:51
    
Yeah that's a good point, I'll edit my answer since it got the check mark – Kevin DiTraglia Jun 13 '12 at 15:53
    
"Without ref it will work on a new shallow clone" - can you elaborate this a bit? – Groo Jun 14 '12 at 7:19
1  
@Groo - No I can't, because I'm wrong! See my updated answer. – weston Jun 14 '12 at 12:18

You have two issues here.

First, the 'before/after return' issue means you are seeing two different execution frames-- that is, in the debugger the stack trace will show a bunch of PrintReverses on top of each other because each is there in its own context, with its own state, concurrently. It's almost (though not really) like an 'instance' of that method, and you're seeing two different ones.

Second, because each has its own state, the local variables in each-- including, critically, the parameters-- are also duplicated. They initially point to the same heap object (your initial Char array), but they are all different variables.

Now, look at this code:

char[] test1 = new char[] { '1', '2', '3' }, test2 = test1;
Array.Resize(ref test2, 2);
MessageBox.Show(new string(test1) + " - " + new string(test2)); // result: 123 - 12

If you run this, you'll see that although the variables initially refer to the same object, Array.Resize creates a new object and changes the reference of the variable passed in to point to the new one. The reference in the first variable remains pointing at the old (immutable) object.

This is what's happening in your case, only with the Chars parameter. In each method, you reassign Chars to point elsewhere using Array.Resize(), but the original variables remain referencing the old location.

share|improve this answer
    
+1 This so far seems like the only correct answer. – Groo Jun 14 '12 at 7:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.