Skip to content

Control Structures — Find the Bug

10+ exercises. Each shows buggy code in all 3 languages — find, explain, and fix.


Exercise 1: Infinite Loop — Missing Update

Go

func countdown(n int) {
    for n > 0 {
        fmt.Println(n)
        // BUG: n is never decremented
    }
}

Java

public static void countdown(int n) {
    while (n > 0) {
        System.out.println(n);
        // BUG: n is never decremented
    }
}

Python

def countdown(n):
    while n > 0:
        print(n)
        # BUG: n is never decremented

Bug: Loop variable n never changes → infinite loop. Fix: Add n-- / n -= 1 inside the loop.


Exercise 2: Off-by-One in Range

Go

arr := []int{1, 2, 3, 4, 5}
for i := 1; i <= len(arr); i++ { // BUG: starts at 1, goes to len(arr)
    fmt.Println(arr[i])           // index out of range at i=5
}

Java

int[] arr = {1, 2, 3, 4, 5};
for (int i = 1; i <= arr.length; i++) { // BUG
    System.out.println(arr[i]);
}

Python

arr = [1, 2, 3, 4, 5]
for i in range(1, len(arr) + 1):  # BUG: goes to index 5
    print(arr[i])

Bug: Loop starts at 1 (misses first element) and goes one past the end. Fix: for i := 0; i < len(arr); i++


Exercise 3: Switch Fallthrough (Java)

Java

int grade = 85;
String letter;
switch (grade / 10) {
    case 10:
    case 9:
        letter = "A";
    case 8:              // BUG: no break — falls through
        letter = "B";
    case 7:
        letter = "C";
    default:
        letter = "F";
}
System.out.println(letter); // Always prints "F"!

Bug: Missing break statements — every case falls through to default. Fix: Add break; after each case, or use switch expressions (->).


Exercise 4: Wrong Condition in While

Go

func findFirst(arr []int, target int) int {
    i := 0
    for i < len(arr) || arr[i] != target { // BUG: should be &&
        i++
    }
    return i
}

Java

public static int findFirst(int[] arr, int target) {
    int i = 0;
    while (i < arr.length || arr[i] != target) { // BUG: || should be &&
        i++;
    }
    return i;
}

Python

def find_first(arr, target):
    i = 0
    while i < len(arr) or arr[i] != target:  # BUG: or should be and
        i += 1
    return i

Bug: Using ||/or means: continue if EITHER condition is true. When i >= len(arr), it still checks arr[i] → index out of bounds. Fix: Use &&/and — continue only while BOTH conditions are true.


Exercise 5: Else Attached to Wrong If

Go

if x > 0 {
    if x > 100 {
        fmt.Println("big positive")
    }
} else {                         // BUG? Actually correct in Go due to braces
    fmt.Println("non-positive")  // But confusing — looks like it belongs to inner if
}

Java

if (x > 0)
    if (x > 100)
        System.out.println("big positive");
else                              // BUG: else belongs to INNER if, not outer!
    System.out.println("non-positive"); // This prints when x > 0 AND x <= 100

Python

# Python's indentation makes this unambiguous
if x > 0:
    if x > 100:
        print("big positive")
else:                             # Correctly belongs to outer if
    print("non-positive")

Bug: In Java (without braces), else binds to the nearest if — the inner one. Fix: Always use braces {} in Java.


Exercise 6: Boolean Logic Error

All Languages

# Task: check if x is between 1 and 10 (inclusive)
if 1 < x < 10:    # Python: works! (chained comparison)
    print("in range")
// BUG in Go/Java: you can't chain comparisons
if 1 < x < 10 { // COMPILE ERROR in Go
}
// Fix:
if x > 1 && x < 10 {
}
// Java: 1 < x < 10 doesn't compile
if (x > 1 && x < 10) { /* correct */ }

Bug: Python supports chained comparisons; Go and Java don't.


Exercise 7: Loop Variable Scope

Go

for i := 0; i < 5; i++ {
    go func() {
        fmt.Println(i) // BUG: all goroutines print 5
    }()
}

Java

for (int i = 0; i < 5; i++) {
    final int n = i; // Must capture in effectively-final variable
    new Thread(() -> System.out.println(n)).start();
}

Python

funcs = []
for i in range(5):
    funcs.append(lambda: print(i))  # BUG: all lambdas capture same i

for f in funcs:
    f()  # Prints: 4 4 4 4 4

Fix: Capture loop variable by value: - Go: go func(n int) { ... }(i) - Python: lambda n=i: print(n)


Exercise 8: Nested Break Only Exits Inner Loop

Go

for i := 0; i < 10; i++ {
    for j := 0; j < 10; j++ {
        if i*j > 20 {
            break // BUG: only exits inner loop, outer continues
        }
    }
}

Fix: Use labeled break: break outer (Go/Java) or extract to a function (Python).


Exercise 9: Do-While vs While

Java

int n = 0;
do {
    System.out.println(n); // Prints 0 even though condition is false!
    n++;
} while (n > 0 && n < 5);
// BUG: do-while always executes once

Bug: do-while runs the body at least once. If the initial condition is supposed to prevent execution, use while. Fix: Use while (n > 0 && n < 5) { ... } if body shouldn't run when n=0.


Exercise 10: Modifying Collection During Iteration

Go

m := map[string]int{"a": 1, "b": 2, "c": 3}
for k, v := range m {
    if v < 2 {
        delete(m, k) // Go: actually safe for maps during range!
    }
}
// Note: Go allows delete during range, but NOT insert (undefined behavior)

Java

var map = new HashMap<>(Map.of("a", 1, "b", 2, "c", 3));
for (var entry : map.entrySet()) {
    if (entry.getValue() < 2) {
        map.remove(entry.getKey()); // BUG: ConcurrentModificationException
    }
}
// Fix: use Iterator with iterator.remove() or map.entrySet().removeIf(...)

Python

d = {"a": 1, "b": 2, "c": 3}
for k, v in d.items():
    if v < 2:
        del d[k]  # BUG: RuntimeError: dictionary changed size during iteration
# Fix: d = {k: v for k, v in d.items() if v >= 2}

Exercise 11: Early Return in Finally

Java

public static int getValue() {
    try {
        return 1;
    } finally {
        return 2;  // BUG: finally's return OVERRIDES try's return!
    }
}
// Returns 2, not 1!

Python

def get_value():
    try:
        return 1
    finally:
        return 2  # BUG: overrides the return from try
# Returns 2

Bug: finally block's return overrides try block's return. Fix: Never return from finally — use it only for cleanup.