1

I have a sketch containing the following method:

// Writes zeros to the complete screen clearing it:
void clearScreen(uint8_t val) {
  setDrawArea(0x00, 0x7f, 0x00, 0x07); // complete screen
  SSD1306.ssd1306_send_data_start();
  for (int c = 0; c < 128 * 8; c++) {
    SSD1306.ssd1306_send_byte(val);
  }
  SSD1306.ssd1306_send_data_stop();
}

The whole sketch compiles with a size of 6730 Bytes. While refactoring the code I mistakenly changed "c" to uint8_t. After this I compiled to 4124 Bytes. Does this come from the compiler recognizing that the loop condition will never be reached and cut off all code behind or is this some kind of strange optimization event? I'm asking because I can't test the sketch in the moment.

4
  • 1
    Can you confirm this behavior without code from extra libraries? This might just be an optimization with the library (maybe as simple as that another function is called for uint_8, which uses less space)
    – chrisl
    Jul 3, 2018 at 22:10
  • this expression: c < 128 * 8; results in the uint_8 variable 'c' to be overflowed. The result is undefined behavior. With undefined behavior, anything can happen Jul 3, 2018 at 22:25
  • @user3629249: No. Overflowing a uint8_t is perfectly safe and well defined. It just rolls over modulo 256. Only the signed integer overflows are undefined behavior. Jul 4, 2018 at 8:29
  • @user3629249, even if c were signed, making the overflow an issue, that comparison doesn't cause an overflow, the increment c++ might.
    – ilkkachu
    Jul 4, 2018 at 12:47

1 Answer 1

2

Without being able to proof it (since I don't have your libraries and you gave only a small part of the sketch), I think this is what happens:

user3629249 is half right. If you change c to type uint8_t it only has 8 bits (hence the Name), so its maximum value will be 255. When you add 1 to it at this value, the variable will overflow and going back to 0. Thats normal overflow behavior, like used with the Arduinos Timer registers. (No undefined behavior there) So the maximum value c can reach is 255.

When compared to a bigger literal the value of c gets promoted to the correct type, so that the two values can be compared. But a variable that is at maximum 255 is always lower than 128*8. So you have an infinite loop there. The compiler can see this and doesn't include the functions from the rest of clearScreen(), since that is unreachable code.

All in all your code won't work as intended with uint8_t. You need a bigger type, that can hold the full range, that you operate. And this optimization is not strange, but very reasonable, since you really don't need the rest of clearScreen() in that case.

5
  • 1
    Note that any code that follows a call to clearScreen() is also unreachable. The compiler may be removing a lot more than ssd1306_send_data_stop(). Jul 4, 2018 at 8:32
  • Does the compiler covert the 128*8 to a 8-bit value too? 128*8=1024; which is 0 when converted/truncated to a byte. Since it’s zero the for loop will never be executed, so ssd1306_send_byte is never called. The compiler knows this at compile time, so it can remove the for loop. And if the send_byte function isn’t called anywhere else it can remove the entire function, drastically reducing the size.
    – Gerben
    Jul 4, 2018 at 11:53
  • @Gerben, If I'm not mistaken, all operations promote the operands to at least int, and in any case, an unadorned constant is an int, so it's 1024 and c is promoted to match. It's just uint8_t that can never reach such a value, so the condition is always true.
    – ilkkachu
    Jul 4, 2018 at 12:46
  • That fits my thoughts - it seems I underestimated the compiler. There's a good part of code behind this method call and I'm quite sure the compiler understands that the "for" condition will never be false and cuts off everything behind. I'm afraid I got comfortable in the higher languages expecting some kind of compiler warning in such a case. Good thoughts about the conversion issue by the way. Jul 4, 2018 at 14:29
  • 1
    @needfulthing: Re “expecting some kind of compiler warning”: The compiler does warn you that “comparison is always true due to limited range of data type”, but only if you set “Compiler warnings” to “All” in the IDE preferences. There is an open issue requesting this to be the default, but the Arduino folks resist, as they are afraid the warnings may “scare newcomers”. Jul 4, 2018 at 18:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.