понятный код для счета от 0 до 255 с использованием 8-битного типа данных
-
19-09-2019 - |
Вопрос
Мне было интересно, есть ли чистый способ подсчета от 0 до 255 с использованием 8-битного типа данных, что-то вроде:
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-битного микроконтроллера с небольшим объемом памяти.
Решение
Как насчет:
uint8_t i = 0;
do {
...
} while (i++ != 255);
Другие советы
Я не уверен, что ты имеешь в виду, но
uint8_t i = 0;
do {
...
} while (++i & 255) ;
должен делать то, что вы просите, и имеет явную ссылку на 255 (бесполезно, если ваш компилятор соответствует стандарту C99, а uint8_t действительно 8-битный).
Что плохого в очевидном?
i = 255;
do {
work();
} while (i--);
Кажется, вы хотите передать сообщение о счете от 0 до 255 по типу данных, который вы используете, но какое значение имеет 255?Вероятно, вам следует #определить это магическое число, присвоив ему имя, явно указывающее его назначение.Кроме того, комментарий над утверждением был бы гораздо полезнее, чем попытка «закодировать» всю эту информацию в несколько странно выглядящих утверждениях.
Например:
#define MAX_RETRIES 255
unsigned int retries;
for(retries = 0; retries <= MAX_RETRIES; ++retries)
{
do_retry_work();
}
При необходимости добавьте комментарий, почему количество повторов ограничено 255.
Я бы предложил простое решение:
for (int i = 0; i < 256; ++i) {
...
}
вероятно, также будет наиболее эффективным решением.
Даже если вы используете меньший (1 байт) тип данных.Компилятор C преобразует его в int в любом выражении.
На 8-битном контроллере int, вероятно, будет 16-битным.Использование однобайтового типа позволит сэкономить только один байт стекового пространства.С другой стороны, компилятор может поместить эту переменную в регистр, так что никакой экономии места в любом случае не будет.
Проверьте ассемблерный код, сгенерированный приведенным выше кодом, затем решите, нужна ли ему оптимизация (пространства).
Считать пополам?
uint8_t k = 0;
while (k & 0x80 == 0) {
work();
k++;
}
while (k & 0x80 == 1) {
work();
k++;
}
Вы тратите столько усилий, чтобы сохранить один байт?Ваш последний пример будет работать, или вы можете сделать какую-то комбинацию первого и третьего:
for (uint8_t i = 0;;++i)
{
work();
if (i == 255)
break;
}
Тем не менее, спросите себя, стоит ли экономить этот один байт из-за добавленного уродства в коде.Если вы согласны с таким решением, вам, вероятно, следует задокументировать, почему вы не делаете это очевидным способом.
Что ж, если вы хотите прояснить неясный код, вы всегда можете добавить комментарий;)
Одним из решений является размещение тела цикла в функции (или макросе, если публичная порка не является проблемой), а затем:
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) ;
for (uint8_t i(0); (int)i <= 255; ++i)
Мне кажется совершенно ясно.
Даже если вы пытаетесь использовать 1-байтовый счетчик, ваш компилятор вполне может превратить его в такой:
for (int ii(0); ii <= 255; ++ii) {
uint8_t i(ii);
...
}
Например, GCC делает это, потому что он быстрее.
$ 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>
Нет, нет четкого способа сделать это в старом простом C, поскольку условие проверяется после приращения, и если вы сравниваете <= 255, вы будете выполнять цикл вечно, поскольку 8-битное значение не может превышать 255 и завершаться.
Так и делается.
uint8_t i = 0;
while (1)
{
/* your stuff */
if (255 == i++)
break;
}
Если только вы не думаете, что проверка на 0 (видя обход) очевидна в вашей книге.В моем это не понятно.
Обратите внимание, что 8-битные типы очень неэффективны для многих компиляторов, иногда создавая ненужные расширения знаков.Вместо этого вы можете использовать тип uint_least8_t, он, скорее всего, расширится до размера слова вашей системы и будет работать быстрее.