Чи є різниця в ефективності між i ++ та ++ i в C?


Відповіді:


396

Резюме: Ні.

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

Ми можемо продемонструвати це, переглянувши код для цієї функції, як і з, так ++iі з i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

Файли однакові, за винятком ++iта i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

Ми складемо їх, а також отримаємо створений асемблер:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

І ми можемо бачити, що і генеровані об'єктні файли, і файли асемблера однакові.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22

9
Я знаю, що це питання стосується C, але мені було б цікаво дізнатися, чи можуть браузери зробити цю оптимізацію для javascript.
ТМ.

69
Тож значення "Ні" є дійсним для одного компілятора, який ви протестували.
Андреас

173
@Andreas: Добре запитання. Раніше я був автором компілятора і мав можливість протестувати цей код на багатьох процесорах, операційних системах та компіляторах. Єдиний компілятор, який я виявив, що не оптимізував випадку i ++ (насправді компілятор, який професійно довів це до мене), - компілятор Software Toolworks C80 від Уолта Білофського. Цей компілятор був для систем Intel 8080 CP / M. Можна з упевненістю сказати, що будь-який компілятор, який не включає цю оптимізацію, не призначений для загального використання.
Марк Гаррісон

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

7
@monokrome Оскільки розробники можуть забезпечити власну реалізацію для операторів префікса та постфікса на багатьох мовах, це не завжди може бути прийнятним рішенням для компілятора, не попередньо порівнюючи ті функції, які можуть бути нетривіальними.
pickypg

112

Від Ефективності проти наміру Ендрю Кеніга:

По-перше, далеко не очевидно, що ++iє більш ефективним, ніж i++, принаймні, щодо цілих змінних.

І:

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

Отже, якщо отримане значення не буде використано, я б використав ++i. Але не тому, що вона більш ефективна: тому що вона правильно висловлює мій намір.


5
Не забуваймо, що і інші одинарні оператори є префіксом. Я думаю, що ++ i - це "семантичний" спосіб використання одинарного оператора, тоді як i ++ існує для того, щоб відповідати конкретній потребі (оцінка перед додаванням).
ТМ.

9
Якщо отримане значення не використовується, в семантиці немає різниці : тобто немає підстав віддавати перевагу тій чи іншій конструкції.
Еймон Нербонна

3
Як читач я бачу різницю. Тож як письменник я покажу свій намір, вибравши один над іншим. Це просто звичка намагатися спілкуватися зі своїми друзями-програмістами та товаришами по команді через код :)
Sébastien RoccaSerra

11
Слідуючи раді Кеніга, я код i++так само , як я б код i += nабо i = i + n, тобто у вигляді мішені дієслова об'єкта , з метою Операнд зліва від дієслова оператора. У випадку з i++правильним об'єктом немає , але правило все ж діє, зберігаючи ціль зліва від оператора дієслова .
David R Tribble

4
Якщо ви намагаєтеся збільшити i, то i ++ і ++ i правильно висловлюють свої наміри. Немає причин віддавати перевагу одному над іншим. Як читач я не бачу різниці, чи ваші колеги плутаються, коли бачать i ++ і думають, може, це помилка друку, і він не мав на увазі збільшення?
дан Картер

46

Краща відповідь: ++iінколи буде швидше, але ніколи повільніше.

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

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

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

Ще одна відмінність полягає в тому, що у ++iвас є можливість повернути посилання замість значення. Знову ж таки, залежно від того, що бере участь у створенні копії вашого об'єкта, це може бути повільніше.

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


36
У запитанні прямо вказано C, ніяких посилань на C ++.
Ден Крістоловеану

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

-1 Хоча це добре порадити програмістам на C ++, воно не відповідає на найменше питання, яке позначено тегом C. У C це абсолютно не має значення, використовуєте ви префікс чи постфікс.
Лундін

@Pacerier Питання позначено тегом C і C. Чому ви вважаєте, що вони в цьому не зацікавлені? З огляду на те, як ТА працює, чи не було б розумним вважати, що їх цікавить лише C, а не Java, C #, COBOL або будь-яка інша мова, яка не є темою?
Лундін

18

Коротка відповідь:

Там ніколи не буває якась -то різниця між i++і ++iз точки зору швидкості. Хороший компілятор не повинен генерувати різний код у двох випадках.

Довга відповідь:

Що будь-яка інша відповідь не зазначає, це різниця між ++iпротиi++ лише сенс у тому вираженні, яке воно знайдено.

У випадку з for(i=0; i<n; i++), i++один є власним виразом: перед точкою є точка послідовності, i++а після - одна. Таким чином, єдиний згенерований машинний код - це "збільшення iна 1", і це чітко визначено, як це секвенується стосовно решти програми. Тож якби ви змінили його на префікс ++, це не мало значення в найменшій мірі, ви все одно отримаєте машинний код «збільшити iна 1».

Різниці між ++iі i++лише важливими в таких виразах, як array[i++] = x;проти array[++i] = x;. Деякі можуть заперечити і сказати, що постфікс буде повільнішим у таких операціях, оскільки реєстр, де iмешкає, повинен бути перезавантажений пізніше. Але потім зауважте, що компілятор вільний замовляти ваші інструкції будь-яким способом, який йому заманеться, доки він не «порушує поведінку абстрактної машини», як називає її стандарт C.

Тож ви можете припустити, що array[i++] = x;він переводиться на машинний код як:

  • Зберігання вартості iв реєстрі А.
  • Зберегти адресу масиву в регістрі B.
  • Додайте А та В, зберігайте результати в А.
  • У цій новій адресі, представленій A, збережіть значення x.
  • Магазинна вартість i в регістрі A // неефективно, тому що додаткову інструкцію тут ми вже зробили це один раз.
  • Реєстр приросту А.
  • Реєстрація магазину в i.

компілятор може також ефективніше створювати код, наприклад:

  • Зберігання вартості iв реєстрі А.
  • Зберегти адресу масиву в регістрі B.
  • Додайте А та В, зберігайте результати у Б.
  • Реєстр приросту А.
  • Реєстрація магазину в i.
  • ... // решта коду.

Тільки тому, що ви як програміст C навчені думати, що постфікс ++відбувається в кінці, машинний код не потрібно замовляти таким чином.

Отже, немає різниці між префіксом та постфіксом ++у C. Тепер, чим ви, як програміст C, слід відрізнятись, це люди, які непослідовно використовують префікс в деяких випадках та постфікс в інших випадках, без будь-якого обґрунтування, чому. Це говорить про те, що вони не впевнені в тому, як працює C, або що вони мають неправильні знання мови. Це завжди поганий знак, це, в свою чергу, говорить про те, що вони приймають інші сумнівні рішення у своїй програмі, засновані на забобонах чи "релігійних догмах".

"Префікс ++завжди швидший" - це справді одна з таких помилкових догм, яка є поширеною серед потенційних програмістів на С.


3
Ніхто не сказав: "Префікс ++ завжди швидший". Це неправильно написано. Що вони сказали: "Postfix ++ ніколи не буває швидшим".
Pacerier

2
@Pacerier Я не цитую жодної конкретної людини, а просто широке, неправильне переконання.
Лундін

@rbaleksandar C ++! = C.
Лундін

@rbaleksandar Питання та відповіді говорять про C ++. Що відрізняється від C, оскільки має конструктори перевантаження операторів та копіювання тощо
Lundin

3
@rbaleksandar c ++ == c && ++ c! = c
sadljkfhalskdjfh

17

Беручи лист у Скотта Майєрса, Ефективніший c ++ Пункт 6: Розрізняйте форми префікса та постфікса, що збільшуються та зменшуються .

Версія префікса завжди надається переваги над постфіксом стосовно об'єктів, особливо щодо ітераторів.

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

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

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

Ось чому, коли ви бачите приклади за допомогою ітераторів, вони завжди використовують версію префікса.

Але, як ви зазначаєте, для int's різниці фактично немає, оскільки оптимізація компілятора може відбутися.


4
Я думаю, що його питання було спрямовано на C, але для C ++ ви абсолютно праві, і, крім того, люди C повинні прийняти це, оскільки вони можуть використовувати його і для C ++. Я занадто часто бачу, що програмісти на C використовують синтаксис постфікса ;-)
Anders Rune Jensen

-1 Хоча це добре порадити програмістам на C ++, воно не відповідає на найменше питання, яке позначено тегом C. У C це абсолютно не має значення, використовуєте ви префікс чи постфікс.
Лундін

1
Префікс @Lundin і postifx мають значення в C відповідно до відповіді Андреаса . Ви не можете припустити, що компілятор буде оптимізуватися далеко, і це просто хороша практика для будь-якої мови, тому Alwas віддає перевагу ++ i over i ++
JProgrammer

@JProgrammer Вони не мають значення відповідно до вашої відповіді щиро :) Якщо ви виявите, що вони дають інший код, це тому, що вам не вдалося включити оптимізацію, або тому, що компілятор поганий. У будь-якому випадку, ваша відповідь поза темою, оскільки питання
стосувалося

16

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

for (i = 0; i < 100; i++)

У кожному циклі у вас буде одна інструкція для кожного:

  1. Додавання 1до i.
  2. Порівняйте, чи iменше а 100.
  3. Умовна гілка, якщо iменша за a 100.

Беручи до уваги цикл зменшення:

for (i = 100; i != 0; i--)

Цикл матиме інструкцію для кожного з:

  1. Декрет i, встановлення прапора статусу реєстру CPU.
  2. Умовна гілка залежно від стану реєстру процесора ( Z==0).

Звичайно, це працює лише при зменшенні нуля!

Згадано з Посібника для розробників системи ARM.


Хороший. Але це не створює менше звернень кешу?
AndreasT

Існує стара дивна хитрість з книг оптимізації коду, що поєднує перевагу нульової тестової гілки з збільшенням адреси. Ось приклад мови високого рівня (напевно, марний, оскільки багато компіляторів досить розумні, щоб замінити його на менш ефективний, але більш поширений код циклу): int a [N]; для (i = -N; i; ++ i) a [N + i] + = 123;
noop

@noop Ви могли б детальніше розглянути, до яких книг з оптимізації коду ви звертаєтесь? Я намагався знайти хороших.
мезаморфний

7
Ця відповідь зовсім не є відповіддю на питання.
монокром

@mezamorphic Для x86 моїми джерелами є: посібники з оптимізації Intel, Agner Fog, Paul Hsieh, Michael Abrash.
noop

11

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

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


3
Я вважаю, що неправильно віддавати перевагу розпливчастому покращенню читабельності фактичного підвищення ефективності та загальної ясності намірів.
noop

4
Мій термін "час читання програміста" приблизно аналогічний "ясності намірів". "Фактичні підвищення ефективності" часто незмінні, досить близькі до нуля, щоб назвати їх нульовими. У випадку з ОП, якщо код не був профільований, щоб виявити, що ++ i є вузьким місцем, питання про те, який з них швидше, - це марна трата часу та думки програміста.
Енді Лестер

2
Різниця в читанні між ++ i та i ++ - це лише питання особистої переваги, але ++ я, очевидно, передбачає простішу операцію, ніж i ++, незважаючи на те, що результат є еквівалентним для тривіальних випадків та простих типів даних при оптимізації компілятора. Тому ++ i є переможцем для мене, коли конкретні властивості після збільшення не потрібні.
noop

Ви говорите, що я говорю. Важливіше проявити наміри та покращити читабельність, ніж турбуватися про «ефективність».
Енді Лестер,

2
Я досі не можу погодитися. Якщо читабельність стоїть вище у вашому списку пріоритетів, можливо, ваш вибір мови програмування неправильний. Основна мета C / C ++ - написання ефективного коду.
noop

11

Перш за все: Різниця між i++і ++iє незначною в С.


До деталей.

1. Загальновідома проблема C ++: ++iшвидше

У C ++ ++iбільш ефективним iff iє якийсь об'єкт з перевантаженим оператором приросту.

Чому?
В ++iоб'єкт спочатку збільшується і згодом може передаватися як посилання const на будь-яку іншу функцію. Це неможливо, якщо вираз є foo(i++)через те, що тепер приріст потрібно зробити раніше, ніж foo()викликається, але старе значення потрібно передати foo(). Отже, компілятор змушений зробити копію, iперш ніж виконувати оператор приросту на оригіналі. Додаткові виклики конструктора / деструктора - це погана частина.

Як зазначалося вище, це не стосується фундаментальних типів.

2. Маловідомий факт: i++ може бути швидше

Якщо жодного конструктора / деструктора не потрібно викликати, що завжди є у C, ++iі він i++повинен бути однаково швидким, правда? Ні. Вони практично однаково швидкі, але можуть бути невеликі відмінності, через які більшість інших відповідачів отримали неправильний шлях.

Як можна i++швидше?
Справа в залежності від даних. Якщо значення потрібно завантажити з пам’яті, потрібно виконати дві наступні операції з ним, збільшуючи його та використовуючи. З ++i, збільшення варто зробити перед тим, як значення можна використовувати. З i++, використання не залежить від приросту, і ЦП може виконувати операцію використання паралельно операції збільшення. Різниця - це щонайменше один цикл процесора, тож він справді незначний, але він є. І це навпаки, тоді багато хто сподівався б.


Про ваш пункт 2: Якщо ++iабо i++використовується в іншому виразі, зміна між ними змінює семантику виразу, тому будь-яке можливе збільшення / втрата ефективності не викликає сумнівів. Якщо вони є окремими, тобто результат операції використовується не відразу, то будь-який гідний компілятор буде компілювати його до тієї ж речі, наприклад, INCінструкція по збірці.
Шахбаз

1
@Shahbaz Це абсолютно правда, але справа не в цьому. 1) Навіть незважаючи на те, що семантика різна, обидваi++ і ++iїх можна використовувати взаємозамінно майже в будь-якій можливій ситуації, регулюючи константи циклу на одну, так що вони майже еквівалентні тому, що вони роблять для програміста. 2) Незважаючи на те, що обидва компілюються в одній інструкції, їх виконання відрізняється для ЦП. У випадку з i++CPU може обчислити приріст паралельно деякій іншій інструкції, яка використовує те саме значення (процесори дійсно це роблять!), Тоді як з ++iпроцесором доводиться планувати іншу інструкцію після збільшення.
cmaster - відновити моніку

4
@Shahbaz Як приклад: if(++foo == 7) bar();і if(foo++ == 6) bar();є функціонально рівнозначними. Однак другий може бути на один цикл швидшим, тому що порівняння та приріст можна обчислити паралельно CPU. Не те, що цей єдиний цикл має велике значення, але різниця є.
cmaster - відновити моніку

1
Гарна думка. Константи виявляються багато (як і< наприклад, vs <=) там, де ++зазвичай використовується, тому перетворення між тим не менш часто можливе.
Шахбаз

1
Мені подобається пункт 2, але це застосовується лише в тому випадку, якщо значення використовується, правда? Питання говорить "якщо отримане значення не використовується?", Тож це може бути заплутано.
jinawee

7

@Mark Незважаючи на те, що компілятору дозволено оптимізувати тимчасову копію змінної (на основі стека), і gcc (в останніх версіях) робить це, не означає, що всі компілятори завжди будуть робити це.

Я просто перевірив це разом із компіляторами, які ми використовуємо в нашому поточному проекті, і 3 з 4 не оптимізують його.

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

Якщо у вас немає дійсно нерозумної реалізації одного з операторів у вашому коді:

Завжди віддаю перевагу ++ i над i ++.


Просто цікаво ... чому ви використовуєте 4 різні компілятори C в одному проекті? Або в одній команді чи одній компанії з цього питання?
Лоуренс Дол

3
Під час створення ігор для консолей кожна платформа пропонує власний компілятор / ланцюжок інструментів. У ідеальному світі ми могли б використовувати gcc / clang / llvm для всіх цілей, але в цьому світі нам потрібно миритися з Microsoft, Intel, Metroworks, Sony тощо.
Андреас

5

У C компілятор може, як правило, оптимізувати їх, щоб вони були однаковими, якщо результат не використовується.

Однак у C ++, якщо використовуються інші типи, які надають власні оператори ++, версія префікса, швидше за все, буде швидшою, ніж версія постфіксу. Отже, якщо вам не потрібна семантика постфіксу, краще скористатися оператором префікса.


4

Я можу придумати ситуацію, коли постфікс повільніше, ніж приріст префікса:

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

А тепер уявіть наступну програму та їх переклад на гіпотетичну збірку:

Приріст префікса:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

Приріст постфіксу:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

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

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


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


4

Я читав більшість відповідей тут і багато коментарів, і не бачив жодного посилання на один екземпляр, який би міг подумати, де i++це більш ефективно, ніж ++i(і, можливо, напрочуд, --i було більш ефективним, ніж i--). Це для компіляторів С для DEC PDP-11!

PDP-11 мав інструкцію по збірці для попереднього зменшення регістра та післязростання, але не навпаки. Вказівки дозволяли використовувати будь-який регістр загального призначення як покажчик стека. Отже, якщо ви використовували щось подібне, *(i++)воно могло б бути зібране в одну інструкцію по збірці, тоді як *(++i)не могло.

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


2
Дуже езотеричне, але дуже цікаве!
Марк Гаррісон

@daShier. +1, хоча я не згоден, це не є езотеричним, або, принаймні, не повинно бути. C був спільно розроблений з Unix в AT&T Bell Labs на початку 70-х, коли PDP-11 був цільовим процесором. У вихідному коді Unix з цієї епохи збільшення приросту "i ++" частково переважає тому, що розробники знали, коли присвоюється значення "j = i ++", або використовуються як індекс, "a [i ++] = n", код буде трохи швидшим (і меншим). Здається, вони звикли використовувати приріст після посту, якщо цього не потрібно. Інші дізналися, прочитавши їхній код, а також перейняли цю звичку.
jimhark

Модель 68000 має таку ж функцію, післякріплення та попереднє зменшення підтримуються апаратно (як режими адресації), але не навпаки. Команда Motorola була натхненна PDP-11 DEC.
jimhark

2
@jimhark, так, я один з тих програмістів PDP-11, які перейшли на 68000 і все ще використовують --iі i++.
daShier

2

Я завжди віддаю перевагу попередній приріст, однак ...

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

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

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


Ваша публікація призначена для C ++, тоді як питання стосується C. У будь-якому випадку, ваша відповідь неправильна: для користувацьких операторів післярахування компілятор, як правило, не зможе створити як ефективний код.
Конрад Рудольф

Він заявляє, "якщо функція стає впорядкованою", і це робить його міркування правильними.
Blaisorblade

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

0

Мій C трохи іржавий, тому заздалегідь прошу вибачення. Швидко, я можу зрозуміти результати. Але я заплутаний у тому, як обидва файли вийшли на один і той же хеш MD5. Можливо, цикл для циклу працює однаково, але чи не будуть наступні 2 рядки коду генерувати різні збірки?

myArray[i++] = "hello";

проти

myArray[++i] = "hello";

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

Всього два мої копійки.


1
@Jason Z Оптимізація компілятора відбувається до того, як збірка закінчується, генерується, вона бачить, що змінна i не використовується більше ніде в тій же лінії, тож утримання її значення було б марною, ймовірно, ефективно перевертає її на i ++. Але це лише здогадка. Я не можу дочекатися, поки хтось із моїх викладачів не спробує сказати, що це швидше, і я став тим хлопцем, який виправляє теорію з практичними доказами. Я майже відчуваю ворожнечу вже ^ _ ^
Таркс

3
"Мій C трохи іржавий, тому я заздалегідь вибачаюся". З вашим C нічого поганого, але ви не прочитали повністю оригінальне запитання: "Чи є різниця в продуктивності між i ++ та ++ i, якщо отримане значення не використовується? " Зверніть увагу на курсив. У вашому прикладі, «результат» з я ++ / ++ я це використовується, але в ідіоматичним для циклу, «результат» предсессіонной / postincrement оператора не використовується , так що компілятор може робити те , що йому подобається.
Родді

2
у вашому прикладі код буде іншим, оскільки ви використовуєте значення. приклад, коли вони були однакові, використовував лише ++ для збільшення, а не використовуючи значення, повернене будь-яким.
Джон Гарднер

1
Виправлення: "компілятор побачив би, що повернене значення i ++ не використовується, тому він переверне його на ++ i". Те, що ви написали, помиляється також через те, що ви не можете мати я разом із одним із i ++, i ++ в одному рядку (заяві), результат не визначений.
Blaisorblade

1
Зміна foo[i++]до foo[++i]нічого не змінюючи інше, очевидно , змінити семантику програми, але на деяких процесорах при використанні компілятора без оптимізації логіки петлі вантажопідйомної, збільшуються pі qодин раз , а потім запустити цикл , який виконує , наприклад , *(p++)=*(q++);буде швидше , ніж з допомогою циклу , який виконує *(++pp)=*(++q);. Для дуже вузьких циклів на деяких процесорах різниця швидкостей може бути значною (більше 10%), але це, мабуть, єдиний випадок у С, де пост-приріст істотно швидший, ніж попередній приріст.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.