Я використовую непідписані вставки, щоб зробити свій код і його наміри більш зрозумілими. Одне, що я хочу захистити від несподіваних неявних перетворень, виконуючи арифметику як з підписаними, так і без підписаними типами, - це використовувати непідписаний короткий (зазвичай 2 байти) для моїх непідписаних змінних. Це ефективно з кількох причин:
- Якщо ви робите арифметику зі своїми непідписаними короткими змінними та літералами (які є тип int) або змінними типу int, це гарантує, що неподписана змінна завжди буде переведена в int перед оцінкою виразу, оскільки int завжди має більш високий ранг, ніж короткий . Це дозволяє уникнути будь-якої несподіваної поведінки, яка виконує арифметику з підписаними та непідписаними типами, припускаючи, що результат вираження вписується в підписаний int, звичайно.
- У більшості випадків непідписані змінні, які ви використовуєте, не перевищуватимуть максимальне значення непідписаного 2-байтового короткого (65,535)
Загальний принцип полягає в тому, що тип ваших непідписаних змінних повинен мати нижчий ранг, ніж тип підписаних змінних, щоб забезпечити просування до підписаного типу. Тоді у вас не буде несподіваної поведінки переповнення. Очевидно, ви не можете цього забезпечити постійно, але (найчастіше) це зробити можливо.
Наприклад, нещодавно у мене було щось для циклу приблизно такого:
const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
if((i-2)%cuint == 0)
{
//Do something
}
}
Буквальне '2' має тип int. Якщо я був непідписаним int замість непідписаного короткого, то в підвиразі (i-2) 2 буде переведено на безпідписаний int (оскільки unsigned int має більший пріоритет, ніж підписаний int). Якщо i = 0, то підвираз дорівнює (0u-2u) = деяке масивне значення через переповнення. Ідентична ідея з i = 1. Однак, оскільки i є непідписаним коротким, він переходить до того ж типу, що і буквальний '2', який підписаний int, і все працює добре.
Для додаткової безпеки: в рідкісному випадку, коли архітектура, яку ви реалізуєте, викликає, що int буде 2 байти, це може призвести до того, що обидва операнди в арифметичному виразі будуть переведені на безпідписаний int у випадку, коли непідписана коротка змінна не підходить в підписаний 2-байтний int, останній з яких має максимальне значення 32 767 <65,535. (Див. Https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned для отримання більш детальної інформації). Щоб захиститись від цього, ви можете просто додати static_assert до вашої програми наступним чином:
static_assert(sizeof(int) == 4, "int must be 4 bytes");
і не буде компілюватися в архітектурах, де int - 2 байти.
for(unsigned int n = 10; n >= 0; n --)
(петлі нескінченно)