4

Why does the following c code end up in an infinite loop?

for(unsigned char i = 0; i <= 0xff; i++){}

It is the same result with:

for(unsigned char i = 0; i <= 0xff; ++i){}

In which way do I have to modify the code, to get it working as expected (without using int or unsigned int datatype)?

MD XF
  • 7,860
  • 7
  • 40
  • 71
fragwürdig
  • 219
  • 2
  • 6

4 Answers4

18

If you really need to use unsigned char then you can use

unsigned char i = 0;
do {
    // ... 
} while(++i);

When an arithmetic operation on an unsigned integer breaks its limits, the behaviour is well defined. So this solution will process 256 values (for 8-bit unsigned char).

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
8

The range of an unsigned char is 0 to 255 (0xff). Adding 1 to 0xff makes 0x100, which when assigned back to an unsigned char wraps around back to 0.

So the comparison i <= 0xff will always be true. Hence the infinite loop.

If you want the loop to stop after 0xff, use int as the datatype. This is a range of at least -32767 to 32767, and typically more.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • Hmm, perhaps OP should remove *(without using `int` or `unsigned int` datatype)* from the question. – Weather Vane Aug 03 '17 at 20:12
  • "The range of an unsigned char is 0 to 255 (0xff)." on some platforms. – Slava Aug 03 '17 at 20:20
  • 1
    Nitpick: the minimum required range of `int` is actually `-32767` to `32767` (to allow for one's complement machines) – Andrew Sun Aug 03 '17 at 20:21
  • Detail: "Adding 1 to 0xff will wraparound back to 0" --> adding 1 to 0xFF will result in 256, an `int`. It is the assignment of 256 back to the `unsigned char` that is handle _like_ unsigned "overflow" and assign 0 to `i` due to an assignment conversion. – chux - Reinstate Monica Aug 03 '17 at 20:24
  • 1
    @chux Good point. Edited to reflect, and fixed the minimum range of an `int`. – dbush Aug 03 '17 at 20:26
  • @AndrewSun: Meta-nitpick: to allow for one's complement *and sign-and-magnitude* machines. And also two's complement machines that treat the most negative value as a trap representation. – Keith Thompson Aug 03 '17 at 23:16
  • @Slava _on some platforms_ ?? On some platforms is not. I would say nowadays only specialised old DSP processors only. – 0___________ Aug 03 '17 at 23:23
  • @Andrew Sun _to allow for one's complement machines_ do know any younger than 30y? And sign and magnitude younger than 50y? – 0___________ Aug 03 '17 at 23:28
  • @PeterJ so what? C++ does not guarantee that byte has 8 bits, so you need to mention that range is on machines that have it. – Slava Aug 04 '17 at 13:58
  • @Slava did you ever worked on the machine which has a char != 8bit. I personally did not and I do this job for 30+ years. But if I have to write a firmware for a DSP processor the portability of the code will by the least important issue. – 0___________ Aug 04 '17 at 14:04
  • @PeterJ from language point of view that the case, it needs to be mentioned. If you need to write portable code or you meet such platform or not is irrelevant. – Slava Aug 04 '17 at 14:06
  • Programming is a very practical science. Practically your remarks have absolutely no value - because the probability of having any contact with implementation where char != 8 bits is almost zero, and it can be treated as an exemption from the common rule. Such a divagations are purely scholastic. – 0___________ Aug 04 '17 at 14:12
5

A typical for loop relies on being able to detect the termination condition after the last iteration of the loop. In your case, as other answers have pointed out, i <= 0xff is always true (given that i is of type unsigned char and that UCHAR_MAX==0xff, which is typical).

You can run into the same kind of problem near the bounds of any integer type. For example, this:

for (int i = INT_MAX - 9; i <= INT_MAX; i ++) {
    /* ... */
}

is (probably) also an infinite loop (except that overflow for signed integer types has undefined behavior as opposed to the well-defined wraparound semantics for unsigned integers, and an optimizing compiler can take advantage of that and -- but I digress). Just don't do that.

One of many solutions is to move the test to the bottom of the loop, before the increment:

for (unsigned char i = 0; ; i ++) {
    printf("%d\n", i);
    if (i == 0xff) break;
}

The second clause of a for loop is the termination condition, evaluated before each iteration. If you leave it empty, it's treated as always true, giving you an infinite loop (for (;;) is a common idiom for a simple infinite loop). We test whether i is equal to 0xff at the bottom of the loop. If it is then we've just executed the last iteration, and we can break out of the loop.

(Some might prefer to use a while loop here, but I like the for because it lets us combine the declaration of the loop control variable and the increment in a single construct.)

(Strictly speaking the maximum value of unsigned char isn't necessarily 0xff or 255, but it will be on any system you're likely to encounter. Implementations for some DSPs have CHAR_BIT > 8, and thus UCHAR_MAX > 255.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
2

Because you wraparound. An unsigned char values range between 0-255, and because unsigned arithmetic are well defind, you actually wrap the value of i to 0 and the condition is still met and the iteration continues ad infinitum.

In the case of signed values, it is an undefined behaviour and value stored in i might not be 0. But still it will be smaller than the maximum value you could store in a char and the condition is still met.

Tony Tannous
  • 14,154
  • 10
  • 50
  • 86
0___________
  • 60,014
  • 4
  • 34
  • 74
  • 4
    no, overflowing unsigned integers is well defined; they wrap under modulo arithmetic. – Justin Aug 03 '17 at 20:01
  • 4
    The `unsigned` value does not overflow, it wraps. A `signed` value would overflow. – Weather Vane Aug 03 '17 at 20:17
  • There is no addition or arithmetic "overflow" issue. There is a similar issue though, that of assigning 256 to an `unsigned char`. Same result, `i` is 0. – chux - Reinstate Monica Aug 03 '17 at 20:28
  • 1
    "In the case of signed values" does not apply here. The value is going from 0 to 255 to 256 (for a moment) to 0... There are no signed values - they are all non-negative. If you are considering signed types for `i`, the answers differs if `i` is `int` or sub-`int`. One is UB the other IDB/exception. – chux - Reinstate Monica Aug 03 '17 at 23:02
  • @chux it was only said for the undefined behavior, so that if he uses the great solution proposed by WeatherVane, he needs to know signed values might not wrap to 0 and the do-while won't work as expected. – Tony Tannous Aug 04 '17 at 04:45