2

The code below that I have been having strange issues with is meant to trim off the unused portion of an integer array, and then convert it into a string.

Ex: _ABC__DE______ would become _ABC__DE.

The problems show up when the input is filled with the default character. ("_" in the example).

sLength is the length of the integer array chars

The problematic code:

  int inputLength = sLength - 1;

  while (chars[inputLength] == defaultChar && inputLength >= 0) {
    inputLength--;
  }

  inputLength++;

  Serial.println("input length: " + String(inputLength));
  // (in)sanity check
  Serial.println(inputLength);
  Serial.println(String(inputLength));
  Serial.println(inputLength <= 0);
  Serial.println(0 <= 0);
  Serial.println(inputLength == 0);
  Serial.println(0 == 0);

  if (inputLength <= 0) {
    //reset cursor position
    Serial.println("index set to 0");
    index = 0;
  } else {
    output = "";
    for (int i = 0; i < inputLength; i++) {
      char c = charSet[chars[i]];
      if (c == '_') {
        c = ' ';
      }
      output += c;
    }
    done = true;
  }

The output when given an array filled with defaultChar:

input length: 0
0
0
0
1
0
1

If I'm interpreting correctly, the output means that 0 > 0 and 0 =/= 0 on even lines, but 0 <= 0 and 0 = 0 on odd lines.


The workaround I've come up with is replacing

  while (chars[inputLength] == defaultChar && inputLength >= 0) {
    inputLength--;
  }

with one of the following

  while (inputLength >= 0 && chars[inputLength] == defaultChar) {
    inputLength--;
  }

.

  while (chars[inputLength] == defaultChar) {
    inputLength--;
    if (inputLength < 0) {
      break;
    }
  }

which both result in an output of:

input length: 0
0
0
1
1
1
1
index set to 0

Why does this change the result? As far as I knew until now, the && operator was commutative.

Is there something that I am missing that makes

chars[inputLength] == defaultChar && inputLength >= 0

not equal to

inputLength >= 0 && chars[inputLength] == defaultChar?

If It's relevant, this is being run on an 328P Arduino Nano with the old bootloader using IDE 1.8.8

Lundin
  • 195,001
  • 40
  • 254
  • 396

2 Answers2

5

&& is not commutative. It evaluates the left operand first and then stops if the left operand evaluated to 0.

Your original code fails because at some point it evaluates chars[-1] (which causes undefined behaviour if chars is an array). The alternative version does not have that problem because it performs the >= 0 test before using inputLength as an array index.

M.M
  • 138,810
  • 21
  • 208
  • 365
3

&& is commutative in the sense that the result of a && b is same as the result of b && a. But the built-in operator && has a short-circuiting behavior. This means that if the result of a && b can be decided by evaluating the first operand alone, the second one is not evaluated.

So when the first operand is chars[inputLength] == defaultChar and inputLength is -1, you enter the territory of undefined behavior which means the behavior of the program is unpredictable. But with the workarounds, you avoid undefined behavior because of the inputLength >= 0 and inputLength < 0 checks and therefore the code works as intended.

As @PeteBecker notes: a() && b() is not commutative if either a() or b() has side effects.

P.W
  • 26,289
  • 6
  • 39
  • 76