Главная chevron_right Переменные и константы chevron_right Типы данных chevron_right unsigned int

unsigned int

Тип unsigned int хранит только неотрицательные целые числа: на AVR-платах это 2 байта (0–65 535), на Arduino Due — 4 байта (0–4 294 967 295).

Полная статья

Описание

Тип unsigned int — это беззнаковое целое число. В отличие от обычного int, он не резервирует старший бит под знак, поэтому весь диапазон значений уходит в положительную сторону.

На платах UNO и других платах на базе ATMEGA unsigned int занимает 2 байта и хранит значения от 0 до 65 535 (2¹⁶ − 1).

На Arduino Due тип unsigned int занимает 4 байта и охватывает диапазон от 0 до 4 294 967 295 (2³² − 1).

В чём принципиальное отличие от int? В знаковом int старший (15-й) бит интерпретируется как знак числа: если он равен 1, число считается отрицательным, а остальные 15 бит кодируют его величину в Дополнительная математика до 2. В unsigned int такой интерпретации нет — все 16 бит кодируют само значение.

Синтаксис

unsigned int var = val;

Параметры

  • var: имя переменной
  • val: начальное значение, которое присваивается переменной

Пример кода

    unsigned int ledPin = 13;

Примечания и предупреждения

Переполнение (wrap-around)

Когда значение unsigned int выходит за пределы допустимого диапазона, оно «оборачивается»: превышение максимума возвращает переменную к нулю, а уход ниже нуля перебрасывает её к максимуму:

    unsigned int x;
    x = 0;
    x = x - 1;  // x теперь содержит 65535 — переполнение в отрицательную сторону
    x = x + 1;  // x теперь содержит 0 — переполнение

Это поведение предсказуемо и иногда используется намеренно — например, в счётчиках, которые должны циклически возвращаться к нулю.

Неожиданные результаты в арифметике

Даже если ваша unsigned int-переменная никогда не выходит за границы, смешанная арифметика может дать неожиданный результат. Микроконтроллер руководствуется двумя правилами:

  1. Тип целевой переменной определяет тип операции. Если результат записывается в знаковую переменную, вся операция выполняется как знаковая — даже если оба операнда беззнаковые.
  2. Тип промежуточного результата определяется типами операндов. Если оба операнда беззнаковые, промежуточный результат тоже будет беззнаковым — независимо от того, куда он в итоге запишется. Это источник классических ошибок: выражение вычисляется «не в том типе» ещё до присваивания.
    unsigned int x = 5;
    unsigned int y = 10;
    int result;

    result = x - y; // 5 - 10 = -5, как и ожидалось
    result = (x - y) / 2; // 5 - 10 в беззнаковой арифметике равно 65530! 65530/2 = 32765

    // решение: используйте знаковые переменные или выполняйте вычисление пошагово.
    result = x - y; // 5 - 10 = -5, как и ожидалось
    result = result / 2;  //  -5/2 = -2 (только целочисленная арифметика, дробная часть отбрасывается)

Когда использовать unsigned int?

  • Когда нужно поведение с переполнением — например, счётчики событий, которые должны обнуляться после максимума.
  • Когда диапазона знакового int (до 32 767) не хватает, но вы хотите избежать затрат памяти и скорости, которые несут типы long или float.
  • Когда вы работаете с данными, которые по природе своей неотрицательны: размеры, адреса, количества.

Практические замечания

  • Не смешивайте без необходимости знаковые и беззнаковые типы в одном выражении — компилятор может вести себя не так, как вы ожидаете, и предупреждения (-Wsign-compare) помогут это обнаружить.
  • Если вы вычитаете два значения типа unsigned int и результат теоретически может оказаться отрицательным, вы получите огромное положительное число вместо отрицательного — это частая причина ошибок в условиях.
  • Размер unsigned int зависит от платы: на AVR это 2 байта, на Due — 4. Если нужна переносимость, используйте типы с явным размером из заголовка <stdint.h>: uint16_t или uint32_t.
  • Функция millis() возвращает unsigned long — не unsigned int. Не путайте эти типы при работе со временем.
  • Для отладки выводите значения через Serial.println(): так вы сразу увидите, произошло ли переполнение.

Полезные материалы