1

I tried looping over an array in different ways using loop unrolling.

#define MYSIZE 8

int main()
{
    int A[MYSIZE];
    int B[MYSIZE];
    int C[MYSIZE];

    int i = 0;
    while(i < MYSIZE)
    {
        A[i] = i;
        i++;
    }

    /* LOOP 1 */
    i = 0;
    while (i< MYSIZE)
    {
        B[i+0] = A[i+0];
        B[i+1] = A[i+1];
        B[i+2] = A[i+2];
        B[i+3] = A[i+3];
        i += 4;
    }

    /* LOOP 2 */
    i = 0;
    while (i < MYSIZE)
    {
        C[i] = A[i++];
        C[i] = A[i++];
        C[i] = A[i++];
        C[i] = A[i++];
    }

    printf(" i | A | B | C|\r\n");
    i = 0;
    while (i < MYSIZE)
    {
        printf(" %d | %d | %d | %d |\r\n",i,A[i],B[i],C[i]);
        i++;
    }
}

Which does give me this result:

 i | A | B | C |
 0 | 0 | 0 | 1578166688 |
 1 | 1 | 1 | 0 |
 2 | 2 | 2 | 1 |
 3 | 3 | 3 | 2 |
 4 | 4 | 4 | 3 |
 5 | 5 | 5 | 4 |
 6 | 6 | 6 | 5 |
 7 | 7 | 7 | 6 |

I thought that A,B and C should contain the same numbers. As I understood i++, LOOP 2 should be the same as:

/* LOOP 3 */
i = 0
while(i < MYSIZE)
{
    C[i] = A[i];
    i++;
    C[i] = A[i];
    i++;
    C[i] = A[i];
    i++;
    C[i] = A[i];
    i++;
}

Which is not the case. LOOP 3 actually works fine but LOOP 2 does not. What am I doing wrong ?

Ambadrant
  • 45
  • 1
  • 7

2 Answers2

5

This expression:

 C[i] = A[i++];

Invokes undefined behavior because the variable i is both read and written without a sequence point.

While it is true that the right hand side of an assignment must be fully evaluated before it can be assigned to the left side, the evaluation of each subexpression is unsequenced, as is the side effect of i being incremented.

This is spelled out in section 6.5.16p3 of the C standard regarding the assignment operator:

An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment, but is not an lvalue. The type of an assignment expression is the type the left operand would have after lvalue conversion. The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.

In this specific example, the value of i could first be read to use as the index of C, then it could be read and written as part of the i++ expression on the right side. Alternatively, i++ could be evaluated first and incremented, and then i could be read to be used as the index of C.

The increment of i should be done as part of a separate statement to avoid this.

C[i] = A[i];
i++;
dbush
  • 205,898
  • 23
  • 218
  • 273
0

With dbush's response, I did a test using an additional variable to avoid the incidence and I got the expected result.

/* LOOP 2 */
i = 0;
int j = 0;
while (i < MYSIZE)
{
    C[j++] = A[i++];
}