Круглые скобки
Скобками синтаксис C++ злоупотребляет; количество способов их использования приводит в замешательство: они применяются для заключения в них параметров в вызовах функций, в них заключается тип в преобразовании типа (приведении к типу), в именах типов для обозначения функций, а также для разрешения конфликтов приоритетов. К счастью, последнее требуется не слишком часто, потому что уровни приоритета и правила ассоциативности определены таким образом, чтобы выражения "работали ожидаемым образом" (то есть, отражали наиболее привычный способ употребления).
Например, значение
if (i<=0 || max
Порядок вычисления
Порядок вычисления подвыражений в выражении не определен.
Например
int i = 1; v[i] = i++;
может вычисляться или как v[1]=1, или как v[2]=1. При отсутствии ограничений на порядок вычисления выражения может генерироваться более хороший код. Было бы замечательно, если бы компилятор предупреждал о подобных неоднозначностях, но большинство компиляторов этого не делают.
Относительно операций
, && ||
гарантируется, что их левый операнд вычисляется раньше, чем правый. Например, b=(a=2,a=1) присвоит b 3. В этом пункте приводятся примеры использования && и ||. Заметьте, что операция последования , (запятая) логически отличается от запятой, которая используется для разделения параметров в вызове функции. Рассмотрим
f1(v[i],i++); // два параметра f2( (v[i],i++) ) // один параметр
В вызове f1 два параметра, v[i] и i++, и порядок вычисления выражений-параметров не определен. Зависимость выражения-параметра от порядка вычисления - это очень плохой стиль, а также непереносимо. В вызове f2 один параметр, выражение с запятой, которое эквивалентно i++.
С помощью скобок нельзя задать порядок вычисления. Например, a*(b/c) может вычисляться и как (a*b)/c, поскольку * и / имеют одинаковый приоритет. В тех случаях, когда важен порядок вычисления, можно вводить дополнительную переменную, например, (t=b/c,a*t).
Увеличение и уменьшение
Операция ++ используется для явного выражения приращения вместо его неявного выражения с помощью комбинации сложения и присваивания. По определению ++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);
1 2 3 4
8 8 8
| |