rve
rve

Reputation: 6055

clear code for counting from 0 to 255 using 8-bit datatype

I was wondering if there is a clean way of counting from 0 to 255 using an 8 bit datatype, something like:

for(uint8_t i(0);i<=255;++i)
{
    ....
}

This obviously will not work but it makes it clear you want to count from 0 to 255.

A working solution would be something like:

uint8_t i(0);
do
{
    ...
    ++i;
}
while(i > 0);

But here it is not at all clear it counts from 0 to 255.

This will also work but it is just ugly IMHO:

uint8_t i(0);
while(true)
{
    ...
    if (i == 255)
    {
        break;
    }
    ++i;
}

So I was wondering, is there a clean way of doing this without using a larger datatype?

EDIT:

Upvotes: 8

Views: 4805

Answers (10)

ot.
ot.

Reputation: 800

What about:

uint8_t i = 0;
do {
    ...
} while (i++ != 255);

Upvotes: 21

Ferruccio
Ferruccio

Reputation: 100648

I would suggest that the simple solution:

for (int i = 0; i < 256; ++i) {
   ...
}

is probably also going to be the most efficient solution.

  1. Even if you use a smaller (1-byte) data type. The C compiler will promote it to an int in any expression.

  2. On an 8-bit controller an int is probably 16-bits. Using a single-byte type will only save one byte of stack space. Then again the compiler may put that variable in a register, so there will not be any space savings anyway.

Check the assembly code generated by the above code, then decide whether or not it needs (space) optimization.

Upvotes: 2

Southern Hospitality
Southern Hospitality

Reputation: 1280

No, there's no clear way to do it in plain old C, as the conditional is checked after the increment and if you compare <= 255, you will loop forever, as an 8-bit value cannot exceed 255 and terminate.

So it becomes.

uint8_t i = 0;
while (1)
{
  /* your stuff */
  if (255 == i++)
    break;
}

Unless you think that checking against 0 (seeing the wraparound) is clear in your book. It's not clear in mine.

Note that 8-bit types are very inefficient on many compilers, producing unnecessary sign extends at times. You might want to use the uint_least8_t type instead, it'll likely expand to the word size of your system and run quicker.

Upvotes: 0

ephemient
ephemient

Reputation: 204718

for (uint8_t i(0); (int)i <= 255; ++i)

Seems perfectly clear to me.

Even if you are trying to use a 1-byte counter, your compiler may very well turn it into this instead:

for (int ii(0); ii <= 255; ++ii) {
    uint8_t i(ii);
    ...
}

For example, GCC does, because it's faster.

$ cat >test.c
void foo(char);
void bar(void) {
    char i;
    for (i = 0; i <= 255; i++)
        foo(i);
}
^D
$ cc -m32 -c -O3 test.c
$ objdump -d test.o

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <bar>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   53                      push   %ebx
   4:   31 db                   xor    %ebx,%ebx
   6:   83 ec 04                sub    $0x4,%esp
   9:   8d b4 26 00 00 00 00    lea    0x0(%esi,%eiz,1),%esi
  10:   89 1c 24                mov    %ebx,(%esp)
  13:   83 c3 01                add    $0x1,%ebx
  16:   e8 fc ff ff ff          call   17 <bar+0x17>
  1b:   eb f3                   jmp    10 <bar+0x10>
$ cc -m64 -c -O3 test.c
$ objdump -d test.o

test.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <bar>:
   0:   53                      push   %rbx
   1:   31 db                   xor    %ebx,%ebx
   3:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
   8:   89 df                   mov    %ebx,%edi
   a:   83 c3 01                add    $0x1,%ebx
   d:   e8 00 00 00 00          callq  12 <bar+0x12>
  12:   eb f4                   jmp    8 <bar+0x8>

Upvotes: 0

Clifford
Clifford

Reputation: 93476

Well if you want to make less-than-clear code clear, you might always add a comment ;)

One solution is to place the body of the loop in a function (or macro if public flogging is not a concern), and then:

uint8_t i ;
for( i = 0; i < 255; i++ )
{
    body(i) ;
}
body(i) ;

or if you'd rather not extend the scope of i and C++ scope rules apply:

for( uint8_t i = 0; i < 255; i++ )
{
    body(i) ;
}
body(255) ;

Upvotes: 0

cschol
cschol

Reputation: 13059

You seem to want to convey the message of counting from 0 to 255 by the data type you are using, but what's the significance of 255? You should probably #define this magic number with a name explicitly stating the purpose of it. Also, a comment above the statement would be way more helpful than trying to "encode" all that information in somewhat weird looking statements.

For example:

#define MAX_RETRIES   255
unsigned int retries;

for(retries = 0; retries <= MAX_RETRIES; ++retries)
{
  do_retry_work();
}

If needed, add a comment, why the number of retries is limited to 255.

Upvotes: 2

Dipstick
Dipstick

Reputation: 10119

What's wrong with the obvious?

i = 255;
do {
 work();
} while (i--);

Upvotes: 5

Mattias Nilsson
Mattias Nilsson

Reputation: 3757

You spend this much effort to save one byte? Your last example would work, or you could do some sort of combination of the first and third:

for (uint8_t i = 0;;++i)
{
   work();
   if (i == 255)
       break;
}

Still, ask yourself if the added ugliness in the code is worth saving that one byte. If you do go along with a solution like that, you probably should document why you are not doing it the obvious way.

Upvotes: 0

Remo.D
Remo.D

Reputation: 16512

I'm not sure what you mean but

 uint8_t i = 0;

 do {
    ...
 } while (++i & 255) ;

should do what you ask and has an explicit reference to 255 (useless if your compiler is C99 standard and uint8_t is really 8 bits).

Upvotes: 8

pmg
pmg

Reputation: 108978

Count in two halves?

uint8_t k = 0;
while (k & 0x80 == 0) {
  work();
  k++;
}
while (k & 0x80 == 1) {
  work();
  k++;
}

Upvotes: 0

Related Questions