Тот факт, что беззнаковый объект не может быть меньше нуля, влияет на способы написания циклов. Например, в упражнениях раздела 1.4.1 (стр. 39) следовало написать цикл, который использовал оператор декремента для вывода чисел от 10 до 0. Написанный вами цикл, вероятно, выглядел примерно так:
for (int i = 10; i >= 0; --i)
std::cout << i << std::endl;
Казалось бы, этот цикл можно переписать, используя тип unsigned. В конце концов, мы не планируем выводить отрицательные числа. Однако это простое изменение типа приведет к тому, что цикл никогда не закончится:
//
//
for (unsigned u = 10; u >= 0; --u)
std::cout << u << std::endl;
Рассмотрим, что будет, когда u станет равно 0. На этой итерации отображается значение 0, а затем выполняется выражение цикла for. Это выражение, --u, вычитает 1 из u. Результат, -1, недопустим для беззнаковой переменной. Как и любое другое значение, не попадающее в диапазон допустимых, это будет преобразовано в беззнаковое значение. При 32-разрядном типе int результат выражения --u при u равном 0 составит 4294967295.
Исправить этот код можно, заменив цикл for циклом while, поскольку последний осуществляет декремент прежде (а не после) отображения значения:
unsigned u = 11; //
//
while (u > 0) {
--u; //
std::cout << u << std::endl;
}
Цикл начинается с декремента значения управляющей переменной цикла. В начале последней итерации переменная u будет иметь значение 1, а после декремента мы отобразим значение 0. При последующей проверке условия цикла while значением переменной u будет 0, и цикл завершится. Поскольку декремент осуществляется сначала, переменную u следует инициализировать значением на единицу больше первого подлежащего отображению значения. Следовательно, чтобы первым отображаемым значением было 10, переменную u инициализируем значением 11.
Выражения, в которых смешаны знаковые и беззнаковые типы, могут приводить к удивительным результатам, когда знаковое значение оказывается негативным. Важно не забывать, что знаковые значения автоматически преобразовываются в беззнаковые. Например, в таком выражении, как a * b, если а содержит значение -1, a b значение 1 и обе переменные имеют тип int, ожидается результат -1. Но если переменная а имеет тип int, а переменная b — тип unsigned, то значение этого выражения будет зависеть от количества битов, занимаемых типом int на данной машине. На нашей машине результатом этого выражения оказалось 4294967295.
Упражнение 2.3. Каков будет вывод следующего кода?
unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl;
std::cout << u - u2 << std::endl;
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl;
std::cout << i - i2 << std::endl;
std::cout << i - u << std::endl;
std::cout << u - i << std::endl;
Упражнение 2.4. Напишите программу для проверки правильности ответов. При неправильных ответах изучите этот раздел еще раз.
2.1.3. Литералы
Такое значение, как 42, в коде программы называется
Целочисленный литерал может быть в десятичной, восьмеричной или шестнадцатеричной форме. Целочисленные литералы, начинающиеся с нуля (0), интерпретируются как восьмеричные, а начинающиеся с 0x или 0X — как шестнадцатеричные. Например, значение 20 можно записать любым из трех следующих способов.