Упражнение 2.6. Напишите функцию setbits(x, p, n, y), возвращающую значение x, в котором n битов, начиная с p-й позиции, заменены на n правых разрядов из y (остальные биты не изменяются).
Упражнение 2.7. Напишите функцию invert(x, p, n), возвращающую значение x с инвертированными n битами, начиная с позиции p (остальные биты не изменяются).
Упражнение 2.8. Напишите функцию rightrot (x, n), которая циклически сдвигает x вправо на n разрядов.
2.10 Операторы и выражения присваивания
Выражение
i = i + 2;
в котором стоящая слева переменная повторяется и справа, можно написать в сжатом виде:
i += 2;
Оператор +=, как и =, называется оператором присваивания.
Большинству бинарных операторов (аналогичных + и имеющих левый и правый операнды) соответствуют операторы присваивания op=, где op - один из операторов
+
-
*
/
%
‹‹
››
&
^
|
Если выр 1 и выр 2 - выражения, то
выр 1op = выр 2
Эквивалентно
выр 1= (выр 1) op (выр 2)
с той лишь разницей, что выр 1вычисляется только один раз. Обратите внимание на скобки вокруг выр 2:
x *= y + 1
эквивалентно
x = x * (y + 1)
но не
x=x*y+1
В качестве примера приведем функцию bitcount, подсчитывающую число единичных битов в своем аргументе целочисленного типа.
/* bitcount: подсчет единиц в х */
int bitcount(unsigned х)
{
int b;
for (b = 0; х != 0; x ››= 1)
if (x & 01)
b++;
return b;
}
Независимо от машины, на которой будет работать эта программа, объявление аргумента x как unsigned гарантирует, что при правом сдвиге освобождающиеся биты будут заполняться нулями, а не знаковым битом.
Помимо краткости операторы присваивания обладают тем преимуществом, что они более соответствуют тому, как человек мыслит. Мы говорим "прибавить 2 к i" или "увеличить i на 2", а не "взять i, добавить 2 и затем вернуть результат в i", так что выражение i+=2 лучше, чем i=i+2. Кроме того, в сложных выражениях вроде
yyval[yypv[p3+p4] + yypv[p1+p2]]+= 2
благодаря оператору присваивания += запись становится более легкой для понимания, так как читателю при такой записи не потребуется старательно сравнивать два длинных выражения, совпадают ли они, или выяснять, почему они не совпадают. Следует иметь в виду и то, что подобные операторы присваивания могут помочь компилятору сгенерировать более эффективный код.
Мы уже видели, что присваивание вырабатывает значение и может применяться внутри выражения: вот самый расхожий пример:
while ((с = getchar()) != EOF)
В выражениях встречаются и другие операторы присваивания (+=, -= и т. д.), хотя и реже. Типом и значением любого выражения присваивания являются тип и значение его левого операнда после завершения присваивания.
Упражнение 2.9. Применительно к числам, в представлении которых использован дополнительный код, выражение x &= (x-1) уничтожает самую правую 1 в x. Объясните, почему. Используйте это наблюдение при написании более быстрого варианта функции bitcount.
Инструкции
if (a › b)
z = a;
else
z = b;
пересылают в z большее из двух значений a и b. Условное выражение, написанное с помощью тернарного (т. е. имеющего три операнда) оператора "?: ", представляет собой другой способ записи этой и подобных ей конструкций. В выражении
выр 1? выр 2: выр 3
первым вычисляется выражение выр 1. Если его значение не нуль (истина), то вычисляется выражение выр 2, и значение этого выражения становится значением всего условного выражения. В противном случае вычисляется выражение выр 3и его значение становится значением условного выражения. Следует отметить, что из выражений выр 2и выр 3вычисляется только одно из них. Таким образом, чтобы установить в z большее из a и b, можно написать
z = (a › b) ? a: b; /* z = max(a, b) */
Следует заметить, что условное выражение и в самом деле является выражением, и его можно использовать в любом месте, где допускается выражение. Если выр 2и выр 3принадлежат разным типам, то тип результата определяется правилами преобразования, о которых шла речь в этой главе ранее. Например, если f имеет тип float, а n - тип int, то типом выражения
(n › 0) ? f : n
будет floatвне зависимости от того, положительно значение n или нет.
Читать дальше