Тот факт, что беззнаковый объект не может быть меньше нуля, влияет на способы написания циклов. Например, в упражнениях раздела 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
можно записать любым из трех следующих способов.