Search code examples
c++variable-declaration

Having to redeclare an int for a second for loop


I am playing around on edabit.com C++ challenges to refresh myself and I was presented with a challenge where I needed to write a function that would, given a vector, return true if all even indices are even and all odd indices are odd. Otherwise return false. The logic and everything of the function was fine. I know there's a few ways to do this but came up with a (I think) creative solution where instead of running through one for loop with a check if the index is odd or even, I instead wrote two subsequent for loops that increment by two each. One starting at 0 and the other starting at 1. Once it finds a mismatch it returns false. Otherwise at the end it will return true. I am coming from JavaScript where you would not need to redeclare the index variable (if using var not let) but this code failed to compile:

bool isSpecialArray(std::vector<int> arr) {
    for(int i = 0; i < arr.size(); i = i + 2){
        if(arr[i] % 2 == 1){
            return false;
        }
    }

    for(i = 1; i < arr.size(); i = i + 2){
        if(arr[i] % 2 == 0){
            return false;
        }
    }

    return true;
}

Stating:

error: use of undeclared identifier 'i'
        for(i = 1; i < arr.size(); i = i + 2){
            ^

And of course declaring int in the second for loop cleared the error and the program executed as expected. I'm guessing this is due to garbage collection, but is there a specific rule in the standard that defines this? Or is this maybe a per compiler thing? My understanding of the garbage collection was that the variable once declared is not cleared from memory until is no longer being used in the current function it is defined in. Is this different due to the scope of the for loop, similar to JavaScript's let?


Solution

  • Firstly, JS and C++ have very different models of scopes, and JS is known (sorry) for its bad practices. In C++ the rule is more consistent: the variable is destroyed whenever the program exits the scope. That means that in this code:

    for (int i = 0; ; ) {
    }
    

    the variable i is defined just for this scope, it cannot be reused in another (not nested) loop, is not seen elsewhere, and the name can be reused or redefined in the inner scopes. This gives you the advantage that it is much easier to control the data flaw and to have less side effects. So the solution for you is to declare another variable i in the second loop:

        for(int i = 1; i < arr.size(); i = i + 2){
            if(arr[i] % 2 == 0){
                return false;
            }
        }
    

    As long as you are from the JS world I would give you more recommendations. First, do not pass the vector by value (unless you realize what you are doing). Pass it by reference or even better by const reference:

    bool isSpecialArray(const std::vector<int> &arr) {
       // ...
    }
    

    Next, a potentially more efficient and more idiomatic way to increase the index is i += 2.

    And the final observation: there is no "creativity" in your solution, but there are drawbacks. This code becomes less cache friendly, and this may be considered as a bad practice.