Почему у нас есть постфиксный прирост?


55

Отказ от ответственности : я прекрасно знаю семантику приращения префикса и постфикса. Поэтому, пожалуйста, не объясните мне, как они работают.

Читая вопросы о переполнении стека, я не могу не заметить, что программисты путаются с оператором приращения postfix снова и снова. Отсюда вытекает следующий вопрос: есть ли случай использования, когда приращение постфикса дает реальную выгоду с точки зрения качества кода?

Позвольте мне уточнить мой вопрос на примере. Вот краткая реализация strcpy:

while (*dst++ = *src++);

Но это не совсем самодокументируемый код в моей книге (и он вызывает два раздражающих предупреждения на здравомыслящих компиляторах). Так что не так со следующей альтернативой?

while (*dst = *src)
{
    ++src;
    ++dst;
}

Затем мы можем избавиться от запутанного назначения в условии и получить код без предупреждений:

while (*src != '\0')
{
    *dst = *src;
    ++src;
    ++dst;
}
*dst = '\0';

(Да, я знаю, srcи dstв этих альтернативных решениях будут разные конечные значения, но, поскольку strcpyсразу возвращается после цикла, в этом случае это не имеет значения.)

Кажется, цель постфиксного приращения - сделать код максимально кратким. Я просто не понимаю, к чему мы должны стремиться. Если это изначально было связано с производительностью, то все еще актуально сегодня?


7
Вариант с постфиксом является наиболее часто используемым, поэтому, если вы собираетесь возразить против него, лучше быть довольно сильным. И ваш первый пример немного соломенный человек, так как я подозреваю, что немногие на самом деле закодировали бы strcpyметод таким способом (по причинам, которые вы уже упомянули).
Роберт Харви

4
Почему либо вообще?
Джеймс МакНеллис

31
Если бы мы удалили все из языка, который мог запутать программистов, у нас не было бы очень много функций. Тот факт, что что-то бесполезно для вас, или только очень редко полезно, не означает, что его следует обрезать. Если это больше не актуально, не используйте его; конец.
Коди Грей

4
Да, но тогда ты никогда не сможешь это сделатьint c = 0; c++;
Biff MaGriff

4
@ Коди: Потому что некоторые вещи сбивают с толку и полезны. Он не смущен самим постинкрементом, он смущен его полезностью . У нас не должно быть вещей, которые бесполезны, сбивают с толку или нет.
GManNickG

Ответы:


23

Хотя это когда-то сказывалось на производительности, я думаю, что настоящая причина в том, чтобы четко выразить свои намерения. Реальный вопрос в том, while (*d++=*s++);ясно ли выражает намерение что-то или нет. ИМО, так и есть, и я считаю, что альтернативы, которые вы предлагаете, менее ясны - но это может (легко) быть результатом того, что потратили десятилетия, привыкнув к тому, как все делается. Научившись C от K & R (потому что было почти нет других книг по C в то время) , вероятно , тоже помогает.

В некоторой степени это правда, что краткость была оценена в гораздо большей степени в старом коде. Лично я думаю, что это было в значительной степени хорошо - понимание нескольких строк кода обычно тривиально; что трудно понять большие куски кода. Тесты и исследования неоднократно показали, что установка всего кода на экране одновременно является основным фактором в понимании кода. По мере расширения экранов это, похоже, остается верным, поэтому сохранение кода (разумно) кратким остается ценным.

Конечно, можно пойти за борт, но я не думаю, что это так. В частности, я думаю, что это становится слишком сложным, когда понимание одной строки кода становится чрезвычайно трудным или трудоемким, особенно когда понимание меньшего количества строк кода требует больше усилий, чем понимания большего количества строк. Это часто встречается в Лиспе и APL, но, кажется, (по крайней мере, мне) это не так.

Меня меньше беспокоят предупреждения компилятора - по моему опыту, многие компиляторы выдают совершенно нелепые предупреждения на довольно регулярной основе. Хотя я, конечно, думаю, что люди должны понимать свой код (и любые предупреждения, которые он может выдавать), приличный код, который может вызвать предупреждение в каком-то компиляторе, не обязательно ошибочен. По общему признанию, новички не всегда знают то, что они могут безопасно проигнорировать, но мы не останемся новичками навсегда, и нам также не нужно кодировать, как мы.


12
+1 за указание на то, что краткую версию легче читать некоторым из нас. :-)

1
Если вам не нравятся некоторые предупреждения вашего компилятора (а я могу обойтись без нескольких других - серьезно, я хотел напечатать if(x = y), клянусь!), Вам следует настроить параметры предупреждений вашего компилятора, чтобы случайно не пропустить предупреждения вы делаете подобное.

4
@ Брукс Моисей: я гораздо меньше в восторге от этого. Я не люблю загрязнять свой код, чтобы компенсировать недостатки компилятора.
Джерри Гроб

1
@Jerry, @Chris: эта аннотация предназначена для случая, когда вы думаете, что предупреждение обычно является хорошим , но вы не хотите, чтобы оно применялось к определенной строке, которая делает что-то необычное. Если вы никогда не хотите предупреждение и считаете его недостатком, используйте его -Wno-X, но если вы хотите сделать из него только одно исключение и хотите, чтобы предупреждение применялось повсюду, это гораздо более понятно, чем использование таинственных прыжков, таких как Крис ,

1
@Brooks: двойные круглые скобки и (void)xмолчание неиспользуемых предупреждений - это довольно хорошо известные идиомы, чтобы заглушить полезные предупреждения. Аннотация выглядит немного как pragmas, в лучшем случае, не переносимая: /
Матье М.

32

Это было, аппаратная вещь


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

Хотя C разрабатывался на различных ранних и слабых машинах, C и Unix впервые достигли сравнительно большого успеха с моделями PDP-11 с управлением памятью. В свое время это были относительно важные компьютеры, и Unix был намного лучше - экспоненциально лучше - чем другие 7 отвратительных операционных систем, доступных для -11.

И так случилось, что на PDP-11,

*--p

а также

*p++

... были реализованы на аппаратном уровне в качестве режимов адресации. (Кроме того *p, но никакой другой комбинации.) На этих ранних машинах, на частотах менее 0,001 ГГц сохранение одной или двух инструкций в цикле должно было почти подождать секунду, подождать минуту или выйти из строя. -обеденная разница Это конкретно не относится к постинкременту, но цикл с указателем постинкремента мог бы быть намного лучше, чем индексирование в то время.

Как следствие, шаблоны проектирования, потому что C идиомы, которые стали C психическими макросами.

Это что-то вроде объявления переменных сразу после {... не с тех пор, как C89 был текущим, это было требованием, но теперь это шаблон кода.

Обновление: очевидно, что основная причина *p++в языке заключается в том, что это именно то, что так часто хотят делать. Популярность шаблона кода была подкреплена популярным аппаратным обеспечением, которое соответствовало уже существующему шаблону языка, разработанного немного до появления PDP-11.

В наши дни это не имеет значения , какой шаблон вы используете, или если вы используете индексацию, и мы обычно программы на более высоком уровне , в любом случае, но оно должно иметь значения много на этих 0.001GHz машинах, и используя ничего, кроме *--xили *x++означало бы вас не «достали» PDP-11, и к вам могли бы подойти люди и сказать «знаете ли вы, что…» :-) :-)


25
Википедия говорит: «А (ложь) народный миф о том , что набор команд архитектура PDP-11 повлияла на идиоматическое использовании языка программирования C. В PDP-11 в увеличивающих и уменьшающих адресациях режимы соответствуют. −−iИ i++конструкциям в С. Если iи jобе были переменными регистра, такое выражение *(−−i) = *(j++)могло быть скомпилировано в одну машинную инструкцию. [...] Деннис Ритчи однозначно противоречит этому народному мифу. [Развитие языка Си ] "
fredoverflow

3
Ха (по ссылке, приведенной в комментарии FredOverflow): «Люди часто догадываются, что они были созданы для использования режимов адреса с автоматическим увеличением и автоматическим уменьшением, предоставляемых DEC PDP-11, в которых C и Unix впервые стали популярными. Это исторически сложилось так. невозможно, поскольку не было PDP-11 при разработке B. Однако PDP-7 действительно имел несколько ячеек памяти с «автоинкрементом» со свойством, что косвенная ссылка на память через них увеличивала ячейку. предложил такие операторы Томпсону; обобщение, делающее их префиксными и постфиксными, было его собственным. "

3
DMR только сказал, что PDP-11 не был причиной его добавления в C. Я тоже это сказал. То, что я сказал, было то, что это использовалось в качестве преимущества на PDP-11 и стало популярным шаблоном дизайна именно по этой причине. Если Википедия противоречит этому, то они просто неправы, но я так не читаю. Это плохо сформулировано на этой странице W.
DigitalRoss

3
@FredOverflow. Я думаю, вы неправильно поняли, что такое «народный миф». Миф состоит в том, что C был разработан, чтобы использовать эти 11 операций, а не то, что они не существуют, и что кодовый шаблон не стал популярным, потому что все знали, что они сопоставлены с 11 скважинами. На случай, если неясно: PDP-11 действительно реализует эти два режима аппаратно для почти каждой инструкции в качестве режима адресации, и это действительно был первый важный компьютер, на котором работал C.
DigitalRoss

2
«это должно было иметь большое значение на этих машинах с частотой 0,001 ГГц». Хм, я не хочу говорить это, потому что это датирует меня, но у меня были руководства Motorola и Intel для моих процессоров, чтобы посмотреть размер инструкций и сколько циклов они взяли. Поиск мельчайших кодов, добавленных к возможности добавить еще одну функцию в диспетчер очереди печати и сохранение пары циклов, означал, что последовательный порт мог поддерживать некоторый протокол, такой как этот недавно изобретенный MIDI. C избавился от некоторых проблем, особенно с улучшением компиляторов, но все же было важно знать, какой способ написания кода является наиболее эффективным.
Жестянщик

14

Префикс, постфикс --и ++операторы были введены на языке B (предшественник C) Кеном Томпсоном - и нет, они не были вдохновлены PDP-11, которого в то время не существовало.

Цитата из "Развитие языка Си" Деннис Ритчи :

Томпсон пошел дальше, изобретая ++и--операторы, которые увеличивают или уменьшают; их позиция префикса или постфикса определяет, происходит ли изменение до или после указания значения операнда. Они не были в самых ранних версиях B, но появились по пути. Люди часто предполагают, что они были созданы, чтобы использовать режимы адреса с автоматическим увеличением и автоматическим уменьшением, предоставляемые DEC PDP-11, в которых C и Unix впервые стали популярными. Это исторически невозможно, так как при разработке B не было PDP-11. PDP-7, однако, имел несколько ячеек памяти с «автоинкрементом» со свойством, что косвенная ссылка на память через них увеличивала ячейку. Эта особенность, вероятно, предложила Томпсону такие операторы; обобщение, чтобы сделать их префиксом и постфиксом, было его собственным. На самом деле,++xбыл меньше, чем у x=x+1.


8
Нет, я не связан с Кеном Томпсоном.
Кит Томпсон

Спасибо за эту очень интересную ссылку! Это подтверждает мою цитату из оригинального K & R, которая предложила цель компактности, а не технической оптимизации. Я не знал, однако, что эти операторы уже существовали в Б.
Кристоф

@ BenPen Насколько я знаю, нет политики закрытия вопросов, если на другом сайте SE есть дубликат , если оба вопроса соответствуют их сайтам.
Ордос

Интересно ... Программист, конечно, не такой уж и близкий вопрос.
BenPen

@BenPen: Принятый ответ на вопрос переполнения стека уже ссылается на тот же источник, что и я (хотя не так много).
Кит Томпсон

6

Очевидная причина существования оператора postincrement заключается в том, что вам не нужно писать выражения наподобие (++x,x-1)или (x+=1,x-1)повсеместно или бесполезно разделять тривиальные операторы с понятными побочными эффектами на несколько операторов. Вот некоторые примеры:

while (*s) x+=*s++-'0';

if (x) *s++='.';

Всякий раз, когда чтение или запись строки в прямом направлении, постинкремент, а не преинкремент, почти всегда является естественной операцией. Я почти никогда не сталкивался с реальным использованием преинкремента в реальном мире, и единственное основное применение, которое я нашел для прекремента, - это преобразование чисел в строки (потому что человеческие системы письма записывают числа в обратном направлении).

Изменить: На самом деле это не было бы так ужасно; вместо (++x,x-1)вас, конечно, можно использовать ++x-1или (x+=1)-1. Все x++еще более читабельно.


Вы должны быть осторожны с этими выражениями, так как вы изменяете и читаете переменную между двумя точками последовательности. Порядок, в котором эти выражения оцениваются, не гарантируется.

@ Снеговик: какой? Я не вижу таких вопросов в своем ответе.
R ..

если у вас есть что-то вроде (++x,x-1)(вызов функции, может быть?)

@ Снеговик: Это не вызов функции. Это оператор запятой C, представляющий собой точку последовательности, заключенную в скобки для управления приоритетом. Результат такой же, как и x++в большинстве случаев (одно исключение: xэто неподписанный тип с рангом ниже, intа начальное значение xявляется максимальным значением этого типа).
R ..

Ах, хитрый оператор запятой ... когда запятая не запятая. Скобки и редкость оператора запятой меня оттолкнули. Неважно!

4

PDP-11 предлагал операции пост-инкремента и пре-декремента в наборе команд. Теперь это не были инструкции. Это были модификаторы команд, которые позволяли вам указать, что вы хотите получить значение регистра до его увеличения или уменьшения.

Вот шаг копирования текста на машинном языке:

movw (r1)++,(r2)++

который переместил слово из одного места в другое, на которое указывают регистры, и регистры были бы готовы сделать это снова.

Поскольку C был построен на и для PDP-11, многие полезные концепции нашли свое отражение в C. C должен был стать полезной заменой языку ассемблера. Предварительное увеличение и последующее уменьшение были добавлены для симметрии.


Это блестящие модификаторы ... Это заставляет меня хотеть изучить сборку PDP-11. Кстати, у вас есть ссылка на "для симметрии?" Это имеет смысл, но это история, иногда смысл не был ключевым. LOL
BenPen

2
Также известен как режимы адресации . PDP-11 предоставил постинкрементный и предварительный декремент, которые прекрасно сочетались для стековых операций.
Эрик Эйдт

1
PDP-8 обеспечивал косвенное изменение памяти, но не выполняло соответствующую операцию предварительного уменьшения. С памятью на магнитном сердечнике чтение слова памяти уничтожило его (!), Поэтому процессор использовал функцию обратной записи, чтобы восстановить только что прочитанное слово. Применение приращения перед обратной записью произошло довольно естественно, и стало возможным более эффективное перемещение блоков.
Эрик Эйдт

3
Извините, PDP-11 не вдохновил этих операторов. Его еще не было, когда Кен Томпсон изобрел их. Смотри мой ответ .
Кит Томпсон

3

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

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

Это вроде как & и | оператор с более низким приоритетом, чем ==

Он был разработан с благими намерениями (не короткая замыкательная версия && и ||), но теперь он смущает программистов каждый день и, вероятно, никогда не изменится.

В любом случае, это всего лишь ответ, потому что я думаю, что нет хорошего ответа на ваш вопрос, но я, вероятно, ошибаюсь, если я буду более опытным программистом, чем я.

--РЕДАКТИРОВАТЬ--

Должен заметить, что я считаю, что оба они невероятно полезны, я просто указываю, что это не изменится, нравится это кому-то или нет.


Это даже отдаленно не верно. Ни в коем случае «краткость кода» не была более важной, чем ясность.
Коди Грей

Ну, я бы сказал, что это было гораздо более сфокусировано. Вы совершенно правы, хотя, конечно, это никогда не было более важным, чем ясность кода ... для большинства людей.

6
Что ж, определение «краткий» является «плавно изящным: отполированный» или «используя несколько слов: лишенный излишеств», оба из которых, как правило, полезны при написании кода. «Terse» не означает «неясный, трудно читаемый и запутанный», как думают многие.
Джеймс МакНеллис

@James: Хотя вы правы, я думаю, что большинство людей имеют в виду второе определение «краткий», когда оно используется в контексте программирования: «использование нескольких слов; лишенных излишеств». Согласно этому определению, легче представить себе ситуации, в которых существует компромисс между краткостью и выразительностью конкретного фрагмента кода. Очевидно, что при написании кода нужно делать то же самое, что и при разговоре: делать вещи такими короткими, какими они должны быть, но не короче. Оценка краткости над выразительностью может привести к запутанному виду кода, но это, конечно, не должно.
Коди Грей

1
перемотайте 40 лет назад и представьте, что вам нужно было разместить свою программу на том барабане, который будет содержать только около 1000 символов, или написать компилятор, который может работать только с 4 тысячами байтов оперативной памяти. Были времена, когда краткость против ясности даже не была проблемой. Вы должны были быть краткими, на этой планете не было компьютера, который мог бы хранить, запускать или компилировать вашу краткую программу.
Ноябрь

3

Мне нравится постфиксный оператор при работе с указателями (независимо от того, разыменую я их или нет), потому что

p++

читается более естественно, как «перейти к следующему месту», чем эквивалент

p += 1

что, безусловно, должно смутить начинающих, когда они впервые увидят, что он используется с указателем, где sizeof (* p)! = 1.

Вы уверены, что правильно заявляете о проблеме путаницы? Новичков не смущает тот факт, что оператор postfix ++ имеет более высокий приоритет, чем оператор разыменования *, так что

*p++

разбирает как

*(p++)

и нет

(*p)++

как некоторые могут ожидать?

(Вы думаете, что это плохо? См. Чтение объявлений C ).


2

Среди тонких элементов хорошего программирования - локализация и минимализм :

  • положить переменные в минимальном объеме использования
  • использование const, когда доступ для записи не требуется
  • и т.п.

В том же духе x++это можно рассматривать как способ локализации ссылки на текущее значение, при xэтом сразу указав, что вы - по крайней мере, на данный момент - закончили с этим значением и хотите перейти к следующему (допустимо, xявляется ли intили указатель или итератор). Приложив немного воображения, вы можете сравнить это с тем, чтобы позволить старому значению / позиции xвыйти из области видимости сразу после того, как оно больше не требуется, и перейти к новому значению. Точная точка, в которой возможен этот переход, выделяется постфиксом++ . Подразумевается, что это, вероятно, конечное использование «старого», xможет быть полезным для понимания алгоритма, помогая программисту сканировать окружающий код. Конечно, постфикс++ может заставить программиста искать использование нового значения, которое может или не может быть хорошим в зависимости от того, когда это новое значение действительно необходимо, так что это аспект "искусства" или "ремесла" программирования, чтобы определить, что более важно полезно в данных обстоятельствах.

В то время как многие новички могут быть сбиты с толку особенностью, стоит сравнить ее с долгосрочными преимуществами: новички не остаются новичками надолго.


2

Ух ты, много ответов не совсем по теме (если я могу быть настолько смелым), и, пожалуйста, прости меня, если я указываю на очевидное - особенно в свете твоего комментария, чтобы не указывать на семантику, но на очевидное (с точки зрения Страуструпа, я полагаю) еще не опубликовано! :)

Postfix x++создает временное выражение, которое передается «вверх» в выражении, а переменная xвпоследствии увеличивается.

Префикс ++xне создает временный объект, но увеличивает «x» и передает результат в выражение.

Значение - удобство для тех, кто знает оператора.

Например:

int x = 1;
foo(x++); // == foo(1)
// x == 2: true

x = 1;
foo(++x); // == foo(2)
// x == 2: true

Конечно, результаты этого примера могут быть получены из другого эквивалентного (и, возможно, менее косого) кода.

Так почему у нас есть постфиксный оператор? Я думаю, потому что это идиома, которая сохраняется, несмотря на путаницу, которую она, очевидно, создает. Это унаследованная конструкция, которая когда-то воспринималась как имеющая ценность, хотя я не уверен, что воспринимаемая ценность была столько же для производительности, сколько для удобства. Это удобство не было потеряно, но я думаю, что оценка удобочитаемости увеличилась, что привело к сомнению цели оператора.

Нужен ли нам постфиксный оператор? Скорее всего нет. Его результат сбивает с толку и создает барьер для понимания. Некоторые хорошие программисты, безусловно, сразу узнают, где найти его полезным, часто в тех местах, где он имеет своеобразную «PERL-esque» красоту. Стоимость этой красоты - читабельность.

Я согласен, что явный код имеет преимущества над краткостью, удобочитаемостью, понятностью и удобством обслуживания. Хорошие дизайнеры и менеджеры хотят этого.

Однако некоторые программисты признают определенную красоту, которая побуждает их создавать код с вещами, предназначенными исключительно для красивой простоты - такими как оператор postfix, - код, который они никогда бы не подумали - или не нашли интеллектуально стимулирующим. В этом есть что-то, что делает его более красивым, если не более желательным, несмотря на содержательность.

Другими словами, некоторые люди находят, while (*dst++ = *src++);что это просто более красивое решение, которое захватывает ваше дыхание своей простотой, точно так же, как если бы оно было кистью на холсте. То, что вы вынуждены понимать язык, ценить красоту, только добавляет к ее великолепию.


3
+1 "некоторые люди находят while (* dst ++ = * src ++); просто быть более красивым решением". Определенно. Люди, которые не понимают язык ассемблера, не понимают, насколько элегантным может быть C, но этот конкретный код является яркой звездой.
Жестянщик

«Нужен ли нам оператор постфикса? Скорее всего, нет». - Я не могу не думать о машинах Тьюринга здесь. Нужны ли нам когда-нибудь автоматические переменные, forоператоры, черт возьми, даже whilegotoконце концов, мы имеем )?

@Bernd: Ха! Представьте себе замыкания! Затворы! Скандальный! лол
Брайан М. Хант

Затворы! Как смешно. Просто дай мне ленту! Серьезно, я имел в виду, что я не думаю, что / нуждающаяся в / функция должна быть основным критерием принятия решения (если вы не хотите разрабатывать, скажем, шутки). IME Я использую (нет, нужно ) постфиксные операторы почти исключительно, и вместо этого нужен только префиксный оператор.

2

Давайте посмотрим на оригинальное обоснование Kernighan & Ritchie (оригинальные K & R стр. 42 и 43):

Необычный аспект заключается в том, что ++ и - могут использоваться в качестве префикса или постфикса. (...) В контексте, где значение не требуется (..), выберите префикс или постфикс по вкусу. Но есть ситуации, когда одно или другое специально требуется.

Текст продолжается некоторыми примерами, которые используют приращения в индексе, с явной целью написания « более компактного » кода. Таким образом, причина этих операторов заключается в удобстве более компактного кода.

Три приведенных примера ( squeeze(), getline()и strcat()) используют только постфикс в выражениях, использующих индексацию. Авторы сравнивают код с более длинной версией, которая не использует встроенные приращения. Это подтверждает, что основное внимание уделяется компактности.

На странице 102 выделено K & R, использование этих операторов в сочетании с разыменованием указателей (например, *--pи *p--). Дальнейшего примера не приводится, но опять же, они ясно дают понять, что выгода заключается в компактности.

Например, префикс очень часто используется при уменьшении индекса или указателя, начиная с конца.

 int i=0; 
 while (i<10) 
     doit (i++);  // calls function for 0 to 9  
                  // i is 10 at this stage
 while (--i >=0) 
     doit (i);    // calls function from 9 to 0 

1

Я буду не согласен с утверждением о том , что ++p, p++, каким - то образом трудно читать или неясными. Один означает «увеличить p и затем прочитать p», другой означает «прочитать p и затем увеличить p». В обоих случаях оператор в коде находится именно там, где он находится в объяснении кода, поэтому, если вы знаете, что ++означает, вы знаете, что означает результирующий код.

Вы можете создать обфусцированный код, используя любой синтаксис, но я не вижу здесь случая, который p++/ ++pявляется по своей сути запутанным.


1

это от POV электротехника:

Есть много процессоров, которые имеют встроенные операторы пост-инкремента и пре-декремента для поддержки стека «последний пришел-первым вышел» (LIFO).

это может быть как:

 float stack[4096];
 int stack_pointer = 0;

 ... 

 #define push_stack(arg) stack[stack_pointer++] = arg;
 #define pop_stack(arg) arg = stack[--stack_pointer];

 ...

я не знаю, но по этой причине я ожидал увидеть префиксные и постфиксные операторы приращения.


1

Чтобы подчеркнуть точку зрения Кристофа о компактности, я хочу показать вам немного кода из V6. Это часть оригинального компилятора C, http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/c/c00.c :

/*
 * Look up the identifier in symbuf in the symbol table.
 * If it hashes to the same spot as a keyword, try the keyword table
 * first.  An initial "." is ignored in the hash.
 * Return is a ptr to the symbol table entry.
 */
lookup()
{
    int ihash;
    register struct hshtab *rp;
    register char *sp, *np;

    ihash = 0;
    sp = symbuf;
    if (*sp=='.')
        sp++;
    while (sp<symbuf+ncps)
        ihash =+ *sp++;
    rp = &hshtab[ihash%hshsiz];
    if (rp->hflag&FKEYW)
        if (findkw())
            return(KEYW);
    while (*(np = rp->name)) {
        for (sp=symbuf; sp<symbuf+ncps;)
            if (*np++ != *sp++)
                goto no;
        csym = rp;
        return(NAME);
    no:
        if (++rp >= &hshtab[hshsiz])
            rp = hshtab;
    }
    if(++hshused >= hshsiz) {
        error("Symbol table overflow");
        exit(1);
    }
    rp->hclass = 0;
    rp->htype = 0;
    rp->hoffset = 0;
    rp->dimp = 0;
    rp->hflag =| xdflg;
    sp = symbuf;
    for (np=rp->name; sp<symbuf+ncps;)
        *np++ = *sp++;
    csym = rp;
    return(NAME);
}

Это код, который был распространен в самиздате как хорошее образование, и тем не менее мы бы никогда не написали его сегодня. Посмотрите , как много побочных эффектов , которые были упакованы в ifи whileусловие, и , наоборот, как оба forпетель не имеет приращение выражения , так как это сделано в теле цикла. Посмотрите, как блоки заключаются в фигурные скобки, только когда это абсолютно необходимо.

Частично это была, конечно, ручная микрооптимизация, подобная той, которую мы ожидаем, что компилятор сделает для нас сегодня, но я бы также выдвинул гипотезу о том, что, когда весь ваш интерфейс к компьютеру представляет собой один стеклянный tty 80x25, вам нужен код быть настолько плотным, насколько это возможно, чтобы вы могли видеть больше этого одновременно.

(Использование глобальных буферов для всего - это, вероятно, похмелье от перерезания зубов на ассемблере.)


Обращайтесь с осторожностью: «ihash = + * sp ++;» В какой-то момент C имел не только оператор + =, но и = +. Сегодня = = - это оператор присваивания, за которым следует унарный плюс, который ничего не делает, поэтому он эквивалентен «ihash = * sp; sp + = 1;»
gnasher729

@ gnasher729 Да, и позже это rp->hflag =| xdflgпроизойдет, что, я считаю, будет синтаксической ошибкой на современном компиляторе. «В какой-то момент» как раз и есть именно V6; переход к +=форме произошел в V7. (Компилятор V6 только принял =+, если я правильно читаю этот код - см.
Symbol

-1

Возможно, вам стоит подумать и о косметике.

while( i++ )

возможно, "выглядит лучше", чем

while( i+=1 )

По какой-то причине оператор постинкремента выглядит очень привлекательно. Он короткий, сладкий, и все увеличивается на 1. Сравните это с префиксом:

while( ++i )

"оглядывается назад" не так ли? Вы никогда не увидите знак «плюс» в математике, за исключением случаев, когда кто-то глупо указывает что-то положительное ( +0или что-то в этом роде). Мы привыкли видеть ОБЪЕКТ + ЧТО-ТО

Это как-то связано с оценкой? А, А +, А ++? Я могу сказать вам, что сначала я был довольно сыром, что люди operator++из Python удалили свой язык!


1
Ваши примеры не делают то же самое. i++возвращает исходное значение iи i+=1и ++iвозвращает исходное значение iплюс 1. Так как вы используете возвращаемые значения для потока управления, это имеет значение.
Дэвид Торнли

Да, ты прав. Но я смотрю только на удобочитаемость здесь.
Бобобобо

-2

Считая от 9 до 0 включительно:

for (unsigned int i=10; i-- > 0;)  {
    cout << i << " ";
}

Или эквивалентный цикл while.


1
Как насчет for (unsigned i = 9; i <= 9; --i)?
fredoverflow
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.