Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Прочее

Постфиксные инкремент и декремент

Операции «++» и «--» широко используются в языках с С-подобным синтаксисом. Перед обсуждением этих операций необходимо сразу же сделать оговорку. Здесь анализируется не принцип записи обозначения операции после операнда, а свойство таких операций выполнять действие над операндом по окончании всех остальных операций.
постфиксные инкремент и декремент


         В чём аномальность этих операций? В справочных руководствах по С/С++ унарные операции «++» и «--» как в префиксном, так и в постфиксном варианте имеют приоритет, равный 1 (всего приоритетов 16, от 0 до 15; 0 — наивысший приоритет). Для префиксных операций это справедливо безо всяких замечаний.

         Постфиксные «++» и «--» же при синтаксическом анализе так же имеют приоритет, равный 1. А при выполнении появляется «фокус»: они выполняются в последнюю очередь, после выполнения всего выражения. Получается, что эти операции помимо обычного существования имеют «параллельную реальность». В алголоподобных языках операции и функции могут иметь операнды в количестве от нуля и более. Но один операнд может принадлежать только одной операции или функции. Но в С/С++ это не так. «Отклонением от нормы» как раз являются «++» и «--». Возьмём пример:

	f(a++);
        Аргумент «а» сначала является операндом функции «f()», а затем , после её выполнения, операнд «а» является операндом операции «++». Операнд «а» один, а операций применяется к нему две! В примере
	f(++a);
такого не возникает: сначала выполняется «++», а затем результат этой операции является аргументом функции «f()».

         Можно было бы предположить, что операция «++» просто имеет самый низкий приоритет. И по этой причине она выполняется в последнюю очередь. Опровергаем это, используя скобки для повышения приоритета:
   a = (b++);
«a» будет присвоено значение «b», а не «b+1»!

         Какой вывод из всего этого можно сделать?
1) Свойство постфиксных операций «++» и «--» выполняться после выполнения всего выражения противоречит общепринятому, что операция или функция может иметь несколько операндов, но операнд относится только к одной операции или функции.
2) Эти противоречивые операции усложняют язык и компилятор соответственно; это повод исключить подобные операции из языка, тем более что теряем не многое.
3) Удаление этих операций из языка приведёт к небольшому удлинению программ. В примере на C вместо
do { *dst++ = *src++;} while (*src);
придётся писать:
do { *dst = * src; ++dst; ++src;) while (*src);
Это не очень дорогая расплата. Зато отсутствуют «фокусы» и «трюки».

        Есть ещё одна причина «не любить» эти операции. Рассмотрим переопределение операции «++» в C++:
BigInt& operator++(BigInt& op1, BigInt op2) { ... }; // Это постфиксный инкремент
BigInt& operator++(BigInt& op1)             { ... }; // А это префиксный инкремент
        Чем отличаются эти определения? Постфиксный инкремент имеет дополнительный операнд, единственное назначение которого — сигнализировать о том, что инкремент постфиксный, а не префиксный. Но средствами языка невозможно повторить такой трюк для, допустим, префиксного унарного минуса. И для любой другой операции. Это «зашито» в язык «шаманским» образом. Но в языке не должно быть «магии», всё должно быть логичным и подчиняться общим правилам! Меньше шаманства — меньше танцев с бубнами.

        И третья причина «не любить» постфиксные «++» и «--». Вы обратили внимание, что синтаксис этих операций при объявлении и употреблении разный? Постфиксные «++» и «--» — это унарные операции, но объявляются как бинарные. Опять нелогичность! Операции и функции в языке должны и объявляться, и употребляться одним и тем же способом! Но последнее замечание относится к реализации этих операций в C-подобных языках. Вновь разрабатываемые языки могут избежать подобного недостатка.

Последняя правка: 2014-12-23    16:37

ОценитеОценки посетителей
   ████████████████████████████████ 28 (75.6%)
   ██████ 5 (13.5%)
   ███ 2 (5.40%)
   ███ 2 (5.40%)

Отзывы

     2014/04/26 11:45, Utkin

Как сторонник Паскаля, вообще не вижу смысла в подобных операциях. Есть вполне себе работающее а=а+1. Для чего городить огород. Сам факт существования Паскаля и его использование для написания вполне рабочих программ говорит о том, что данные операции не обязательны вообще. Можно рассматривать как частный случай синтаксического сахара.

     2014/05/01 15:05, Автор сайта

Это краткая и удобная запись, позволяющая записывать имя объекта только единожды. Поэтому она и популярная, несмотря на свою «антинаучность». В Хаскеле нет таких операторов, но для императивных языков они удобны.

     2014/06/17 04:44, 87.241.204.6

Это краткая и удобная запись, позволяющая записывать имя объекта только единожды

И источник путаницы. На современном этапе основная проблема не в написании программы (при желании можно все очень сильно автоматизировать), а в её отладке, тестировании и дальнейшем сопровождении. Если говорить об одиночных разработках, то на данный факт можно закрыть глаза. Если говорить о промышленном программировании, то это источник бед. Я крайне негативно отношусь к смешиванию различных видов записи подобных операций. Есть удобное "человеческое" представление, большой практический опыт показал, что эксперименты на программистах с/с++ закончились неудачей. Программирование не должно коробить мыслительные процессы (итак алгоритм переводится на язык программирования, так еще в самом языке для понимания кода требуются дополнительные преобразования). Все эти переводы из одной системы представления в другую источник ошибок. Если же форма записи привлекательна, то должна быть единственной во всей программе и никак иначе.

     2014/06/17 16:16, Автор сайта

А какую запись Вы считаете человеческой? А := А + 1 — это лучше, чем ++ А? И с какой точки зрения запись «А := А + 1» лучше — с точки зрения школьника? Когда я был школьником, то вообще не представлял, что делает операция «&». Но вуз помог потом. Теперь понимаю, что делают «&» и «++». Они по своей сложности не очень отличаются.

     2014/06/19 10:02, utkin

Нет, я о том, что должна быть единая форма записи без смесей. И желательно как можно ближе к естественной форме записи. А:=А+1 совершенно не обязательно. Можно и А=А+1.

     2014/06/19 17:47, Автор сайта

В функциональном программировании вообще предпочитают не модифицировать значения имеющихся объектов, а создавать новые объекты на основании старых:
В = А + 1
Так легче раскрутить цепочку вычислений на предмет её доказательной правильности.

Лично для меня инкремент столь же естественен, как и А + 1, ещё со времён Clipper и Forth. Си был для меня третьим языком, где я увидел инкремент :)

     2014/06/23 09:25, utkin

Ну вот опять Ваши личные предпочтения. Вы же не один будете программировать на новом языке, или один? Ориентироваться на Вас (как и на любого другого) заведомо провальное дело. Ориентироваться нужно на большинство. А большинство вполне естественно воспринимает А=А+1, а вот насчет а++ будет путаница. Именно из-за сочетания обоих вариантов. Также как такой инкремент порождает ошибки в C/C++ где есть и ++а и а++ и а=а+1. В комбинации со скобками это создает вырвиглазные выражения. И если один выпендрился, то остальные путаются. Для промышленных масштабов это абсолютно не приемлемо. Код должен быть ясен большинству участников проекта, а не только его создателю.

     2014/06/23 13:48, Автор сайта

Большинство таки приемлет инкремент и декремент. Это личные предпочтения всех, кто программирует на Си-подобных языках: C, C++, C#, Objective C, D, Java, Cyclone и т.д. Если заглянуть в рейтинг Tiobe, то суммарный рейтинг этих языков — 2/3, т.е. «конституционное большинство». Каков процент тех, кому нравится — трудно сказать, но они его приемлют — это точно. Иначе бы они не пользовались этими языками. Вам, как поклоннику Паскаля, это не нравится. Но что поделаешь, когда речь идёт о большинстве.

     2014/06/30 12:23, utkin

Это личные предпочтения всех, кто программирует на Си-подобных языках: C, C++, C#, Objective C, D, Java, Cyclone и т.д.

Я не согласен с Вами, так как следует разделять программирование «для себя» и промышленное программирование. В последнем случае язык навязывает работодатель — не попрётесь же с Паскалем там где пишут на C/C++. Поэтому тут тонкости насчет «личных предпочтений».

     2014/06/30 17:28, Автор сайта

Похоже, Вам трудно представить, что инкремент и декремент могут употреблять добровольно. Был такой «всесоюзный староста» Михаил Иванович Калинин, который пить не любил, но ему приходилось это делать, чтобы не быть белой вороной в когорте сталиных, молотовых, кагановичей. Он недоумевал по поводу водки: «Как её могут беспартийные пить?». В его голове не укладывалось, что водку можно пить в удовольствие, а не по долгу службы.
Между прочим, в C++ ничто не препятствует отказу от инкремента и декремента. Возьмём такой код:
int  array[10];
int* ptr = array;
++ ptr;
*ptr = 99; // array[1] теперь равен 99
ptr = ptr + 1; // это по-паскалевски!
*ptr = 98; // array[2] теперь равен 98
Т.е. программированию без инкремента нет препятствий. Но программистам они нравится, они предпочитают более короткий код. Поэтому пользуют его и в хвост, и в гриву.

Кстати, о большинстве. Где-то читал примерно такой совет: «Если хотите достичь своей цели, никогда не ориентируйтесь на большинство. Иначе бы мы до сих пор жили на земле, которая покоится на трёх китах, не открыли бы Америки и не летали бы в атмосфере и космосе — ведь это невозможно».

     2015/02/11 17:19, Автор комментария

Т.е. я так понял из комментариев, что никто не знает операцию inc в Паскале?

     2015/02/11 20:40, Автор сайта

Автор Паскаля Никлаус Вирт выступал против Си-шных операций инкремента/декремента.

     2015/09/03 14:49, Александр

Отличный урок How to по работе инкремента и декремента в C# — https://www.youtube.com/watch?v=FTuLlHBRZxs

     2016/04/29 04:29, Олег

Utkin:

Сам факт существования Паскаля и его использование для написания вполне рабочих программ говорит о том, что данные операции не обязательны вообще. Можно рассматривать как частный случай синтаксического сахара.

Ты серьезно?! Сам факт СУЩЕСТВОВАНИЯ Паскаля?! Что-то Вас занесло, уважаемый ))

     2016/04/29 11:30, Автор сайта

А ещё сам факт существования Brainfuck говорит о том, что в языке программирования можно обойтись без букв.

     2016/05/05 14:18, utkin

Ты серьезно?! Сам факт СУЩЕСТВОВАНИЯ Паскаля?! Что-то Вас занесло, уважаемый ))

Конечно. Он существует:
а) давно,
б) используется для обучения,
в) используется в реальных проектах,
г) имеет также и всяческие ответвления навроде языка Дельфи.

Все это говорит о том, что операции вида а++ НЕОБЯЗАТЕЛЬНЫ для использования. То есть синтаксический сахар. Никакого преимущества они не дают. Вопрос в эстетике — кому-то нравится или легче воспринимается, а для кого-то это противоестественная запись.

     2016/05/05 14:19, utkin

А ещё сам факт существования Brainfuck говорит о том, что в языке программирования можно обойтись без букв.

А он используется в реальной работе?

     2016/05/05 14:36, Автор сайта

Просто я хотел сказать, что сам факт существования в данном случае ничего не значит.

     2016/08/06 00:11, Александр

1. Постфиксный инкремент/декремент имеют приоритет 1, префиксный инкремент/декремент имеют приоритет 2.
2. Приоритетов 18.
3. Не каждая функция примет аргумент (a++), в связи с тем, что постфиксные инкремент/декремент порождают временную переменную не LVALUE. Таким образом, следующая функция вызовет ошибку при попытке передать ей a++
void f(int& val){/* */};
4. Попробуйте скопировать простую строку "тест\0" предложенным Вами кодом
do { *dst = * src; ++dst; ++src;) while (*src);
И последнее. Возможно, у постфиксных инкремента/декремента должны быть подобные свойства?

     2016/08/23 03:59, xDDGx

Почему бы тогда не оставить постфиксные ин/декремент, но дать им минимальный (разумный) приоритет? Никакой магии, а возможность написать
do { *dst++ = *src++;} while (*src);
останется.

Почему-то никто не вспомнил ещё про такую конструкцию: a+=1. Длиннее, чем ++a, но короче a=a+1, да и функциональнее первого (шаг может быть и 2, и 3, и нецелым, и выбор операций больше — даже сдвиги — а не только +/-).

Александр, если вы имеете в виду, что не копируется \0, то исходным do { *dst++ = *src++;} while (*src); достигается то же самое, их функционал эквивалентен. В конце концов, это всего лишь пример...

     2016/10/31 08:43, rst256

Теперь понимаю, что делают «&» и «++». Они по своей сложности не очень отличаются.

Может, вы знаете что они делают «&» и «++» в указанном ниже коде?
void iseq(int a, int b){ assert(a==b); }
void ismt(int a, int b){ assert(a+1==b); }

int main(int argc, char **argv){

{ int j=100, *jp; jp=&j; iseq(j++, j); assert(j==101); } //100, 100
{ int j=100, *jp; iseq(j++, j); jp=&j; assert(j==101); } //100, 100
{ int j=100, *jp; iseq(j++, j); assert(&j && j==101); } //100, 100
{ int j=100, *jp; ismt(j++, j); assert(j==101); } //100, 100

{ int j=100, *jp; jp=&j; printf("\n%d, %d\n", j++, j); assert(j==101); } //100, 100
{ int j=100, *jp; printf("%d, %d\n", j++, j); jp=&j; assert(j==101); } //100, 100
{ int j=100, *jp; printf("%d, %d\n", j++, j); assert(&j && j==101); } //100, 100
{ int j=100; printf("%d, %d\n", j++, j); assert(j==101); } //100, 101

{ int j=100, *jp; jp=&j; printf("\n%d, %d\n", ++j, j); assert(j==101); } //101, 100
{ int j=100, *jp; printf("%d, %d\n", ++j, j); jp=&j; assert(j==101); } //101, 100
{ int j=100; printf("%d, %d\n", ++j, j); assert(j==101); } //101, 101

return 0;
}

     2017/08/03 12:30, Алексей

Написано:

Аргумент «а» сначала является операндом функции «f()», а затем , после её выполнения, операнд «а» является операндом операции «++».

Проблема в словах «после её выполнения». Это следует исправить на:

Аргумент «а» копируется в операнд функции «f()», затем, до выполнения тела функции, над «а» производится «++», и затем выполняется тело функции «f()» с исходным операндом.

Иначе говоря, на момент исполнения кода «f()», над «а» уже произведен постфиксный инкремент, хотя операнд она получит не инкрементированный.

Именно таково поведение языка C++ по стандарту: C++ standard, Program execution 1.9.15:, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf

А также языка C. Это также проверяется поведением реальных компиляторов. Причём описанная ситуация становится сложнее. И, вообще говоря, поведение не определено в случае
f(a++, a++);
Здесь запятая «,» не является оператором запятой.

Написать отзыв

Написать автору можно на электронную почту mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Каким должен быть язык программирования?

Анализ и критика

Устарел ли текст как форма представления программы

Русский язык и программирование

Многоязыковое программирование

Синтаксис языков программирования

Синтаксический сахар

Некоторые «вкусности» Алгол-68

«Двухмерный» синтаксис Python

Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

Должна ли программа быть удобочитаемой?

Стиль языка программирования

Тексто-графическое представление программы

●  Разделители

●  Строки программы

●  Слева направо или справа налево?

Комментарии

●  Длинные комментарии

●  Короткие комментарии

●  Комментарии автоматической генерации документации

●  Нерабочий код

●  Помеченные комментарии

Нужны ли беззнаковые целые?

Шестнадцатиричные и двоичные константы

Условные операторы

Переключатель

Циклы

●  Продолжение цикла и выход из него

Некошерный «goto»

Изменение приоритетов операций

Операции присвоения и проверки на равенство. Возможно ли однаковое обозначение?

Так ли нужны операции «&&», «||» и «^^»?

Постфиксные инкремент и декремент

Почему в PHP для конкатенации строк используется «.»?

Указатели и ссылки в C++

Использование памяти

Почему динамическое распределение памяти — это плохо

Как обеспечить возврат функциями объектов переменной длины?

●  Типы переменного размера (dynamically sized types, DST) в языке Rust

●  Массивы переменной длины в C/C++

●  Размещение объектов в стеке, традиционный подход

●  Размещение объектов переменной длины с использованием множества стеков

●  Размещение объектов переменной длины с использованием двух стеков

●  Реализация двухстековой модели размещения данных

●  Двухстековая модель: тесты на скорость

●  Изменение длины объекта в стеке во время исполнения

●  Размещение объектов переменной длины с использованием одного стека

Можно ли забыть о «куче», если объекты переменной длины хранить в стеке

Безопасность и размещение объектов переменной длины в стеке

Массивы, структуры, типы, классы переменной длины

О хранении данных в стеке, вместо заключения

Описание языка

Компилятор

Отечественные разработки

Cтатьи на компьютерные темы

Компьютерный юмор

Прочее

Последние комментарии

2018/12/08 23:03 ••• Попов Михаил
✎ Программирование без программистов — это медицина без врачей

2018/12/07 08:57 ••• Автор сайта
✎ Почему обречён язык Форт

2018/12/07 08:36 ••• Автор сайта
✎ Нужны ли беззнаковые целые?

2018/12/03 13:51 ••• kt
✎ Экстракоды при синтезе программ

2018/11/30 17:56 ••• Freeman
✎ Изменение приоритетов операций

2018/11/30 17:20 ••• Автор сайта
✎ Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

2018/11/26 14:23 ••• Автор сайта
✎ Так ли нужны операции «&&», «||» и «^^»?

2018/11/18 15:21 ••• Freeman
✎ Устарел ли текст как форма представления программы

2018/11/17 03:28 ••• Comdiv
✎ Изменение длины объекта в стеке во время исполнения

2018/11/16 12:53 ••• Автор сайта
✎ Помеченные комментарии

2018/11/11 14:01 ••• Александр Коновалов aka Маздайщик
✎ Нерабочий код

2018/11/11 13:39 ••• Александр Коновалов aka Маздайщик
✎ О русском языке в программировании