Бьерн Страуструп - Язык программирования С++. Главы 2-4
Страница 41. Инкремент и декремент


 

3.2.3  Инкремент и декремент

Операция ++ явно задает инкремент в отличие от неявного его задания
с помощью сложения и присваивания. По определению ++lvalue означает
lvalue+=1, что, в свою очередь  означает lvalue=lvalue+1 при условии,
что содержимое lvalue не вызывает побочных эффектов. Выражение,
обозначающее операнд инкремента, вычисляется только один раз. Аналогично
обозначается операция декремента (--). Операции ++ и -- могут
использоваться как префиксные и постфиксные операции.  Значением ++x
является новое (т. е. увеличенное на 1) значение x. Например, y=++x
эквивалентно y=(x+=1). Напротив, значение x++ равно прежнему значению x.
Например, y=x++ эквивалентно y=(t=x,x+=1,t), где t - переменная того
же типа, что и x.
    Напомним, что операции инкремента и декремента указателя
эквивалентны сложению 1 с указателем или вычитанию 1 из указателя, причем
вычисление происходит в элементах массива, на который настроен
указатель. Так, результатом  p++ будет указатель на следующий элемент.
Для указателя p типа T* следующее соотношение верно по определению:

          long(p+1) == long(p) + sizeof(T);

    Чаще всего операции инкремента и декремента используются для
изменения переменных в цикле. Например, копирование строки,
оканчивающейся нулевым символом, задается следующим образом:

         inline void cpy(char* p, const char* q)
         {
           while (*p++ = *q++) ;
         }

Язык С++ (подобно С) имеет как сторонников, так и противников именно
из-за такого сжатого, использующего сложные выражения стиля
программирования. Оператор

         while (*p++ = *q++) ;

вероятнее всего, покажется невразумительным для незнакомых с С.
Имеет смысл повнимательнее посмотреть на такие конструкции, поскольку
для C и C++ они не является редкостью.
    Сначала рассмотрим более традиционный способ копирования массива
символов:

         int length = strlen(q)
         for (int i = 0; i<=length; i++) p[i] = q[i];

Это неэффективное решение: строка оканчивается нулем; единственный
способ найти ее длину - это прочитать ее всю до нулевого символа;
в результате строка читается и для установления ее длины, и для
копирования, то есть дважды. Поэтому попробуем такой вариант:

         for (int i = 0; q[i] !=0 ; i++) p[i] = q[i];
         p[i] = 0;   // запись нулевого символа

Поскольку p и q - указатели, можно обойтись без переменной i,
используемой для индексации:

         while (*q !=0) {
               *p = *q;
               p++;      // указатель на следующий символ
               q++;      // указатель на следующий символ
         }
         *p = 0;         // запись нулевого символа

Поскольку операция постфиксного инкремента позволяет сначала использовать
значение, а затем уже увеличить его, можно переписать цикл так:

          while (*q != 0) {
                *p++ = *q++;
           }
           *p = 0;   // запись нулевого символа

Отметим, что результат выражения  *p++ = *q++ равен *q. Следовательно,
можно переписать наш пример и так:

           while ((*p++ = *q++) != 0)  { }

В этом варианте учитывается, что *q равно нулю только тогда, когда
*q  уже скопировано в *p, поэтому можно исключить завершающее
присваивание нулевого символа. Наконец, можно еще более сократить
запись этого примера, если учесть, что пустой блок не нужен, а
операция "!= 0" избыточна, т.к. результат условного выражения и так
всегда сравнивается с нулем.  В результате мы приходим к
первоначальному варианту, который вызывал недоумение:

           while (*p++ = *q++) ;

Неужели этот вариант труднее понять, чем приведенные выше? Только
неопытным программистам на С++ или С! Будет ли последний вариант
наиболее эффективным по затратам времени и  памяти?  Если  не  считать
первого варианта с функцией strlen(), то это неочевидно. Какой из
вариантов окажется эффективнее, определяется как спецификой системы
команд, так и возможностями транслятора. Наиболее эффективный алгоритм
копирования для вашей машины можно найти в стандартной функции копирования
строк из файла <string.h>:

        int strcpy(char*, const char*);

 
« Предыдущая статья   Следующая статья »