Start learning free

LESSON 147 OF 347 · LOOPING OVER ARRAY INDICES

Changing Array Elements in a C# For Loop: Fixing the Bug

Lesson Read the full explanation

Suppose you have an array nums and your task is to increment every number in that array by one.

The first idea that might come to your mind is to write a for loop like this:

int[] nums = {1, 2, 3};

// Increment items:
for (int num : nums) {
  num++;
}

// Print the array after:
for (int num : nums) {
  Console.WriteLine(num);
}

but when you run this code, you'll be surprised to see that the output is:

1
2
3

No element in nums changed its value. How is this possible since we clearly do num++ for every num in nums?


Let's talk about copies:

Take a look at this code:

int number = 100;

int number_copy = number;

number_copy++;

Console.WriteLine(number_copy); // Output: 101
Console.WriteLine(number); // Output: 100

Although we wrote int number_copy = number;, this doesn't mean that number and number_copy are one and the same.

number_copy is a completely different entitity (stored at a different memory address) than number.

What we did is initialize this new number_copy entity with the value of number. That's why we say the number_copy is a copy of number.

And so number_copy++ will only increment number_copy and won't have any effect on number.


The loop variable is just a copy:

This is what also happens with a for loop.

Whenever you write a for loop, the loop variable (in our case num) is just a copy of the current item of the array, not the item itself.

This code

int[] nums = {1, 2, 3};

for (int num : nums) {
  num++;
}

works exactly like this code:

int[] nums = {1, 2, 3};

int num = nums[0];
num++;

num = nums[1];
num++;

num = nums[2];
num++;

Notice that only the variable num is being manipulated, which has nothing to do with the actual entries of the array nums.

You'll see this better if we also print the value of num on each iteration.

int[] nums = {1, 2, 3};

for (int num : nums) {
  num++;
  Console.WriteLine(num);
}

// Print the array after:
for (int num : nums) {
  Console.WriteLine(num);
}

The output of this code is:

2
3
4
1
2
3

Looping over the indices:

In order to manipulate the entries of some array we have to loop over the items' indices:

int[] nums = {1, 2, 3};

for (int index = 0; index < nums.length; index++) {
  nums[index]++;
}

This code works exactly like this one:

int[] nums = {1, 2, 3};

int index = 0;
nums[index]++;

index++;
nums[index]++;

index++;
nums[index]++;

In this way we directly manipulate the actual entries of the list (nums[0], nums[1], etc.)

Assignment:

Fix our code such that it correctly increments (adds one to) the even numbers of nums and decrements (subtracts one from) the odd ones.

STEP 1 OF 1
    Ctrl + Enter to check

    C# · LOOPING OVER ARRAY INDICES

    Buggy Code Manipulate Array in C#, explained

    The problem: a loop that can't change the array

    The task is to add one to every even number in nums and subtract one from every odd number, so that {1, 2, 3, 4, 5} becomes {0, 3, 2, 5, 4}. The starting code uses a foreach loop:

    int[] nums = {1, 2, 3, 4, 5};
    
    foreach (int num in nums) {
        if (num % 2 == 0) {
            num++;
        } else {
            num--;
        }
    }
    

    It doesn't even compile. The compiler stops at num++ and num-- with an error like this:

    error CS1656: Cannot assign to 'num' because it is a 'foreach iteration variable'
    

    That error is C# protecting you from a mistake. Even if the assignment were allowed, it would have no effect on the array, and the next section explains why.

    The loop variable is a copy

    With an int[], each pass of a foreach loop copies the current element into the loop variable. The variable num is a separate value that happens to start out equal to nums[0], then nums[1], and so on. Changing it can't reach back into the array, in the same way that changing one variable doesn't change the variable you copied it from:

    int number = 100;
    int numberCopy = number;
    numberCopy++;
    
    Console.WriteLine(numberCopy); // 101
    Console.WriteLine(number);     // 100
    

    C# makes a foreach iteration variable read-only. Many languages let you modify it silently, and the bug only shows up as a loop that "does nothing". In C# the compiler points at the exact line instead.

    What you can still change through a copy

    If the array holds objects (instances of a class), the loop variable is a copy of the reference, not of the object. You still can't assign a new object to the loop variable, but you can change the object's members through it, and the change is visible in the array. That is a different situation from the one in this lesson: an int is a plain value, so the copy is all there is.

    The fix: loop over the indices

    To change an element, you have to write to the array slot itself. The expression nums[i] is that slot, so the loop needs the position i and not just the value. A for loop provides it.

    1. Replace foreach with a counting loop. Start i at 0, continue while i < nums.Length, and add one after each pass. Arrays use Length, a property with no parentheses.
    2. Read the element through its index. The test becomes nums[i] % 2 == 0. Reading by index gives the same value the old loop variable held.
    3. Write the element through its index. nums[i]++ and nums[i]-- change the array slot in place, which is what the original lines were trying to do.

    The fixed code in C#

    int[] nums = {1, 2, 3, 4, 5};
    
    for (int i = 0; i < nums.Length; i++) {
        if (nums[i] % 2 == 0) {
            nums[i]++;
        } else {
            nums[i]--;
        }
    }
    

    Tracing the first three passes shows what happens:

    • i = 0: nums[0] is 1, which is odd, so it becomes 0.
    • i = 1: nums[1] is 2, which is even, so it becomes 3.
    • i = 2: nums[2] is 3, which is odd, so it becomes 2.

    After all five passes, the array holds 0, 3, 2, 5, 4. Each element is visited exactly once and changed at most once, so the order of the checks can't cause one change to confuse another.

    To see the result, print the array after the loop with Console.WriteLine(string.Join(", ", nums));, which shows 0, 3, 2, 5, 4.

    Common mistakes

    • Using <= instead of <. The last valid index is nums.Length - 1. With i <= nums.Length, the final pass reads nums[5] and throws an IndexOutOfRangeException.
    • Writing nums.Length() or nums.length. C# is case-sensitive, and Length is a property, not a method. Code ported from Java often has this slip.
    • Copying the Java or Python loop syntax. C# writes foreach (int num in nums) with the keyword in. A colon, as in for (int num : nums), doesn't compile.
    • Working with a copy of the whole array. Assigning int[] other = nums; doesn't copy the elements. Both names refer to the same array, so changes through either one are visible through the other.
    • Growing or shrinking a List<int> inside foreach. Adding or removing items while a foreach is running makes the next step throw an InvalidOperationException. Use an index loop for that kind of work as well.

    Key takeaways

    • A foreach variable holds a copy of each element, and C# makes it read-only.
    • To change array elements in place, loop over the indices and assign to nums[i].
    • Use foreach when you only need to read values, and a for loop with an index when you need to write them.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free