8

我想知道是否有一种使用 8 位数据类型从 0 计数到 255 的干净方法,例如:

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

这显然行不通,但它清楚地表明你想从 0 数到 255。

一个可行的解决方案是这样的:

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

但这里根本不清楚它从 0 计数到 255。

这也可以,但它只是丑陋的恕我直言:

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

所以我想知道,有没有一种干净的方法可以在不使用更大数据类型的情况下做到这一点?

编辑:

  • 我喜欢使用 for 的版本,因为它不假思索就清楚地表明了它的意图:从 0 循环到 255。所有其他版本都需要对正在发生的事情进行一些思考,因此更容易混淆其他人。
  • 我不想使用 int 因为代码是用于内存不多的 8 位微控制器。
4

10 回答 10

21

关于什么:

uint8_t i = 0;
do {
    ...
} while (i++ != 255);
于 2009-11-10T13:23:20.123 回答
8

我不确定你的意思但是

 uint8_t i = 0;

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

应该按照您的要求做并且明确引用 255(如果您的编译器是 C99 标准并且 uint8_t 实际上是 8 位,则无用)。

于 2009-11-10T13:16:10.013 回答
5

显而易见的有什么问题?

i = 255;
do {
 work();
} while (i--);
于 2009-11-10T13:24:59.350 回答
2

我建议简单的解决方案:

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

可能也将是最有效的解决方案。

  1. 即使您使用较小的(1 字节)数据类型。C 编译器将在任何表达式中将其提升为 int。

  2. 在 8 位控制器上,int 可能是 16 位。使用单字节类型只会节省一个字节的堆栈空间。然后编译器可能会再次将该变量放入寄存器中,因此无论如何都不会节省任何空间。

检查上述代码生成的汇编代码,然后决定是否需要(空间)优化。

于 2009-12-27T14:24:02.067 回答
2

您似乎想通过您使用的数据类型来传达从 0 计数到 255 的信息,但是 255 的意义是什么?您可能应该使用明确说明其用途的名称来#define 这个幻数。此外,声明上方的注释比尝试将所有信息“编码”成看起来有些奇怪的声明更有帮助。

例如:

#define MAX_RETRIES   255
unsigned int retries;

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

如果需要,请添加注释,为什么重试次数限制为 255。

于 2009-11-10T13:28:21.533 回答
0

不,在普通的旧 C 中没有明确的方法可以做到这一点,因为在增量之后检查条件,如果比较 <= 255,您将永远循环,因为 8 位值不能超过 255 并终止。

于是就变成了。

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

除非您认为对 0 进行检查(查看环绕)在您的书中很清楚。我的不清楚。

请注意,8 位类型在许多编译器上效率非常低,有时会产生不必要的符号扩展。您可能想改用 uint_least8_t 类型,它可能会扩展到系统的字长并运行得更快。

于 2009-11-10T22:06:23.223 回答
0

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

对我来说似乎很清楚。

即使您尝试使用 1 字节计数器,您的编译器也很可能将其转换为:

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

例如,GCC 可以,因为它更快。

$猫>test.c
无效的富(字符);
无效酒吧(无效){
    字符我;
    对于 (i = 0; i <= 255; i++)
        富(一);
}
^D
$ cc -m32 -c -O3 test.c
$ objdump -d test.o

test.o:文件格式elf32-i386


部分.text的反汇编:

00000000 <条>:
   0: 55 推 %ebp
   1: 89 e5 移动 %esp,%ebp
   3: 53 推送 %ebx
   4: 31 分贝异或 %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 添加 $0x1,%ebx
  16: e8 fc ff ff ff 调用 17 <bar+0x17>
  1b: eb f3 jmp 10 <bar+0x10>
$ cc -m64 -c -O3 test.c
$ objdump -d test.o

test.o:文件格式elf64-x86-64


部分.text的反汇编:

0000000000000000 <条>:
   0: 53 推送 %rbx
   1: 31 分贝异或 %ebx,%ebx
   3: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
   8: 89 df mov %ebx,%edi
   a: 83 c3 01 添加 $0x1,%ebx
   d: e8 00 00 00 00 callq 12 <bar+0x12>
  12: eb f4 jmp 8 <bar+0x8>
于 2009-11-10T15:40:01.370 回答
0

分成两半?

uint8_t k = 0;
while (k & 0x80 == 0) {
  work();
  k++;
}
while (k & 0x80 == 1) {
  work();
  k++;
}
于 2009-11-10T13:10:51.983 回答
0

你花这么多精力来保存一个字节?你的最后一个例子会起作用,或者你可以做第一个和第三个的某种组合:

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

不过,问问自己代码中增加的丑陋是否值得保存那个字节。如果您确实采用了这样的解决方案,您可能应该记录为什么您没有以明显的方式这样做。

于 2009-11-10T13:16:45.560 回答
0

好吧,如果您想让不太清晰的代码清晰,您可能总是添加注释;)

一种解决方案是将循环体放在一个函数中(如果不考虑公共鞭打,则将其放置在宏中),然后:

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

或者如果您不想扩展适用范围i和 C++ 范围规则:

for( uint8_t i = 0; i < 255; i++ )
{
    body(i) ;
}
body(255) ;
于 2009-11-10T16:27:10.020 回答