Вопрос № 1: является ли объявление переменной внутри цикла хорошей или плохой практикой?
Я читал другие темы о том, есть ли проблемы с производительностью (большинство сказали нет), и что вы всегда должны объявлять переменные как можно ближе к месту их использования. Что мне интересно, так это то, следует ли этого избегать или это действительно предпочтительнее.
Пример:
for(int counter = 0; counter <= 10; counter++)
{
string someString = "testing";
cout << someString;
}
Вопрос № 2: понимают ли большинство компиляторов, что переменная уже была объявлена, и просто пропускают эту часть, или она фактически создает место для нее в памяти каждый раз?
c++
loops
variable-declaration
JeramyRR
источник
источник
Ответы:
Это отличная практика.
Создавая переменные внутри циклов, вы гарантируете, что их область действия ограничена внутри цикла. На него нельзя ссылаться и вызывать вне цикла.
Сюда:
Если имя переменной немного «универсально» (например, «i»), нет риска смешать ее с другой переменной с тем же именем где-нибудь позже в вашем коде (это также можно уменьшить с помощью
-Wshadow
инструкции предупреждения в GCC)Компилятор знает, что область действия переменной ограничена внутри цикла, и, следовательно, выдаст правильное сообщение об ошибке, если на переменную по ошибке ссылаются в другом месте.
И последнее, но не менее важное: некоторая специальная оптимизация может выполняться компилятором более эффективно (наиболее важно распределение регистров), поскольку он знает, что переменная не может использоваться вне цикла. Например, нет необходимости сохранять результат для последующего повторного использования.
Короче говоря, вы правы сделать это.
Однако обратите внимание, что переменная не должна сохранять свое значение между каждым циклом. В этом случае вам может потребоваться инициализировать его каждый раз. Вы также можете создать больший блок, охватывающий цикл, единственной целью которого является объявление переменных, которые должны сохранять свое значение от одного цикла к другому. Обычно это включает сам счетчик циклов.
На вопрос № 2: переменная выделяется один раз, когда вызывается функция. Фактически, с точки зрения распределения, это (почти) то же самое, что и объявление переменной в начале функции. Единственное отличие заключается в объеме: переменную нельзя использовать вне цикла. Возможно даже, что переменная не будет выделена, просто повторно используя какой-то свободный слот (из другой переменной, область которой закончилась).
С ограниченным и более точным охватом приходят более точные оптимизации. Но что еще более важно, это делает ваш код более безопасным, с меньшим количеством состояний (т.е. переменных), о которых нужно беспокоиться при чтении других частей кода.
Это верно даже за пределами
if(){...}
блока. Как правило, вместо:безопаснее написать:
Разница может показаться незначительной, особенно на таком маленьком примере. Но на большую базу кода, это поможет: в настоящее время не существует никакого риска транспортировать некоторое
result
значение изf1()
кf2()
блоку. Каждыйresult
строго ограничен своей областью действия, что делает его роль более точной. С точки зрения рецензента, это гораздо приятнее, так как у него меньше переменных состояния на большие расстояния, о которых нужно беспокоиться и отслеживать.Даже компилятор поможет лучше: предполагая, что в будущем, после некоторого ошибочного изменения кода,
result
не будет должным образом инициализироваться сf2()
. Вторая версия просто откажется работать, сообщив об ошибке во время компиляции (лучше, чем во время выполнения). Первая версия ничего не обнаружит, результатf1()
будет просто проверен во второй раз, будучи запутанным для результатаf2()
.Дополнительная информация
Инструмент с открытым исходным кодом CppCheck (инструмент статического анализа для кода C / C ++) предоставляет несколько полезных советов относительно оптимального диапазона переменных.
В ответ на комментарий о распределении: вышеприведенное правило верно для C, но может не подходить для некоторых классов C ++.
Для стандартных типов и структур размер переменной известен во время компиляции. В C нет такого понятия, как «конструкция», поэтому пространство для переменной будет просто выделено в стек (без какой-либо инициализации) при вызове функции. Вот почему при объявлении переменной внутри цикла существует «нулевая» стоимость.
Тем не менее, для классов C ++ есть конструктор, о котором я знаю гораздо меньше. Я предполагаю, что распределение, вероятно, не будет проблемой, так как компилятор должен быть достаточно умным, чтобы повторно использовать то же пространство, но инициализация, вероятно, будет происходить на каждой итерации цикла.
источник
string
и вvector
частности, оператор присваивания может повторно использовать выделенный буфер каждого цикла, который ( в зависимости от вашего цикла) может быть огромная экономия времени.Как правило, это очень хорошая практика, чтобы держать это очень близко.
В некоторых случаях, например, производительность, которая оправдывает вытягивание переменной из цикла.
В вашем примере программа создает и уничтожает строку каждый раз. В некоторых библиотеках используется небольшая оптимизация строк (SSO), поэтому в некоторых случаях можно избежать динамического выделения.
Предположим, что вы хотите избежать этих избыточных созданий / распределений, вы бы написали это так:
или вы можете вытащить константу:
Он может повторно использовать пространство, которое занимает переменная , и он может вытянуть инварианты из вашего цикла. В случае массива const char (выше) - этот массив может быть извлечен. Тем не менее, конструктор и деструктор должны выполняться на каждой итерации в случае объекта (например,
std::string
). В случаеstd::string
этого «пробел» включает в себя указатель, который содержит динамическое распределение, представляющее символы. Итак, это:потребует избыточного копирования в каждом случае, а также динамического размещения и освобождения, если переменная находится выше порогового значения для количества символов единого входа (а единый вход реализован вашей стандартной библиотекой).
Делая это:
по-прежнему потребуется физическая копия символов на каждой итерации, но форма может привести к одному динамическому выделению, потому что вы присваиваете строку, и реализация должна видеть, что нет необходимости изменять размер резервного размещения строки. Конечно, вы не сделаете этого в этом примере (потому что несколько улучшенных альтернатив уже были продемонстрированы), но вы можете рассмотреть это, когда содержимое строки или вектора меняется.
Так что вы делаете со всеми этими вариантами (и больше)? Держите его очень близко по умолчанию - пока вы не будете хорошо понимать затраты и не будете знать, когда вам следует отклоняться.
источник
Для C ++ это зависит от того, что вы делаете. ОК, это глупый код, но представь
Вы будете ждать 55 секунд, пока не получите вывод myFunc. Просто потому, что каждому контуру и деструктору цикла нужно 5 секунд, чтобы закончить.
Вам понадобится 5 секунд, пока вы не получите вывод myOtherFunc.
Конечно, это безумный пример.
Но это показывает, что это может стать проблемой производительности, когда каждый цикл выполняется одинаковой конструкцией, когда конструктору и / или деструктору требуется некоторое время.
источник
Я не отвечал на вопросы JeremyRR (так как на них уже был дан ответ); вместо этого я отправил только, чтобы дать предложение.
Для JeremyRR вы можете сделать это:
Я не знаю, понимаете ли вы (я не знал, когда впервые начал программировать), что квадратные скобки (если они попарно) могут быть размещены в любом месте кода, а не только после «если», «для», « время "и т. д.
Мой код скомпилирован в Microsoft Visual C ++ 2010 Express, поэтому я знаю, что он работает; Кроме того, я попытался использовать переменную вне скобок, в которой она была определена, и я получил ошибку, поэтому я знаю, что переменная была «уничтожена».
Я не знаю, является ли плохой практикой использование этого метода, так как многие непомеченные скобки могут быстро сделать код нечитаемым, но, возможно, некоторые комментарии могут прояснить ситуацию.
источник
Это очень хорошая практика, так как все вышеприведенные ответы дают очень хороший теоретический аспект вопроса, позвольте мне взглянуть на код, я пытался решить DFS через GEEKSFORGEEKS, я столкнулся с проблемой оптимизации ...... Если вы попытаетесь решить код, объявляющий целое число вне цикла, даст вам сообщение об ошибке оптимизации.
Теперь поместите целые числа внутри цикла, это даст вам правильный ответ ...
это полностью отражает то, что сэр @justin говорил во втором комментарии .... попробуйте это здесь https://practice.geeksforgeeks.org/problems/depth-first-traversal-for-a-graph/1 . просто дай ему шанс ... ты получишь это. Надеюсь, это поможет.
источник
flag
следует повторно инициализировать в 0 на каждойwhile
итерации. Это логическая проблема, а не проблема определения.Глава 4.8 Блочная структура в K & R Язык программирования C 2.Ed. :
Я мог пропустить увидеть соответствующее описание в книге, как:
Но простой тест может доказать предположение:
источник