Первоначально переменная 1UL
имеет 1 в самом младшем бите и по крайней мере 31 нулевой бит. Она определена как unsigned long
, поскольку тип int
гарантированно имеет только 16 битов, а необходимо по крайней мере 27. Это выражение сдвигает 1 на 27 битовых позиций, вставляя в биты позади 0.
К этому значению и значению переменной quiz1
применяется оператор OR. Поскольку необходимо изменить значение самой переменной quiz1
, используем составной оператор присвоения (см. раздел 4.4):
quiz1 |= 1UL << 27; //
Оператор |=
выполняется аналогично оператору +=
.
quiz1 = quiz1 | 1UL << 27; //
Предположим, что учитель пересмотрел контрольные и обнаружил, что ученик 27 фактически списал работу. Теперь учитель должен сбросить бит 27 в 0. На сей раз необходимо целое число, бит 27 которого сброшен, а все остальные установлены в 1. Применение побитового AND к этому значению и значению переменной quiz1
позволяет сбросить только данный бит:
quiz1 &= ~(1UL << 27); //
Мы получаем значение со всеми установленными битами, кроме бита 27, инвертируя предыдущее значение. У него все биты были сброшены в 0, кроме бита 27, который был установлен в 1. Применение побитового NOT к этому значению сбросит бит 27, а все другие установит. Применение побитового AND к этому значению и значению переменной quiz1
оставит неизменными все биты, кроме бита 27.
И наконец, можно узнать, как дела у ученика 27:
bool status = quiz1 & (1UL << 27); //
Здесь оператор AND применяется к значению с установленным битом 27 и значением переменной quiz1
. Результат отличен от нуля (т.е. истинен), если бит 27 в значении переменной quiz1
установлен; в противном случае он нулевой.
Хотя многие программисты никогда не используют побитовые операторы непосредственно, почти все они использует их перегруженные версии в виде операторов ввода и вывода. Перегруженный оператор имеет тот же приоритет и порядок, что и его встроенная версия. Поэтому программисты должны иметь понятие о приоритете и порядке операторов сдвига, даже если они никогда не используют их встроенные версии.
Поскольку операторы сдвига имеют левосторонний порядок, выражение
cout << "hi" << " there" << endl;
выполняется так:
( (cout << "hi") << " there" ) << endl;
В этом операторе операнд "hi"
группируется с первым символом <<
. Его результат группируется со вторым, а его результат с третьим символом.
Приоритет операторов сдвига средний: ниже, чем у арифметических операторов, но выше, чем у операторов отношения, присвоения и условных операторов. Эти различия в уровнях приоритета свидетельствуют о том, что для правильной группировки операторов с более низким приоритетом следует использовать круглые скобки.
cout << 42 + 10; //
cout << (10 < 42); //
cout << 10 < 42; //
Последний оператор cout
интерпретируется так
(cout << 10) < 42;
Он гласит: "записать 10 в поток cout
, а затем сравнить результат (т.е. поток cout
) со значением 42
".
Упражнение 4.25. Каково значение выражения ~'q' << 6
на машине с 32-битовыми целыми числами и 8-битовыми символами, с учетом, что символ 'q'
имеет битовое представление 01110001
?
Упражнение 4.26. Что будет, если в приведенном выше примере оценки учеников использовать для переменной quiz1
тип unsigned int
?
Упражнение 4.27. Каков результат каждого из этих выражений?
unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2 (b) ul1 | ul2
(c) ul1 && ul2 (d) ul1 || ul2
4.9. Оператор sizeof
Оператор sizeof
возвращает размер в байтах результата выражения или указанного по имени типа. Оператор имеет правосторонний порядок. Результат оператора sizeof
— это константное выражение (см. раздел 2.4.4) типа size_t
(см. раздел 3.5.2). Оператор существует в двух формах.
sizeof(
sizeof
Во второй форме оператор sizeof
возвращает размер типа, возвращаемого выражением. Оператор sizeof
необычен тем, что он не выполняет свой операнд.
Sales_data data, *p;