Шрифт:
С помощью скобок нельзя задать порядок вычисления. Например, a*(b/c) может вычисляться и как (a*b)/c, поскольку * и / имеют одинаковый приоритет. В тех случаях, когда важен прядок вычисления, можно вводить дополнительную переменную, например, (t=b/c,a*t).
3.2.3 Увеличение и уменьшение*
– * Следовало бы переводить как «инкремент» и «декремент», однако мы следовали терминологии, принятой в переводной литратуре по C, поскольку эти операции унаследованы от C. (прим. перев.)
Операция ++ используется для явного выражения приращения вместо его неявного выражения с помощью комбинации сложения и присваивания. По определению ++lvalue означает lvalue+=1, что в свою очередь означает lvalue=lvalue+1 при условии, что lvalue не вызывает никаких побочных эффектов. Выражение, обозначающее (денотирующее) объект, который должен быть увличен, вычисляется один раз (только). Аналогично, уменьшение выражается операцией –. Операции ++ и – могут применяться и как префиксные, и как постфиксные. Значением ++x является нвое (то есть увеличенное) значение x. Например, y=++x эквивлентно y=(x+=1). Значение x++, напротив, есть старое значение x. Например, y=x++ эквивалентно y=(t=x,x+=1,t), где t – перменная того же типа, что и x.
Операции приращения особенно полезны для увеличения и уменьшения переменных в циклах. Например, оканчивающуюся нлем строку можно копировать так:
inline void cpy(char* p, const char* q) (* while (*p++ = *q++) ; *)
Напомню, что увеличение и уменьшение арифметических указателей, так же как сложение и вычитание указателей, осуществляется в терминах элементов вектора, на которые указывает указатель p++ приводит к тому, что p указывает на следующий элемент. Для указателя p типа T* по определению выполняется следующее:
long(p+1) == long(p)+sizeof(T);
3.2.4 Побитовые логические операции
Побитовые логические операции
amp; ! ^ ~ »» ««
применяются к целым, то есть к объектам типа char, short, int, long и их unsigned аналогам, результаты тоже цлые.
Одно из стандартных применений побитовых логических опраций – реализация маленького множества (вектор битов). В этом случае каждый бит беззнакового целого представляет один член множества, а число членов ограничено числом битов. Бнарная операция amp; интерпретируется как пересечение, ! как объединение, а ^ как разность. Для наименования членов такого множества можно использовать перечисление. Вот маленький прмер, заимствованный из реализации (не пользовательского итерфейса) «stream.h»:
enum state_value (* _good=0, _eof=1, _fail=2, _bad=4 *); // хорошо, конец файла, ошибка, плохо
Определение _good не является необходимым. Я просто хтел, чтобы состояние, когда все в порядке, имело подходящее имя. Состояние потока можно установить заново следующим обрзом:
cout.state = _good;
Например, так можно проверить, не был ли испорчен поток или допущена операционная ошибка:
if (cout.state amp;(_bad!_fail)) // не good
Еще одни скобки необходимы, поскольку amp; имеет более всокий приоритет, чем !.
Функция, достигающая конца ввода, может сообщать об этом так:
cin.state != _eof;
Операция != используется потому, что поток уже может быть испорчен (то есть, state==_bad), поэтому
cin.state = _eof;
очистило бы этот признак. Различие двух потоков можно находить так:
state_value diff = cin.state^cout.state;
В случае типа stream_state (состояние потока) такая раность не очень нужна, но для других похожих типов она оказвается самой полезной. Например, при сравнении вектора бит, представляющего множество прерываний, которые обрабатываются, с другим, представляющим прерывания, ждущие обработки.
Следует заметить, что использование полей (#2.5.1) в действительности является сокращенной записью сдвига и маскрования для извлечения полей бит из слова. Это, конечно, моно сделать и с помощью побитовых логических операций, Например, извлечь средние 16 бит из 32-битового int можно следующим образом:
unsigned short middle(int a) (* return (a»»8) amp;0xffff; *)
Не путайте побитовые логические операции с логическими операциями:
amp; amp; !! !
Последние возвращают 0 или 1, и они главным образом ипользуются для записи проверки в операторах if, while или for (#3.3.1). Например, !0 (не ноль) есть значение 1, тогда как ~ 0 (дополнение нуля) есть набор битов все-единицы, который обычно является значением -1.
3.2.5 Преобразование типа
Бывает необходимо явно преобразовать значение одного тпа в значение другого. Явное преобразование типа дает значние одного типа для данного значения другого типа. Например:
float r = float(1);
перед присваиванием преобразует целое значение 1 к знчению с плавающей точкой 1.0. Результат преобразования типа не является lvalue, поэтому ему нельзя присваивать (если только тип не является ссылочным типом).
Есть два способа записи явного преобразования типа: трдиционная в C запись приведения к типу (double)a и функцинальная запись double(a). Функциональная запись не может прменяться для типов, которые не имеют простого имени. Например, чтобы преобразовать значение к указательному типу надо или использовать запись преобразования типа