tw1ggyz
@tw1ggyz

Сколько раз выделяется память под локальные переменные цикла в С++?

Из книги Страуструпа "Программирование. Принципы и практика с использованием C++"
Пример кода
vector<string> v;

void f()
{
    string s;
    while (cin >> s && s != "quit")
    {
        string stripped;
        string not_letters;

        for (int i = 0; i < s.size(); ++i)
        {
            if (isalpha(s[i]))
                stripped += s[i];
            else
                not_letters += s[i];
        }

        v.push_back(stripped);
    }
}



И вот такие пояснения к нему:
Каждый раз, когда мы входим в тело цикла while, создаются переменные stripped и not_letters. Они существуют до выхода из тела цикла. В этот момент они уничтожаются до того, как произойдет проверка условия выхода из цикла. Так что если до того, как мы обнаружим строку quit, мы выполним цикл десять раз, переменные stripped и not_letters будут созданы и уничтожены десять раз


Каждый раз, когда мы входим в цикл for, создается переменная i. Каждый раз, когда мы выходим из цикла for, переменная i уничтожается до того, как мы достигнем инструкции v.push_back(stripped)


Обратите внимание на то, что компиляторы довольно разумны и им позволено оптимизировать код. В частности, компиляторы не выделяют и не освобождают память чаще, чем это действительно требуется.


Какие-то взаимоисключающие параграфы. Допустим, с переменной i в цикле for еще можно как-то логически сделать вывод, что она не уничтожается после каждого шага, потому что значение накапливается и служит условием для завершения цикла. Но если в тело этого for добавить еще какое-нибудь string str = "Just for fun"; то str будет в конце каждого шага удаляться, а в начале создаваться заново?

Аналогично с while и переменными stripped и not_letters. Из приведенного в книге объяснения не понятно - то ли действительно все так плохо, то ли компиляторы в итоге все-таки не удаляют-выделяют память туда-сюда, а делают это единожды.
  • Вопрос задан
  • 500 просмотров
Решения вопроса 1
tsarevfs
@tsarevfs Куратор тега C++
C++ developer
for (int i = 0, j = 10; i < j; ++i, --j)
{
   const int l = a[i] + 1;
   const int r = a[j] + 1;
   const std::string msg = std::to_string(l + r) + " bottles of rum";
   std::cout << msg << std::endl; 
}

1) i, j обьявлены в заголовке цикла. Они создаются 1 раз и живут до конца цикла.
2) l, r обьявлены в теле цикла. Каждую итерацию мы присваиваем им нове значение. Поскольку int это базовый тип, переменная просто записывется на стек без дополнительного выделения памяти.
3) msg это уже переменная более сложного типа. Хотя она и создается на стеке, внутри конструктора она может выделять память с помощью new. И это будет происходить на каждой итерации.
4) Более того, std::to_string(l + r) создаст временный объект std::string, который удалится после завершения operator+

С одной стороны полезно понимать какие операции могут приводить к выделению памяти, так как это не быстро. С другой об этом можно не думать слишком много при написании бизнес кода. Преждевременная оптимизация это не есть хорошо.
Оптимизации компилятора работают достаточно хорошо. Но они не всесильны, и не должны менять поведение. Поэтому, содавать тяжелый обьект в цикле, если можно сделать это один раз, не очень хорошая идея.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
15432
@15432
Системный программист ^_^
Зависит от ситуации. Компиляторы стараются оптимизировать такие вещи по максимуму. Держать переменные в регистрах или в зарезервированной ячейке стека.
А вот в случае со строкой очень даже может получиться создание и удаление каждый раз - например, вы поменяете в строке один символ внутри цикла, а по логике строка в начале цикла должна быть в неизмененном виде. И таких нюансов полно.

Потому желательно не перекладывать оптимизацию кода полностью на компилятор и не создавать ситуации, которые очевидно затормозят выполнение. В случае со строкой достаточно добавить static к определению, и инициализация произойдет единожды
Ответ написан
Adamos
@Adamos
Есть часто используемые паттерны, которые компилятор умеет распознавать и оптимизировать на уровне машинного кода. Это позволяет вам не париться с оптимизацией переменных цикла и спопойно использовать итераторы, например.
Но это не освобождает от понимания, что строка - это не просто так объявленная переменная, она может оперировать памятью не только при создании, но и при изменении. Поскольку работа с памятью - куда более затратная операция, чем всякие там вычисления, при оптимизации нужно обращать внимание на то, действительно ли тебе требуется создавать переменную в цикле, например. Не рассчитывая, что процессор сделает это сам. Но только при оптимизации.
Ответ написан
vt4a2h
@vt4a2h Куратор тега C++
Senior software engineer (C++/Qt/boost)
На самом деле, всё зависит от того, насколько хорошо компилятор оптимизирует код. Я советую вам использовать вот этот сервис: https://godbolt.org/ для изучения. Попробуйте поиграть с различными флагами оптимизации и т.п. Современные компиляторы очень хорошо оптимизируют код.
Помимо этого, можно использовать различные бенчмарки.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы