Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cpp_Страуструп.doc
Скачиваний:
16
Добавлен:
03.05.2015
Размер:
3.2 Mб
Скачать

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*);