Подтвердить что ты не робот

Разница между uint8_t, uint_fast8_t и uint_least8_t

В стандарте C99 представлены следующие типы данных. Документацию можно найти здесь для библиотеки stdint AVR.

  • uint8_t означает 8-разрядный неподписанный тип.
  • uint_fast8_t означает, что это самый быстрый беззнаковый int с не менее чем 8 биты.
  • uint_least8_t означает, что он имеет unsigned int с по меньшей мере 8 битами.

Я понимаю uint8_t и что такое uint_fast8_t (я не знаю, как он реализован на уровне регистра).

1. Можете ли вы объяснить, что означает "это a unsigned int с по меньшей мере 8 бит"?

2. Как uint_fast8_t и uint_least8_t помогают повысить эффективность/кодовое пространство по сравнению с uint8_t?

4b9b3361

Ответ 1

uint_least8_t - наименьший тип, который имеет не менее 8 бит. uint_fast8_t - самый быстрый тип, который имеет не менее 8 бит.

Вы можете видеть различия, представляя экзотические архитектуры. Представьте себе 20-битную архитектуру. Его unsigned int имеет 20 бит (один регистр), а его unsigned char имеет 10 бит. Поэтому sizeof(int) == 2, но с использованием типов char требуются дополнительные инструкции для сокращения регистров пополам. Тогда:

  • uint8_t: undefined (без 8-битного типа).
  • uint_least8_t: unsigned char, наименьший тип, который содержит не менее 8 бит.
  • uint_fast8_t: unsigned int, потому что в моей воображаемой архитектуре переменная с половинным регистром медленнее, чем полная запись.

Ответ 2

Теория выглядит примерно так:

uint8_t требуется ровно 8 бит, но он не должен существовать. Поэтому вы должны использовать его там, где вы полагаетесь на арифметическое поведение по модулю 256 для 8-битного целого и где вы предпочли бы скомпилировать отказ от неправильного поведения на неясных архитектурах.

uint_least8_t должен быть наименьшим доступным беззнаковым целым типом, который может хранить не менее 8 бит. Вы бы использовали его, когда хотите минимизировать использование памяти такими вещами, как большие массивы.

uint_fast8_t должен быть "самым быстрым" неподписанным типом, который может хранить не менее 8 бит; однако на самом деле он не гарантированно будет самым быстрым для любой заданной операции на любом данном процессоре. Вы использовали бы его в обработке кода, который выполняет много операций над значением.

Практика заключается в том, что "быстрый" и "наименее" типы не используются много.

"Наименее" типы действительно полезны, если вы заботитесь о переносимости, чтобы замаскировать архитектуры с помощью CHAR_BIT!= 8, которые большинство людей этого не делают.

Проблема с "быстрыми" типами заключается в том, что "самый быстрый" трудно скопировать. Меньший тип может означать меньшую нагрузку на систему памяти/кэша, но с использованием типа, который меньше, чем native, может потребоваться дополнительные инструкции. Кроме того, что лучше всего может измениться между версиями архитектуры, но разработчики часто хотят избежать нарушения ABI в таких случаях.

От взгляда на некоторые популярные реализации кажется, что определения uint_fastn_t довольно произвольны. glibc, по-видимому, определяет их как по крайней мере "размер родного слова" рассматриваемой системы, не принимая во внимание тот факт, что многие современные процессоры (особенно 64-битные) имеют определенную поддержку для быстрых операций над элементами, меньшими, чем их родное слово размер. IOS, по-видимому, определяет их как эквивалентные типам фиксированного размера. Другие платформы могут отличаться.

В целом, если ваша производительность жесткого кода с маленькими целыми числами - ваша цель, вы должны быть на месте на платформах, которые вам нужны, с разными типами, чтобы увидеть, что лучше всего работает.

Ответ 3

uint8_t означает: дать мне unsigned int ровно 8 бит.

uint_least8_t означает: дайте мне наименьший тип unsigned int, который имеет не менее 8 бит. Оптимизируйте потребление памяти.

uint_fast8_t означает: дать мне unsigned int не менее 8 бит. Выберите более крупный тип, если он ускорит мою программу из-за соображений выравнивания. Оптимизируйте скорость.

Кроме того, в отличие от простых типов int, подписанная версия указанных выше типов stdint.h гарантированно является 2-мя дополнительными форматами.

Ответ 4

1. Можете ли вы объяснить, что означает "это неподписанный int с не менее 8 бит"?

Это должно быть очевидно. Это означает, что это целочисленный тип без знака и ширина его не менее 8 бит. Фактически это означает, что он может по крайней мере удерживать числа от 0 до 255, и он может определенно не содержать отрицательные числа, но он может удерживать числа выше 255.

Очевидно, что вы не должны использовать какой-либо из этих типов, если вы планируете хранить любое число вне диапазона от 0 до 255 (и вы хотите, чтобы оно было переносимым).

2.How uint_fast8_t и uint_least8_t помогают повысить эффективность/кодовое пространство по сравнению с uint8_t?

uint_fast8_t требуется быстрее, поэтому вы должны использовать это, если требуется, чтобы код был быстрым. uint_least8_t, с другой стороны, требуется, чтобы не было кандидата меньшего размера, поэтому вы должны использовать это, если размер является проблемой.


И, конечно, вы используете только uint8_t, когда вы абсолютно требуете, чтобы это было ровно 8 бит. Использование uint8_t может сделать код не переносимым, поскольку uint8_t не требуется, чтобы существовать (потому что такой маленький целочисленный тип не существует на некоторых платформах).

Ответ 5

"Быстрые" целые типы определяются как самое быстрое целое число, доступное, по крайней мере, с количеством бит (в вашем случае 8).

Платформа может определять uint_fast8_t как uint8_t, тогда не будет никакой разницы в скорости.

Причина в том, что есть платформы, которые медленнее, если не используют их родную длину слова.

Ответ 6

Некоторые процессоры не могут работать так же эффективно на небольших типах данных, как на больших. Например, данный:

uint32_t foo(uint32_t x, uint8_t y)
{
  x+=y;
  y+=2;
  x+=y;
  y+=4;
  x+=y;
  y+=6;
  x+=y;
  return x;
}

если y были uint32_t, компилятор для ARM Cortex-M3 мог просто генерировать

add r0,r0,r1,asl #2   ; x+=(y<<2)
add r0,r0,#12         ; x+=12
bx  lr                ; return x

но поскольку y есть uint8_t, компилятор должен будет генерировать:

add r0,r0,r1          ; x+=y
add r1,r1,#2          ; Compute y+2
and r1,r1,#255        ; y=(y+2) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#4          ; Compute y+4
and r1,r1,#255        ; y=(y+4) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#6          ; Compute y+6
and r1,r1,#255        ; y=(y+6) & 255
add r0,r0,r1          ; x+=y
bx  lr                ; return x

Предполагаемая цель "быстрых" типов заключалась в том, чтобы позволить компиляторам заменять более мелкие типы, которые не могли быть эффективно обработаны с более быстрыми. К сожалению, семантика "быстрых" типов довольно плохо указана, что, в свою очередь, оставляет мрачные вопросы о том, будут ли выражения оцениваться с использованием подписанной или неподписанной математики.