Отказ от ответственности : я прекрасно знаю семантику приращения префикса и постфикса. Поэтому, пожалуйста, не объясните мне, как они работают.
Читая вопросы о переполнении стека, я не могу не заметить, что программисты путаются с оператором приращения postfix снова и снова. Отсюда вытекает следующий вопрос: есть ли случай использования, когда приращение постфикса дает реальную выгоду с точки зрения качества кода?
Позвольте мне уточнить мой вопрос на примере. Вот краткая реализация strcpy
:
while (*dst++ = *src++);
Но это не совсем самодокументируемый код в моей книге (и он вызывает два раздражающих предупреждения на здравомыслящих компиляторах). Так что не так со следующей альтернативой?
while (*dst = *src)
{
++src;
++dst;
}
Затем мы можем избавиться от запутанного назначения в условии и получить код без предупреждений:
while (*src != '\0')
{
*dst = *src;
++src;
++dst;
}
*dst = '\0';
(Да, я знаю, src
и dst
в этих альтернативных решениях будут разные конечные значения, но, поскольку strcpy
сразу возвращается после цикла, в этом случае это не имеет значения.)
Кажется, цель постфиксного приращения - сделать код максимально кратким. Я просто не понимаю, к чему мы должны стремиться. Если это изначально было связано с производительностью, то все еще актуально сегодня?
strcpy
метод таким способом (по причинам, которые вы уже упомянули).int c = 0; c++;
Ответы:
Хотя это когда-то сказывалось на производительности, я думаю, что настоящая причина в том, чтобы четко выразить свои намерения. Реальный вопрос в том,
while (*d++=*s++);
ясно ли выражает намерение что-то или нет. ИМО, так и есть, и я считаю, что альтернативы, которые вы предлагаете, менее ясны - но это может (легко) быть результатом того, что потратили десятилетия, привыкнув к тому, как все делается. Научившись C от K & R (потому что было почти нет других книг по C в то время) , вероятно , тоже помогает.В некоторой степени это правда, что краткость была оценена в гораздо большей степени в старом коде. Лично я думаю, что это было в значительной степени хорошо - понимание нескольких строк кода обычно тривиально; что трудно понять большие куски кода. Тесты и исследования неоднократно показали, что установка всего кода на экране одновременно является основным фактором в понимании кода. По мере расширения экранов это, похоже, остается верным, поэтому сохранение кода (разумно) кратким остается ценным.
Конечно, можно пойти за борт, но я не думаю, что это так. В частности, я думаю, что это становится слишком сложным, когда понимание одной строки кода становится чрезвычайно трудным или трудоемким, особенно когда понимание меньшего количества строк кода требует больше усилий, чем понимания большего количества строк. Это часто встречается в Лиспе и APL, но, кажется, (по крайней мере, мне) это не так.
Меня меньше беспокоят предупреждения компилятора - по моему опыту, многие компиляторы выдают совершенно нелепые предупреждения на довольно регулярной основе. Хотя я, конечно, думаю, что люди должны понимать свой код (и любые предупреждения, которые он может выдавать), приличный код, который может вызвать предупреждение в каком-то компиляторе, не обязательно ошибочен. По общему признанию, новички не всегда знают то, что они могут безопасно проигнорировать, но мы не останемся новичками навсегда, и нам также не нужно кодировать, как мы.
источник
if(x = y)
, клянусь!), Вам следует настроить параметры предупреждений вашего компилятора, чтобы случайно не пропустить предупреждения вы делаете подобное.-Wno-X
, но если вы хотите сделать из него только одно исключение и хотите, чтобы предупреждение применялось повсюду, это гораздо более понятно, чем использование таинственных прыжков, таких как Крис ,(void)x
молчание неиспользуемых предупреждений - это довольно хорошо известные идиомы, чтобы заглушить полезные предупреждения. Аннотация выглядит немного какpragmas
, в лучшем случае, не переносимая: /Это было, аппаратная вещь
Интересно, что вы заметите. Постфиксный прирост, вероятно, существует по ряду совершенно веских причин, но, как и многие вещи в C, его популярность можно проследить в происхождении языка.
Хотя C разрабатывался на различных ранних и слабых машинах, C и Unix впервые достигли сравнительно большого успеха с моделями PDP-11 с управлением памятью. В свое время это были относительно важные компьютеры, и Unix был намного лучше - экспоненциально лучше - чем другие 7 отвратительных операционных систем, доступных для -11.
И так случилось, что на PDP-11,
а также
... были реализованы на аппаратном уровне в качестве режимов адресации. (Кроме того
*p
, но никакой другой комбинации.) На этих ранних машинах, на частотах менее 0,001 ГГц сохранение одной или двух инструкций в цикле должно было почти подождать секунду, подождать минуту или выйти из строя. -обеденная разница Это конкретно не относится к постинкременту, но цикл с указателем постинкремента мог бы быть намного лучше, чем индексирование в то время.Как следствие, шаблоны проектирования, потому что C идиомы, которые стали C психическими макросами.
Это что-то вроде объявления переменных сразу после
{
... не с тех пор, как C89 был текущим, это было требованием, но теперь это шаблон кода.Обновление: очевидно, что основная причина
*p++
в языке заключается в том, что это именно то, что так часто хотят делать. Популярность шаблона кода была подкреплена популярным аппаратным обеспечением, которое соответствовало уже существующему шаблону языка, разработанного немного до появления PDP-11.В наши дни это не имеет значения , какой шаблон вы используете, или если вы используете индексацию, и мы обычно программы на более высоком уровне , в любом случае, но оно должно иметь значения много на этих 0.001GHz машинах, и используя ничего, кроме
*--x
или*x++
означало бы вас не «достали» PDP-11, и к вам могли бы подойти люди и сказать «знаете ли вы, что…» :-) :-)источник
−−i
Иi++
конструкциям в С. Еслиi
иj
обе были переменными регистра, такое выражение*(−−i) = *(j++)
могло быть скомпилировано в одну машинную инструкцию. [...] Деннис Ритчи однозначно противоречит этому народному мифу. [Развитие языка Си ] "Префикс, постфикс
--
и++
операторы были введены на языке B (предшественник C) Кеном Томпсоном - и нет, они не были вдохновлены PDP-11, которого в то время не существовало.Цитата из "Развитие языка Си" Деннис Ритчи :
источник
Очевидная причина существования оператора postincrement заключается в том, что вам не нужно писать выражения наподобие
(++x,x-1)
или(x+=1,x-1)
повсеместно или бесполезно разделять тривиальные операторы с понятными побочными эффектами на несколько операторов. Вот некоторые примеры:Всякий раз, когда чтение или запись строки в прямом направлении, постинкремент, а не преинкремент, почти всегда является естественной операцией. Я почти никогда не сталкивался с реальным использованием преинкремента в реальном мире, и единственное основное применение, которое я нашел для прекремента, - это преобразование чисел в строки (потому что человеческие системы письма записывают числа в обратном направлении).
Изменить: На самом деле это не было бы так ужасно; вместо
(++x,x-1)
вас, конечно, можно использовать++x-1
или(x+=1)-1
. Всеx++
еще более читабельно.источник
(++x,x-1)
(вызов функции, может быть?)x++
в большинстве случаев (одно исключение:x
это неподписанный тип с рангом ниже,int
а начальное значениеx
является максимальным значением этого типа).PDP-11 предлагал операции пост-инкремента и пре-декремента в наборе команд. Теперь это не были инструкции. Это были модификаторы команд, которые позволяли вам указать, что вы хотите получить значение регистра до его увеличения или уменьшения.
Вот шаг копирования текста на машинном языке:
который переместил слово из одного места в другое, на которое указывают регистры, и регистры были бы готовы сделать это снова.
Поскольку C был построен на и для PDP-11, многие полезные концепции нашли свое отражение в C. C должен был стать полезной заменой языку ассемблера. Предварительное увеличение и последующее уменьшение были добавлены для симметрии.
источник
Я сомневаюсь, что это когда-либо было действительно необходимо. Насколько я знаю, он не компилируется во что-то более компактное на большинстве платформ, чем использование предварительного приращения в цикле. Просто в то время, когда это было сделано, краткость кода была важнее, чем ясность кода.
Это не значит, что ясность кода не важна (или не важна), но когда вы печатаете на модеме с низкой скоростью передачи (все, что вы измеряете в бодах - это медленно), где каждое нажатие клавиши должно было сделать это мэйнфрейм, а затем возвращать по одному байту за раз с одним битом, используемым в качестве проверки на четность, вам не нужно было много печатать.
Это вроде как & и | оператор с более низким приоритетом, чем ==
Он был разработан с благими намерениями (не короткая замыкательная версия && и ||), но теперь он смущает программистов каждый день и, вероятно, никогда не изменится.
В любом случае, это всего лишь ответ, потому что я думаю, что нет хорошего ответа на ваш вопрос, но я, вероятно, ошибаюсь, если я буду более опытным программистом, чем я.
--РЕДАКТИРОВАТЬ--
Должен заметить, что я считаю, что оба они невероятно полезны, я просто указываю, что это не изменится, нравится это кому-то или нет.
источник
Мне нравится постфиксный оператор при работе с указателями (независимо от того, разыменую я их или нет), потому что
читается более естественно, как «перейти к следующему месту», чем эквивалент
что, безусловно, должно смутить начинающих, когда они впервые увидят, что он используется с указателем, где sizeof (* p)! = 1.
Вы уверены, что правильно заявляете о проблеме путаницы? Новичков не смущает тот факт, что оператор postfix ++ имеет более высокий приоритет, чем оператор разыменования *, так что
разбирает как
и нет
как некоторые могут ожидать?
(Вы думаете, что это плохо? См. Чтение объявлений C ).
источник
Среди тонких элементов хорошего программирования - локализация и минимализм :
В том же духе
x++
это можно рассматривать как способ локализации ссылки на текущее значение, приx
этом сразу указав, что вы - по крайней мере, на данный момент - закончили с этим значением и хотите перейти к следующему (допустимо,x
является лиint
или указатель или итератор). Приложив немного воображения, вы можете сравнить это с тем, чтобы позволить старому значению / позицииx
выйти из области видимости сразу после того, как оно больше не требуется, и перейти к новому значению. Точная точка, в которой возможен этот переход, выделяется постфиксом++
. Подразумевается, что это, вероятно, конечное использование «старого»,x
может быть полезным для понимания алгоритма, помогая программисту сканировать окружающий код. Конечно, постфикс++
может заставить программиста искать использование нового значения, которое может или не может быть хорошим в зависимости от того, когда это новое значение действительно необходимо, так что это аспект "искусства" или "ремесла" программирования, чтобы определить, что более важно полезно в данных обстоятельствах.В то время как многие новички могут быть сбиты с толку особенностью, стоит сравнить ее с долгосрочными преимуществами: новички не остаются новичками надолго.
источник
Ух ты, много ответов не совсем по теме (если я могу быть настолько смелым), и, пожалуйста, прости меня, если я указываю на очевидное - особенно в свете твоего комментария, чтобы не указывать на семантику, но на очевидное (с точки зрения Страуструпа, я полагаю) еще не опубликовано! :)
Postfix
x++
создает временное выражение, которое передается «вверх» в выражении, а переменнаяx
впоследствии увеличивается.Префикс
++x
не создает временный объект, но увеличивает «x» и передает результат в выражение.Значение - удобство для тех, кто знает оператора.
Например:
Конечно, результаты этого примера могут быть получены из другого эквивалентного (и, возможно, менее косого) кода.
Так почему у нас есть постфиксный оператор? Я думаю, потому что это идиома, которая сохраняется, несмотря на путаницу, которую она, очевидно, создает. Это унаследованная конструкция, которая когда-то воспринималась как имеющая ценность, хотя я не уверен, что воспринимаемая ценность была столько же для производительности, сколько для удобства. Это удобство не было потеряно, но я думаю, что оценка удобочитаемости увеличилась, что привело к сомнению цели оператора.
Нужен ли нам постфиксный оператор? Скорее всего нет. Его результат сбивает с толку и создает барьер для понимания. Некоторые хорошие программисты, безусловно, сразу узнают, где найти его полезным, часто в тех местах, где он имеет своеобразную «PERL-esque» красоту. Стоимость этой красоты - читабельность.
Я согласен, что явный код имеет преимущества над краткостью, удобочитаемостью, понятностью и удобством обслуживания. Хорошие дизайнеры и менеджеры хотят этого.
Однако некоторые программисты признают определенную красоту, которая побуждает их создавать код с вещами, предназначенными исключительно для красивой простоты - такими как оператор postfix, - код, который они никогда бы не подумали - или не нашли интеллектуально стимулирующим. В этом есть что-то, что делает его более красивым, если не более желательным, несмотря на содержательность.
Другими словами, некоторые люди находят,
while (*dst++ = *src++);
что это просто более красивое решение, которое захватывает ваше дыхание своей простотой, точно так же, как если бы оно было кистью на холсте. То, что вы вынуждены понимать язык, ценить красоту, только добавляет к ее великолепию.источник
for
операторы, черт возьми, дажеwhile
(вgoto
конце концов, мы имеем )?Давайте посмотрим на оригинальное обоснование Kernighan & Ritchie (оригинальные K & R стр. 42 и 43):
Текст продолжается некоторыми примерами, которые используют приращения в индексе, с явной целью написания « более компактного » кода. Таким образом, причина этих операторов заключается в удобстве более компактного кода.
Три приведенных примера (
squeeze()
,getline()
иstrcat()
) используют только постфикс в выражениях, использующих индексацию. Авторы сравнивают код с более длинной версией, которая не использует встроенные приращения. Это подтверждает, что основное внимание уделяется компактности.На странице 102 выделено K & R, использование этих операторов в сочетании с разыменованием указателей (например,
*--p
и*p--
). Дальнейшего примера не приводится, но опять же, они ясно дают понять, что выгода заключается в компактности.Например, префикс очень часто используется при уменьшении индекса или указателя, начиная с конца.
источник
Я буду не согласен с утверждением о том , что
++p
,p++
, каким - то образом трудно читать или неясными. Один означает «увеличить p и затем прочитать p», другой означает «прочитать p и затем увеличить p». В обоих случаях оператор в коде находится именно там, где он находится в объяснении кода, поэтому, если вы знаете, что++
означает, вы знаете, что означает результирующий код.Вы можете создать обфусцированный код, используя любой синтаксис, но я не вижу здесь случая, который
p++
/++p
является по своей сути запутанным.источник
это от POV электротехника:
Есть много процессоров, которые имеют встроенные операторы пост-инкремента и пре-декремента для поддержки стека «последний пришел-первым вышел» (LIFO).
это может быть как:
я не знаю, но по этой причине я ожидал увидеть префиксные и постфиксные операторы приращения.
источник
Чтобы подчеркнуть точку зрения Кристофа о компактности, я хочу показать вам немного кода из V6. Это часть оригинального компилятора C, http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/c/c00.c :
Это код, который был распространен в самиздате как хорошее образование, и тем не менее мы бы никогда не написали его сегодня. Посмотрите , как много побочных эффектов , которые были упакованы в
if
иwhile
условие, и , наоборот, как обаfor
петель не имеет приращение выражения , так как это сделано в теле цикла. Посмотрите, как блоки заключаются в фигурные скобки, только когда это абсолютно необходимо.Частично это была, конечно, ручная микрооптимизация, подобная той, которую мы ожидаем, что компилятор сделает для нас сегодня, но я бы также выдвинул гипотезу о том, что, когда весь ваш интерфейс к компьютеру представляет собой один стеклянный tty 80x25, вам нужен код быть настолько плотным, насколько это возможно, чтобы вы могли видеть больше этого одновременно.
(Использование глобальных буферов для всего - это, вероятно, похмелье от перерезания зубов на ассемблере.)
источник
rp->hflag =| xdflg
произойдет, что, я считаю, будет синтаксической ошибкой на современном компиляторе. «В какой-то момент» как раз и есть именно V6; переход к+=
форме произошел в V7. (Компилятор V6 только принял=+
, если я правильно читаю этот код - см.Возможно, вам стоит подумать и о косметике.
возможно, "выглядит лучше", чем
По какой-то причине оператор постинкремента выглядит очень привлекательно. Он короткий, сладкий, и все увеличивается на 1. Сравните это с префиксом:
"оглядывается назад" не так ли? Вы никогда не увидите знак «плюс» в математике, за исключением случаев, когда кто-то глупо указывает что-то положительное (
+0
или что-то в этом роде). Мы привыкли видеть ОБЪЕКТ + ЧТО-ТОЭто как-то связано с оценкой? А, А +, А ++? Я могу сказать вам, что сначала я был довольно сыром, что люди
operator++
из Python удалили свой язык!источник
i++
возвращает исходное значениеi
иi+=1
и++i
возвращает исходное значениеi
плюс 1. Так как вы используете возвращаемые значения для потока управления, это имеет значение.Считая от 9 до 0 включительно:
Или эквивалентный цикл while.
источник
for (unsigned i = 9; i <= 9; --i)
?