Каковы исторические причины Языки C имеют предварительные приращения и пост-приращения?
(Примечание: я не спрашиваю об определениях pre-increment vs. post-increment или о том, как они используются в C/С++. Поэтому я не думаю, что это дублированный вопрос.)
Разработчики C (Dennis Ritchie и др.) создали приращения и сокращения операторов по очень веским причинам. Я не понимаю, почему они решили создать различие пре-инкрементов/декрементов?
Я считаю, что эти операторы были гораздо полезнее, когда C разрабатывался, чем сегодня. Большинство программистов на C/С++ используют одно или другое, а программисты из других языков находят различие сегодня странным и запутанным (NB: это основано исключительно на анекдотических доказательствах).
Почему они решили это сделать, и что изменилось при вычислении, что это различие сегодня не так полезно?
Для записи разница между двумя может быть видна в коде С++:
int x = 3;
cout << "x = 3; x++ == " << x++ << endl;
cout << "++x == " << ++x << endl;
cout << "x-- == " << x-- << endl;
cout << "--x == " << --x << endl;
даст в качестве выходного
x++ == 3
++x == 5
x-- == 5
--x == 3
Ответы
Ответ 1
Инкремент и декремент на 1 были широко поддерживаются в аппаратных средствах в то время: один код операции и быстрый. Это потому, что "увеличение на 1" и "уменьшение на 1" были очень распространенной операцией в коде (истинно по сей день).
Формы post и preecrement влияют только на место, где этот код операции был вставлен в сгенерированный машинный код. Концептуально это подражает "увеличению/уменьшению до или после использования результата". В одном утверждении
i++;
понятие "до/после" не используется (и поэтому оно делает то же самое, что и ++i;
), но в
printf ("%d", ++i);
это. Это различие так же важно в наши дни, как и когда был разработан язык C (эта конкретная идиома была скопирована из ее предшественника с именем "B" ).
От Разработка языка C
Эта функция [PDP-7 "` `автоматически увеличивать ячейки памяти '], вероятно, предложила такие операторы Томпсону [Кен Томпсон, который проектировал" B ", предшественник C]; обобщение, чтобы сделать их как префикс, так и постфикс, было его собственным. Действительно, ячейки автоматического инкремента не использовались непосредственно в реализации операторов, и более сильной мотивацией для нововведения было, вероятно, его наблюдение, что перевод ++ x был меньше, чем у x = x + 1.
Благодаря @dyp для упоминания этого документа.
Ответ 2
При обратном отсчете от n
очень важно, является ли предредукция или пост-декремент
#include <stdio.h>
void foopre(int n) {
printf("pre");
while (--n) printf(" %d", n);
puts("");
}
void foopost(int n) {
printf("post");
while (n--) printf(" %d", n);
puts("");
}
int main(void) {
foopre(5);
foopost(5);
return 0;
}
Смотрите код, запущенный на ideone.
Ответ 3
Чтобы получить ответ, который выходит за рамки спекуляций, скорее всего, вы должны спросить Денниса Ричи и других лично.
Добавив к уже указанному ответу, я хотел бы добавить две возможные причины:
-
ленивость/сохранение пространства:
вы могли бы сохранить несколько нажатий клавиш/байтов во входном файле, используя соответствующую версию в конструкциях типа while(--i)
vs. while(i--)
. (см. ответ PMg, чтобы увидеть, почему оба имеют значение, если вы не видели его в первом прогоне)
-
Эстетика
По соображениям симметрии, имеющим только одну версию, пре-или постинкремент/декремент может чувствовать что-то вроде чего-то.
EDIT: добавлено сохранение нескольких байтов во входном файле в разделе спекуляции, предоставляя также довольно приятную "историческую" причину.
В любом случае главное в составлении списка - это примеры возможных объяснений, которые не слишком историчны, но все еще сохраняются сегодня.
Конечно, я не уверен, но я думаю, что просить "историческую" причину, отличную от личного вкуса, исходит из предположения, которое не обязательно верно.
Ответ 4
Для C
Посмотрите на оригинальное обоснование Кернигана и Ричи (оригинальные K & R стр. 42 и 43):
Необычные аспекты: ++ и - могут использоваться либо как префикс, либо как как постфикс. (...) В контексте, где не требуется никакого значения (..) префикс или постфикс по вкусу. Но htere - ситуации, когда тот или иной специально вызван.
Текст продолжается с некоторыми примерами, которые используют приращения в индексе, с явной целью записать код более компактный. Поэтому причиной этих операторов является удобство более компактного кода.
Три приведенных примера (squeeze()
, getline()
и strcat()
) используют только постфикс в выражениях с использованием индексации. Авторы сравнивают код с более длинной версией, которая не использует встроенные приращения. Это подтверждает, что основное внимание уделяется компактности.
K & R highlight на стр. 102, использование этих операторов в сочетании с разыменованием указателя (например, *--p
и *p--
). Дальнейшего примера не приводится, но опять же они ясно показывают, что преимущество - компактность.
Для С++
Bjarne Stroustrup хотел иметь совместимость C, поэтому С++ унаследовал префикс и постфиксный прирост и декремент.
Но там больше: в своей книге "Дизайн и эволюция С++" Строуструп объясняет, что изначально он планировал иметь только одну перегрузку как для постфикса, так и для префикса в пользовательских классах:
Несколько человек, особенно Брайан Керниган, отметили, что это ограничение было неестественным с точки зрения C и предотвратило пользователей от определения класса, который может быть использован в качестве замены для обычный указатель.
Это заставило его найти текущую разницу подписи, чтобы дифференцировать префикс и постфикс.
Кстати, без этих операторов С++ не будет С++, но C_plus_1;-)
Ответ 5
Рассмотрим следующий цикл:
for(uint i=5; i-- > 0;)
{
//do something with i,
// e.g. call a function that _requires_ an unsigned parameter.
}
Вы не можете реплицировать этот цикл с помощью операции предварительного декремента, не перемещая операцию декремента вне конструкции for (...), и просто лучше иметь вашу инициализацию, взаимодействие и проверку всего в одном месте.
Гораздо большая проблема заключается в следующем: можно переопределить операторы инкремента (все 4) для класса. Но тогда операторы критически разные: операторы сообщения обычно приводят к временной копии экземпляра класса, который, как и предоператоры, этого не делают. Это огромная разница в семантике.
Ответ 6
PDP-11 имел одну инструкцию, которая соответствовала *p++
, а другая для *--p
(или, возможно, наоборот).