Страницы

Поиск по вопросам

пятница, 14 февраля 2020 г.

Преинкремент и постинкремент

#c_sharp


Задали вопрос на учебе, что больше потребляет ресурсов ++i или i++. Самостоятельно
инфо не нашёл, прошу подсказать где искать.
    


Ответы

Ответ 1



Согласно спецификации языка, разница между пре- и постинкрементом состоит в том, что при вычислении выражения значением результат является в одном случае старое, а в другом — новое значение инкрементируемой переменной. В случае простого применения в форме x++; (например, такое часто встречается в циклах) возвращаемое значение не используется, и смысл обеих форм строго одинаков, так что они компилируются в одинаковый объектный код. Даже если оператор инкремента переопределён, разницы нет, т. к. (в отличие от C++) нет возможности иметь различные определения для пре- и постинкрементов. Откуда растут ноги у этого вопроса? А вот откуда. В древние времена были наивные компиляторы языка C (и, возможно, C++), которые не умели оптимизировать код. И если они компилировали прямолинейно, то код получался таким: // x++ int temp = x; x = x + 1; // результат = temp; но он не нужен, так что он отбрасывается и // ++x; x = x + 1; // результат = x; но он не нужен, так что он отбрасывается Ваш преподаватель, вероятно, слыхал об этом, и ждёт от вас ответа о том, что необходимо ручной оптимизацией убрать ненужную временную переменную. Он неправ, отстал от реальности на вечность и тянет вас в эпоху трилобитов. В реальности последние как минимум 10 лет компиляторы умеют делать оптимизации намного лучше людей, и в частности прекрасно умеют выбрасывать никому не нужные временные переменные. Так что разницы в сгенерированном коде просто не будет, что можно видеть даже для C++ здесь.* (Если вы, конечно, не отключите оптимизации.) Теперь с точки зрения промышленной практики. Если вам нужно ускорить выполнение вашей программы, нанооптимизации наподобие использования преинкремента вместо постинкремента — даже если бы они и давали выигрыш в один такт процессора — никогда не являются нужным средством. Оптимизируйте ваши алгоритмы и структуры данных, переходите от квадратичных алгоритмов к линейным и логарифмическим. Не экономьте копейки, это не окупается. *Например, из таких двух функций: volatile int x; // угадайте, что будет, если убрать volatile :-P int pre(int num) { for (int i = 0; i < num; ++i) x = i; } int post(int num) { for (int i = 0; i < num; i++) x = i; } gcc 7.2 с ключом -O3 производит pre(int): test edi, edi jle .L2 xor eax, eax .L3: mov DWORD PTR x[rip], eax add eax, 1 cmp edi, eax jne .L3 .L2: rep ret post(int): test edi, edi jle .L6 xor eax, eax .L8: mov DWORD PTR x[rip], eax add eax, 1 cmp edi, eax jne .L8 .L6: xor eax, eax ret А с ключом -Os вообще pre(int): xor eax, eax .L3: cmp eax, edi jge .L2 mov DWORD PTR x[rip], eax inc eax jmp .L3 .L2: ret post(int): jmp pre(int)

Ответ 2



В C# разницы между пост- и преинкрементом нет, в C++ (отвечая на Ваш комментарий с ?) ситуация следующая: как правило, постинкремент реализуется посредством преинкремента и возвращает первоначальное значение переменной. Каноническая форма постинкремента: T T::operator++(int) { T old(*this); ++*this; return old; } Как видите, создается временная переменная old. Каноническая форма преинкремента: T& T::operator++() { // increment your object here return *this; } Поэтому быстрее будет работать преинкремент.

Комментариев нет:

Отправить комментарий