Чому у нас збільшується постфікс?


55

Відмова : Я прекрасно знаю семантику приставки та збільшення постфікса. Тому, будь ласка, не поясніть мені, як вони працюють.

Читаючи питання про переповнення стека, я не можу не помітити, що програмісти заплутуються оператором збільшення постфікса знову і знову. З цього випливає наступне питання: чи є випадки використання, коли збільшення постфікса дає реальну користь з точки зору якості коду?

Дозвольте пояснити моє запитання на прикладі. Ось надзвичайна реалізація 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
@Cody: Оскільки деякі речі є і заплутаними, і корисними. Його не бентежить сам пост-приріст, він збентежений у тому, що це корисність . У нас не повинно бути непотрібних, заплутаних чи ні.
GManNickG

Відповіді:


23

Хоча колись це мало певні наслідки для продуктивності, я думаю, що справжня причина полягає в тому, щоб чітко висловити свої наміри. Справжнє питання полягає в тому, щось while (*d++=*s++);виражає наміри чітко чи ні. ІМО, це так, і я вважаю, що альтернативи, які ви пропонуєте, менш зрозумілі - але це може бути (легко) наслідком того, що витратили десятиліття на звикання до того, як це робиться. Дізнавшись C з K&R (оскільки інших книг про C на той час майже не було ), ймовірно, теж допомагає.

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

Звичайно, можна переходити за борт, але я не думаю, що це так. Зокрема, я думаю, що це перебігає за межі, коли розуміння одного рядка коду стає надзвичайно складним або забирає багато часу, зокрема, коли розуміння меншої кількості рядків коду вимагає більше зусиль, ніж розуміння більшої кількості рядків. Це часто в Lisp та APL, але тут, принаймні, для мене, це не так.

Мене менше турбують попередження компілятора - це мій досвід, коли багато компіляторів доволі регулярно надсилають абсолютно смішні попередження. Хоча я, безумовно, думаю, що люди повинні розуміти їх код (і будь-які попередження, які він може створювати), пристойний код, який трапляється, щоб викликати попередження в якомусь компіляторі, не обов'язково помиляється. Щоправда, початківці не завжди знають, що вони можуть сміливо ігнорувати, але ми не залишаємось початківцями назавжди, і нам не потрібно кодувати, як ми.


12
+1 за вказівку на те, що стислий варіант легше читати деяким із нас. :-)

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

4
@Brooks Мойсей: Я набагато менше захоплений цим. Мені не подобається забруднювати свій код, щоб компенсувати недоліки компілятора.
Джеррі Труну

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

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

32

Це, помилково, було апаратне


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

Хоча C розроблявся на багатьох ранніх і малопотужних машинах, C і Unix вперше вразили відносний великий час за допомогою моделей PDP-11, керованих пам'яттю. Це були відносно важливі комп’ютери свого часу, і Unix був набагато кращим - експоненціально кращим - порівняно з іншими 7 оперативними операційними системами, доступними до -11.

І так трапляється, що на PDP-11

*--p

і

*p++

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

Як наслідок, дизайнерські зразки через C ідіоми, які стали C ментальними макросами.

Це щось на зразок оголошення змінних відразу після {... не з того часу, як C89 був чинним, це вже є вимогою, але тепер це схема коду.

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

У наші дні немає жодної різниці, яку схему ви використовуєте, або якщо ви використовуєте індексацію, і ми, як правило, програмуємо на більш високому рівні, але це, мабуть, мало значення на цих машинах 0,001 ГГц і використовуючи що-небудь, крім того, *--xабо *x++означало б для вас не "дістали" PDP-11, і ви могли б до вас підійти люди, які сказали "чи знаєте ви це ..." :-) :-)


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

3
Х'ю (із посилання, що міститься у коментарі FredOverflow): "Люди часто здогадуються, що вони були створені для використання режимів автоповертання та автоматичного декрементування, передбачених PDP-11 DEC, в якому C і Unix вперше стали популярними. Це історично склалося неможливо, оскільки при розробці B. не було PDP-11. Однак 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, але з'явилися по дорозі. Люди часто здогадуються, що вони були створені для використання режимів автоматичного збільшення та автоматичного зменшення адреси, передбачених PDC-11 DEC, на якому C та Unix вперше стали популярними. Це історично неможливо, оскільки PDP-11 при розробці B не було. PDP-7, однак, мав кілька комірок пам'яті з автоматичним збільшенням, властивістю того, що непряме посилання на пам'ять через них збільшувало комірку. Ця функція, ймовірно, запропонувала Томпсону такі оператори; узагальнення, щоб зробити їх і префіксом, і постфіксом, було його власним. Дійсно,++xбула меншою, ніж у x=x+1.


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

Дякую за цю дуже цікаву довідку! Це підтверджує мою цитату з оригінальної K&R, яка запропонувала мету компактності, а не технічну оптимізацію. Однак я не знав, що ці оператори вже існували в Б.
Крістоф

@BenPen Наскільки я знаю, немає політики закриття запитань, якщо на іншому веб-сайті є дублікат , якщо обидва питання відповідають відповідним сайтам.
Звичайний

Цікаво ... Програміст однозначно не так близько.
BenPen

@BenPen: Прийнята відповідь на запитання про переповнення стека вже наводить те саме джерело, що і я (хоча й не так багато).
Кіт Томпсон,

6

Очевидна причина існування оператора постінкрементації полягає в тому, що вам не доведеться писати вирази на кшталт (++x,x-1)або в (x+=1,x-1)усьому місці або марно відокремлювати тривіальні заяви з легкими для розуміння побічними ефектами на кілька операторів. Ось кілька прикладів:

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

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

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

Редагувати: насправді це було б не так потворно; замість (++x,x-1)вас, звичайно, можна було б використовувати ++x-1або (x+=1)-1. Ще x++читабельніше.


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

@Snowman: Який? Я не бачу таких питань у своїй відповіді.
R ..

якщо у вас є щось на кшталт (++x,x-1)(функція дзвінка, можливо?)

@Snowman: Це не виклик функції. Це оператор з комами С, який є точкою послідовності, сковується в дужках для управління пріоритетом. Результат такий же, як 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

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

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

Це на зразок & і | оператор, що має нижчий пріоритет ніж ==

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

У будь-якому випадку, це лише відповідь, тому що я думаю, що на ваше запитання немає гарної відповіді, але я, мабуть, виявлюсь неправильним кодером більшого гуру ніж я.

--EDIT--

Слід зазначити, що я вважаю, що обидва неймовірно корисні, я просто вказую, що це не змінить, подобається комусь чи ні.


Це навіть не віддалено вірно. Ні в якому разі не було "строгість коду" важливішою за ясність.
Коді Грей

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

6
Що ж, визначення "неглибокий" - "гладко елегантний: відшліфований" або "використання кількох слів: позбавлений зайвої", і те, і інше, як правило, хороша річ при написанні коду. "Терсе" не означає "незрозумілий, важко читається і затуманений", як думають багато людей.
Джеймс Мак-Нілліс

@James: Незважаючи на те, що ви маєте рацію, я думаю, що більшість людей мають на увазі друге визначення терміну при використанні в контексті програмування: "вживання кількох слів; позбавлене зайвої". Згідно з цим визначенням, звичайно, простіше уявити ситуації, коли між стислості та виразністю певного коду існує компроміс. Очевидно, що правильно писати код - це те саме, що і при розмові: зробити речі такими ж короткими, як вони повинні бути, але не коротшими. Оцінювання строгості над експресивністю може призвести до розгубленого вигляду коду, але це, звичайно, не повинно.
Коді Грей

1
перемотайте 40 років і уявіть, що вам довелося помістити свою програму на той барабан, який вмістить лише близько 1000 символів, або написати компілятор може працювати лише з 4 тис. байтів оперативної пам’яті. Були часи, коли строгість та чіткість навіть не були проблемою. Вам довелося бути лаконічним, на цій планеті не існувало комп’ютера, який міг би зберігати, запускати чи компілювати вашу нетермінову програму.
Н.З.К.

3

Мені подобається оператор постфікса при роботі з покажчиками (я їх переписую чи ні) тому, що

p++

читається більш природно як "перехід на наступне місце", ніж еквівалент

p += 1

який, безумовно, повинен збити з пантелику початківців, коли вони вперше бачать, що він використовується із вказівником, де sizeof (* p)! = 1.

Ви впевнені, що правильно констатуєте проблему плутанини? Чи не те, що бентежить початківців, те, що оператор postfix ++ має вищий пріоритет, ніж оператор dereference *, так що

*p++

розбирає як

*(p++)

і ні

(*p)++

як можна було очікувати?

(Ви думаєте, що це погано? Див. « Читання декларацій C» .)


2

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

  • введення змінних в мінімальний обсяг використання
  • використання const, коли доступ до запису не потрібен
  • тощо.

У цьому ж дусі x++можна розглядати як спосіб локалізації посилання на поточне значення, xпри цьому відразу ж вказуючи на те, що ви - принаймні поки що - закінчили це значення і хочете перейти до наступного (дійсно, чи xце intчи вказівник або ітератор). Трохи уяви ви можете порівняти це з тим, щоб xвипустити старе значення / позицію "поза рамки" одразу після того, як воно більше не потрібно, і перейти до нового значення. Точний момент, де можливий цей перехід, підкреслюється постфіксом++ . Наслідком того, що це, мабуть, кінцеве використання «старого», xможе бути корисне розуміння алгоритму, допомагаючи програмісту під час перегляду навколишнього коду. Звичайно, постфікс++ може поставити програміста на пошуки використання нового значення, яке може бути, а може і не добре, залежно від того, коли це нове значення насправді потрібно, тож це аспект "мистецтва" або "майстерності" програмування, щоб визначити, що більше корисні в обставинах.

Хоча багатьох початківців може бентежити особливість, варто збалансувати це з довгостроковими вигодами: початківці не залишаються початківцями довго.


2

Нічого собі, багато відповідей не зовсім точні (якщо я можу бути настільки сміливим), і, будь ласка, пробачте мене, якщо я вказую на очевидне - особливо з огляду на ваш коментар, щоб не вказати на семантику, але очевидну (з точки зору Stroustrup, я думаю,) ще не здається розміщеним! :)

Postfix x++створює тимчасовий, який передається "вгору" у виразі, а зміннуx згодом збільшується.

Префікс ++x не створює тимчасовий об'єкт, а збільшується 'x' і передає результат виразу.

Цінність - зручність для тих, хто знає оператора.

Наприклад:

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

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

Звичайно, результати цього прикладу можна отримати з іншого еквівалентного (і, можливо, менш косого) коду.

То чому ми маємо оператор Postfix? Я здогадуюсь тому, що це ідіома, яка зберігається, незважаючи на плутанину, яку вона, очевидно, створює. Це застаріла конструкція, яка колись, як вважалося, має значення, хоча я не впевнений, що сприйнята цінність була стільки для продуктивності, скільки для зручності. Ця зручність не була втрачена, але я вважаю, що зрозумілість читабельності зросла, в результаті чого ставиться під сумнів мета оператора.

Чи потрібен нам оператор Postfix більше? Ймовірно, ні. Її результат є заплутаним і створює бар'єр для зрозумілості. Деякі хороші кодери, безумовно, відразу знають, де знайти його корисним, часто в місцях, де він має своєрідну "PERL-esque" красу. Вартість цієї краси - читабельність.

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

Однак є певна краса, яку деякі програмісти визнають, що спонукає їх створювати код із речами виключно для красивої простоти - наприклад, оператора Postfix - який код вони б інакше не думали - або вважали інтелектуально стимулюючим. У цьому є певне, що просто робить його красивішим, якщо не більш бажаним, незважаючи на жалість.

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


3
+1 "Деякі люди знаходять, хоча (* dst ++ = * src ++); просто кращим рішенням". Безумовно. Люди, які не розуміють мови складання, насправді не розуміють, наскільки елегантним може бути C, але саме ця заява коду - це сяюча зірка.
Олов'яна людина

"Чи потрібен нам оператор Postfix більше? Ймовірно, ні." - Я не можу не думати про машини Тюрінга. Чи потрібні нам колись автоматичні змінні, forоператор, хек, навіть while(маємо goto, зрештою)?

@Bernd: Ха! Уявіть закриття! Закриття! Скандально! lol
Брайан М. Хант

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

2

Давайте подивимось на оригінальне обґрунтування Kernighan & Ritchie (оригінал K&R, сторінка 42 та 43):

Незвичайними аспектами є те, що ++ і - можуть використовуватися або як префікс, або як постфікс. (...) У контексті, де не потрібне значення (..), виберіть префікс або постфікс відповідно до смаку. Але htere - це ситуації, коли спеціально вимагається те чи інше.

Текст продовжується деякими прикладами, які використовують прирости в межах індексу, з чіткою метою написання " більш компактним » коду. Тому причиною цих операторів є зручність більш компактного коду.

Три приклади , наведені ( squeeze(), getline()іstrcat() ) використовувати тільки в межах постфікси виразів з використанням індексації. Автори порівнюють код із довшою версією, яка не використовує вбудовані кроки. Це підтверджує, що увага зосереджена на компактності.

Виділення K&R на сторінці 102, використання цих операторів у поєднанні з перенаправленням покажчиків (наприклад, *--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

це від ПОВ електричного інженера:

Є багато процесорів, які мають вбудовані оператори після збільшення та попереднього зменшення з метою підтримання стека "останній-перший-вихід" (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петлі не мають вираження приросту, оскільки це робиться в тілі циклу. Подивіться, як блоки загортаються в дужки лише тоді, коли це абсолютно необхідно.

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

(Використання глобальних буферів для всього - це, мабуть, похмілля від різання зубів на мові збірки.)


Поводьтеся обережно: "ihash = + * sp ++;" У якийсь момент C мав не тільки + =, але і = + оператор. Сьогодні = + - це оператор присвоєння, за яким слідує одинарний плюс, який нічого не робить, тому він еквівалентний "ihash = * sp; sp + = 1;"
gnasher729

@ gnasher729 Так, і пізніше це є rp->hflag =| xdflg, що, я вважаю, буде синтаксичною помилкою на сучасному компіляторі. "У якийсь момент" є саме V6, як це відбувається; перехід для +=формування випадковості у V7. (Компілятор V6 прийнятий , лише=+ якщо я правильно читаю цей код - див. Символ () у тому ж файлі.)
zwol

-1

Можливо, вам варто подумати і про косметику.

while( i++ )

певно, «виглядає краще», ніж

while( i+=1 )

Чомусь оператор після примноження виглядає дуже привабливо. Його короткий, його солодкий, і він збільшує все на 1. Порівняйте його з префіксом:

while( ++i )

"дивиться назад", чи не так? Ви ніколи не бачите знак ПЕРШИЙ з математики, за винятком випадків, коли хтось глупо вказує щось позитивне ( +0чи щось подібне). Ми звикли бачити OBJECT + SOMETHING

Це щось пов’язане з оцінкою? A, A +, A ++? Я можу вам сказати, що я спочатку був симпатичним, що народи Python видалили operator++зі своєї мови!


1
Ваші приклади не роблять те саме. i++повертає початкове значення i, а i+=1й ++iповернути початкове значення iплюс 1. Так як ви використовуєте повернені значення для управління потоком, це справа.
Девід Торнлі

Так, ви праві. Але я дивлюся лише на читабельність тут.
bobobobo

-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.