Pregunta

Me preguntaba si hay una manera limpia de contar de 0 a 255 utilizando un tipo de datos de 8 bits, algo como:

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

Esto, obviamente, no va a funcionar, pero deja claro que desea contar de 0 a 255.

Una solución de trabajo sería algo como:

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

Pero aquí no está del todo claro que cuenta de 0 a 255.

Esto también funciona, pero es sólo mi humilde opinión fea:

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

Así que me preguntaba, ¿hay una forma limpia de hacer esto sin necesidad de utilizar un tipo de datos más grande?

EDIT:

  • Me gusta la versión que utiliza para porque hace que su intención clara sin pensar:. Bucle de 0 a 255. Todas las demás versiones requieren una reflexión sobre lo que está pasando y por lo tanto más propensos a confundir a otros
  • No quiero usar int porque el código es para un microcontrolador de 8 bits con poca memoria.
¿Fue útil?

Solución

¿Qué hay de:

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

Otros consejos

No estoy seguro de lo que quieres decir pero

 uint8_t i = 0;

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

debería hacer lo que pida y tiene una referencia explícita a 255 (inútil si su compilador es estándar C99 y uint8_t es realmente 8 bits).

¿Qué hay de malo en lo obvio?

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

Parece que desee transmitir el mensaje de contar de 0 a 255 por el tipo de datos que está utilizando, pero ¿cuál es el significado de 255? Probablemente debería #define este número mágico con un nombre que indica explícitamente el objetivo de la misma. También, un comentario sobre la declaración sería mucho más útil que tratar de "codificar" toda esa información en declaraciones a un tanto extraños.

Por ejemplo:

#define MAX_RETRIES   255
unsigned int retries;

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

Si es necesario, agregar un comentario, por qué el número de reintentos es limitado a 255.

Yo sugeriría que la solución simple:

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

También es probable que va a ser la solución más eficiente.

  1. Incluso si utiliza un (1 byte) tipo de datos más pequeño. El compilador C promoverá a un int en cualquier expresión.

  2. En un controlador de 8 bits un int es probablemente de 16 bits. El uso de un tipo de un solo byte sólo ahorrará un byte de espacio de pila. Por otra parte el compilador puede poner esa variable en un registro, por lo que no habrá ningún ahorro de espacio de todos modos.

Compruebe el código ensamblador generado por el código anterior, a continuación, decidir si es o no necesario (espacio) de optimización.

Contar en dos mitades?

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

gastar tanto esfuerzo por salvar un byte? Su último ejemplo podría funcionar, o se puede hacer algún tipo de combinación de la primera y tercera:

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

Sin embargo, preguntarse si la fealdad añadido en el código que vale la pena salvar un byte. Si tienes que ir junto con una solución de esa manera, es probable que debe documentar por qué no están haciendo de la manera obvia.

Bueno, si usted quiere hacer menos-que-claro código claro, siempre se puede añadir un comentario;)

Una solución es colocar el cuerpo del bucle en una función (o macro si flagelación pública no es una preocupación) y, a continuación:

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

o si usted prefiere no ampliar el alcance de i y aplicar reglas de ámbito C ++:

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

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

parece perfectamente claro para mí.

Incluso si usted está tratando de utilizar un contador de 1 byte, el compilador puede muy bien convertirlo en su lugar:

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

Por ejemplo, GCC hace, porque es más rápido.

$ 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>

No, no hay manera clara de hacerlo en el viejo y simple C, ya que la condición se comprueba después de que el incremento y si se compara <= 255, se le bucle infinito, como un valor de 8 bits no puede exceder de 255 y terminar.

Así se vuelve.

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

A menos que usted piensa que la comprobación contra 0 (viendo la envolvente) es claro en su libro. No está claro en la mía.

Tenga en cuenta que los tipos de 8 bits son muy ineficientes en muchos compiladores, produciendo signo innecesario extiende a veces. Es posible que desee utilizar el tipo de uint_least8_t lugar, es probable que va a ampliar al tamaño palabra de su sistema y ejecute más rápido.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top