Thread: Order of operations of for loops

  1. #1
    Registered User
    Join Date
    Jan 2017
    Posts
    16

    Order of operations of for loops

    Hi,

    As I understand at the moment a for loop is supposed to be in the form of:

    Code:
    for(expression1; expression2; expression3) {
        statements
    }
    And that expression1 is evaluated only once at the beginning, expression2 is evaluated before each loop and if it is true the statements are evaluated and then expression3 is evaluated. and the next loop begins with the check of expression2.

    But, if I do these two loops:

    loop style 1:
    Code:
        for(i=io=0;io<MAXO;io++) {
            for(ii=0;ii<MAXI && ( twodem[io][ii]=*(argv[2]+i) ) !='\0';ii++) {
                i++;
            }
        }
    loop style 2:
    Code:
        for(i=io=0;io<MAXO;io++) {
            for(ii=0;ii<MAXI && ( twodem[io][ii]=*(argv[2]+i) ) !='\0';i++ && ii++) {
                ;
            }
        }
    The first produces the results I expect, whereas the second does not.

    Assuming I call the program so that argv[2] is "testing" when I use the first style loop the values at twodem[0][0] to twodem[0][3] are "test" whereas the second loop results in the values being "esti".

    I'm guessing the problem is with expression3 being "i++ && ii++" and somehow i is getting incremented before expression2 but ii is not.

    is expression3 invalid?

    Oh, I just had a thought, what if I used an || instead of an && in expression3?

    Well, crud. That results in "tsti" instead of either "test" or "esti".

    Okay I am even more sure I am doing expression3 wrong.

    Any thoughts?

  2. #2
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    That's the craziest update expression I've ever seen. Your problems have nothing to do with for loops and are all about short-circuit evaluation of && and ||. Basically, if evaluating the first operand is enough to determine the result, then the second expression is not evaluated at all. So when you use && and i is 0, i++ && ii++ is determined to be false without the ii++ part being evaluated at all. When you use || then the only time ii++ is evaluated is when i is 0.

  3. #3
    Registered User
    Join Date
    Jan 2017
    Posts
    16
    Okay, I think I know what is going on.

    I wrote this test script with no loops:

    Code:
    #include <stdio.h>
    int main() {
        int i,ii,c;
    
        i=ii=c=0;
        printf("\nStart:i=%d,ii=%d\n",i,ii);
    
        c++;
        if( i++ || ii++ ){
            printf("%d:if:i=%d,ii=%d\n",c,i,ii);
        } else {
            printf("%d:else:i=%d,ii=%d\n",c,i,ii);
        }
    
        c++;
        if( i++ && ii++ ){
            printf("%d:if:i=%d,ii=%d\n",c,i,ii);
        } else {
            printf("%d:else:i=%d,ii=%d\n",c,i,ii);
        }
    
        i=ii=0;
        printf("\nReStart:i=%d,ii=%d\n",i,ii);
    
        c++;
        if( i++ && ii++ ){
            printf("%d:if:i=%d,ii=%d\n",c,i,ii);
        } else {
            printf("%d:else:i=%d,ii=%d\n",c,i,ii);
        }
    
        c++;
        if( i++ || ii++ ){
            printf("%d:if:i=%d,ii=%d\n",c,i,ii);
        } else {
            printf("%d:else:i=%d,ii=%d\n",c,i,ii);
        }
    
    }
    When I run the code I get:

    Start:i=0,ii=0
    1:else:i=1,ii=1
    2:if:i=2,ii=2

    ReStart:i=0,ii=0
    3:else:i=1,ii=0
    4:if:i=2,ii=0

    Because the output for 1 says "else" it wasn't "true" but both counters got incremented.

    Because the output for 2 says "if" it was true and both got incremented.

    But after the restart 3 says "else" just like 1 but only the first variable was incremented.

    And 4 was true but still only the first variable was incremented.

    So, when there is an && both sides of the and are getting evaluated and that is why both counters went up. That makes sense, but since the else is what printed that means neither side was true, right?

    I seem to remember in the K&R that in something like "a=b++" would give the value of b before it was incremented to a, and then increment b, so since my counters are starting at 0 the expression is considered false and then it gets incremented.

    So in my original problem that should mean I can ... what? Reverse it? So I should change all the i++ and ii++ to ++i and ++ii?

    Let me try that.

    Hmmmn. I added a new block:
    Code:
        i=ii=0;
        printf("\nReReStart:i=%d,ii=%d\n",i,ii);
    
        c++;
        if( ++i || ++ii ){
            printf("%d:if:i=%d,ii=%d\n",c,i,ii);
        } else {
            printf("%d:else:i=%d,ii=%d\n",c,i,ii);
        }
    
        c++;
        if( ++i && ++ii ){
            printf("%d:if:i=%d,ii=%d\n",c,i,ii);
        } else {
            printf("%d:else:i=%d,ii=%d\n",c,i,ii);
        }
    And got:

    ReReStart:i=0,ii=0
    5:if:i=1,ii=0
    6:if:i=2,ii=1

    So it is true both times but of course 5 doesn't increment ii because of the ||, which is fine.

    So if I want to increment more than one counter in a for loop I should use ++varname and use && operators.

    Is there a better way to do this?

    Doing "++a && ++b && ++c" seems okay right?

  4. #4
    Registered User
    Join Date
    Jan 2017
    Posts
    16
    Quote Originally Posted by algorism View Post
    That's the craziest update expression I've ever seen. Your problems have nothing to do with for loops and are all about short-circuit evaluation of && and ||. Basically, if evaluating the first operand is enough to determine the result, then the second expression is not evaluated at all. So when you use && and i is 0, i++ && ii++ is determined to be false without the ii++ part being evaluated at all. When you use || then the only time ii++ is evaluated is when i is 0.
    Thanks for the input. I could have saved myself some work by just going away and reading or something and then come back and read your post before gnawing at the problem.

    Can you explain why the expression seems crazy to you?

    I'm not saying it isn't I would just honestly like the input.

    Am I trying to load too much stuff into places it doesn't belong?

    It certainly wouldn't have hurt me to put one or both increments inside the loop, but then again it wouldn't have "hurt" to use a while loop I guess.

    Thoughts?

  5. #5
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    Quote Originally Posted by TechnoGourmet View Post
    Can you explain why the expression seems crazy to you?
    I simply haven't seen anything like that before.
    What were you trying to achieve?

    EDIT: The code is equivalent to this:
    Code:
    int i = 0, io = 0;
    while (io < MAXO) {
    
        ii = 0;
        while (ii < MAXI && (twodim[io][ii] = argv[2][i])) {   // EDIT2:  added some parens here
    
            if (i++ != 0)
                ii++;
        }
    
        io++;
    }
    BTW, it's always best if you post a full, copy/paste/runnable program so we can easily run it ourselves.
    Last edited by algorism; 01-19-2017 at 08:42 PM.

  6. #6
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    It occurs to me that maybe you were just trying to increment both variables and didn't realize that you can use the comma operator to do that:
    Code:
        for (i = io = 0; io < MAXO; io++)
            for (ii = 0; ii < MAXI && (twodim[io][ii] = argv[2][i]); i++, ii++)
                ;

  7. #7
    Registered User
    Join Date
    Jan 2017
    Posts
    16
    Quote Originally Posted by algorism View Post
    I simply haven't seen anything like that before.
    What were you trying to achieve?
    The double for loop was supposed to be filling a 2D array with data from ... well some place. I wanted to be able to understand how to do it with a pointer into some data from elsewhere so I wanted the "i" variable to represent that offset into the data. But since I was learning/testing I was using data from arguments but I wanted the logic of the loop to be what I thought would be used for a pointer offset so that's why "=*(argv[2]+i)" because I was "pretending" that "argv[2]" was just some pointer so it would eventually have been something like "*ptr+i".

    The constants where the same ones used to set the size of the array like: char twodem[MAXO][MAXI]; and there to prevent an overflow and the check against '\0' was so that if the string ended the loop would stop.

    I don't know if that clears it up at all.

    Quote Originally Posted by algorism View Post
    BTW, it's always best if you post a full, copy/paste/runnable program so we can easily run it ourselves.
    Yeah, I get that. I kind of thought the full code in this case, which was/is a mess, would confuse the issue. I should have pulled out those loops, simplified them and put them in a new test program to demonstrate the problem. I'll try to be better about that in the future.

    I really thought if I kept it simple enough I might get a quick answer pointing out the exact obvious problem.

    Hey wait, that is what happened.

    Anyway, thanks for the input.

  8. #8
    Registered User
    Join Date
    Jan 2017
    Posts
    16
    Quote Originally Posted by algorism View Post
    It occurs to me that maybe you were just trying to increment both variables and didn't realize that you can use the comma operator to do that:
    Well hey howdy hey. I should have thought of that.

    Thanks.

  9. #9
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    Quote Originally Posted by TechnoGourmet View Post
    Well hey howdy hey. I should have thought of that.
    All you wanted to do was increment both variables! (*facepalm*) If I had just read the code more closely I would have realized that sooner. Your use of && threw me.

    The comma operator was possibly originally added to the language for just that purpose in the for loop. It can be fruitfully applied in both the initial expression and the update expression. It's not much use in the test expression.
    Code:
    #include <stdio.h>
    
    int main() {
        int i, j;
        for (i = 0, j = 1; j <= 64; i++, j *= 2)
            printf("%d %d\n", i, j);
        return 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Order of operations question
    By Mathieas in forum C++ Programming
    Replies: 3
    Last Post: 04-08-2012, 09:46 AM
  2. order of operations...
    By jverkoey in forum C++ Programming
    Replies: 2
    Last Post: 05-21-2004, 11:38 PM
  3. order of operations when programming in API
    By stallion in forum Windows Programming
    Replies: 2
    Last Post: 12-07-2002, 05:34 PM
  4. Order of Operations
    By C-Duddley in forum C Programming
    Replies: 3
    Last Post: 12-06-2002, 08:10 PM
  5. order of operations
    By DavidP in forum A Brief History of Cprogramming.com
    Replies: 18
    Last Post: 10-31-2002, 08:51 PM

Tags for this Thread