← Назад к вопросам

Бесконечный цикл с unsigned char

1.0 Junior🔥 111 комментариев
#Структуры данных и алгоритмы#Язык C++

Условие

Проанализируйте следующий код. Что будет выведено на экран и почему?

#include <iostream>

int main() {
    unsigned char half_limit = 150;
    
    for (unsigned char i = 0; i < 2 * half_limit; ++i) {
        std::cout << static_cast<int>(i) << " ";
    }
    
    return 0;
}

Вопросы

  1. Сколько итераций выполнит цикл?
  2. Какое значение примет переменная 2 * half_limit?
  3. Как исправить код, чтобы он работал корректно?

Подсказки

  • unsigned char имеет диапазон от 0 до 255
  • Обратите внимание на тип результата выражения 2 * half_limit
  • Рассмотрите правила integer promotion в C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение: Бесконечный цикл с unsigned char

Анализ проблемы

Исходный код имеет критическую ошибку переполнения типа.

Вычисление выражения

unsigned char half_limit = 150;
for (unsigned char i = 0; i < 2 * half_limit; ++i)

Важно: 2 * half_limit вычисляется как выражение, а не как присваивание!

Шаг 1: Integer promotion

  • half_limit имеет тип unsigned char
  • В выражении 2 * half_limit срабатывает integer promotion
  • unsigned char повышается до int
  • Результат: 2 * 150 = 300 (тип int)

Шаг 2: Сравнение в условии цикла

  • Условие: i < 300
  • i имеет тип unsigned char (диапазон 0-255)
  • 300 имеет тип int
  • Сравнение выполняется с приведением типов

Результат: Цикл выполнит 256 итераций (i от 0 до 255):

  • i = 0, 1, 2, ..., 254, 255 (все значения в диапазоне unsigned char)

Пошаговое объяснение с примерами

i = 0:      0 < 300? ДА
i = 1:      1 < 300? ДА
...
i = 254:    254 < 300? ДА
i = 255:    255 < 300? ДА
i = 256:    256 % 256 = 0, но переполнение! → НОВЫЙ ЦИКЛ

При i = 255:

  • ++i выполняется
  • unsigned char переполняется: 255 + 1 = 256 % 256 = 0
  • i становится 0
  • Условие: 0 < 300? ДА → СНОВА НАЧИНАЕТСЯ ЦИКЛ!

РЕЗУЛЬТАТ: БЕСКОНЕЧНЫЙ ЦИКЛ

Вывод исходного кода

0 1 2 3 4 5 ... 253 254 255 0 1 2 3 ... 253 254 255 0 1 2 ...

Цикл никогда не завершится (Ctrl+C нужно нажать).

Почему это происходит?

Integer promotion в C++ — это правило, по которому типы меньше int автоматически повышаются:

unsigned char a = 255;
int b = 2 * a;          // ✓ a повышается до int
                        // int a = 255
                        // 2 * 255 = 510 (тип int)

unsigned char c = 2 * a;  // a повышается до int
                          // 510 приводится к unsigned char
                          // 510 % 256 = 254 → c = 254

Правильные способы исправления

Исправление 1: Использовать int для переменной цикла

int main() {
    unsigned char half_limit = 150;
    
    for (int i = 0; i < 2 * half_limit; ++i) {  // ✓ int вместо unsigned char
        std::cout << i << " ";
    }
    
    return 0;
}

Вывод:

0 1 2 3 ... 297 298 299

(300 чисел, как и планировалось)

Исправление 2: Явно привести верхний предел

int main() {
    unsigned char half_limit = 150;
    
    for (unsigned char i = 0; i < static_cast<unsigned char>(2 * half_limit); ++i) {
        std::cout << static_cast<int>(i) << " ";
    }
    
    return 0;
}

Вывод:

0 1 2 3 ... 42 43 44

(только до 2*150 % 256 = 44, так как 300 переполняется в unsigned char)

Исправление 3: Использовать переменную для верхнего предела (ЛУЧШЕ)

int main() {
    unsigned char half_limit = 150;
    int limit = 2 * half_limit;  // явное вычисление с нужным типом
    
    for (unsigned char i = 0; i < limit; ++i) {  // сравнение unsigned char < int
        std::cout << static_cast<int>(i) << " ";
    }
    
    return 0;
}

Вывод:

0 1 2 3 ... 253 254 255

(256 чисел, затем бесконечный цикл, потому что unsigned char переполнится)

Исправление 4: Правильное сравнение без переполнения

int main() {
    unsigned char half_limit = 150;
    int limit = 2 * half_limit;  // 300
    
    for (int i = 0; i < limit; ++i) {  // ✓ int для цикла
        std::cout << i << " ";
    }
    
    return 0;
}

Вывод:

0 1 2 3 ... 298 299

(все 300 чисел, цикл завершится нормально)

Сравнение всех вариантов

ИсправлениеКодРезультатПравильно?
Исходныйfor (unsigned char i = 0; i < 2*150; ++i)Бесконечный цикл
1for (int i = 0; i < 2*150; ++i)0-299
2for (unsigned char i = 0; i < (unsigned char)(2*150); ++i)0-255 → бесконечный
3int limit = 2*150; for (unsigned char i = 0; i < limit; ++i)0-255 → бесконечный
4int limit = 2*150; for (int i = 0; i < limit; ++i)0-299

Правила integer promotion (C++ standard)

Основные правила:

  1. Очень малые типы повышаются:

    • signed char → int
    • unsigned char → int
    • short → int
    • unsigned short → int (если int может вместить все значения)
  2. Примеры:

unsigned char a = 255;
int result = 2 * a;           // int (2 * 255 = 510)
unsigned char b = 2 * a;      // unsigned char (510 % 256 = 254)

short x = 30000;
int y = x + 1;                // int (30001)
  1. Арифметические операции:
unsigned char c1 = 100, c2 = 200;
int sum = c1 + c2;            // int (300), потом может привести к unsigned char
unsigned char csum = c1 + c2; // переполнение! 300 % 256 = 44

Ошибки, связанные с этим

// ❌ Ошибка 1: Бесконечный цикл
for (unsigned char i = 0; i < 256; ++i) {}  // i никогда не станет >= 256

// ❌ Ошибка 2: Неожиданное переполнение
unsigned char x = 250;
unsigned char y = x + 10;  // 260 % 256 = 4, а не 260!

// ❌ Ошибка 3: Потеря данных
unsigned char z = 30000;   // z = 30000 % 256 = 48

// ✅ Исправление: использовать правильные типы
for (int i = 0; i < 256; ++i) {}             // OK
int sum = x + 10;                            // 260 (правильно)
int big = 30000;                             // 30000 (правильно)

Лучшие практики

// ✅ ПРАВИЛЬНО: выбирай тип в зависимости от значений
for (int i = 0; i < 300; ++i) { }            // > 255 → используй int/long
for (unsigned char i = 0; i < 200; ++i) { }  // < 256 → unsigned char OK

// ✅ ПРАВИЛЬНО: явные приведения типов когда нужны
int large = 10000;
unsigned char small = static_cast<unsigned char>(large);  // явное переполнение

// ✅ ПРАВИЛЬНО: используй стандартные типы для циклов
for (size_t i = 0; i < container.size(); ++i) { }  // size_t для размеров
for (int i = 0; i < array_size; ++i) { }           // int для индексов

// ❌ ИЗБЕГАЙ: char/unsigned char для значений, только для символов
for (char i = 0; i < 100; ++i) { }                 // плохая идея
for (unsigned char byte_value = 0; byte_value < 256; ++byte_value) { }  // бесконечный