Почему локальные переменные инициализируются мусором?
#новичкам
Изучать С++ больно по многим причинам. Одна из них - есть вещи, которые просто надо принять на веру. И чтобы понять, почему эти вещи работают так, а не иначе, нужно много времени за кодингом и качать сиплюсплюсные и computer science бицепсы.
Сегодня разберем вопрос, который лично меня мучал в начале пути:
почему локальная переменная будет заполнена мусором, если ее не инициализировать?
int main() {
int i;
std::cout << i << std::endl;
}
// POSSIBLE OUTPUT:
// 64
Ну и в дополнение:
Откуда берется этот мусор и почему нельзя просто нулями заполнить?
На самом деле
мусор - не значит какие-то рандомные числа, никто их не генерирует. И чтобы понять, откуда берется мусор, нужно знать о стеке вызовов.
Стек вызовов - пространство в памяти, которое содержит информацию об исполнении функций. Представим аналогию со стопкой книг. Каждая книга - небольшой кусочек памяти с информацией об отдельной функции(фрейм функции). Как только вы вызываете функцию, на стопку кладется новая книга(фрейм). Когда исполнение доходит до конца функции - сверху снимается книга. Это происходит автоматически на уровне машинных инструций.
Так вот
локальные переменные как раз хранятся на стеке. Если точнее, то значения локальных переменных конкретной функции лежат внутри фрейма этой функции. И нас интересует процесс выделения памяти под локальные переменные.
При исполнении программы есть специальный указатель, который указывает на вершину стека.
Выделение памяти под локальную переменную - это просто сдвиг этого указателя. И когда мы говорим:
int main() {
int i;
...
}
Мы говорим, что хотим иметь переменную
i и надо выделить под нее память, то есть просто передвинуть указатель на стек с позиции X на позицию X+4. Само значение мы при этом не задаем.
Но значение-то все равно будет.
И будет оно браться их тех битиков-байтиков, которые находились в куске памяти от X до X-4.
А кто-то из вас заранее знает, какие байтики там будут храниться?
Нет
На этом месте почти наверняка уже располагались данные какой-нибудь другой функции, которая уже выполнилась ранее и ее фрейм снялся со стека.
Так как для вас все эти процессы невидимы, вы и получаете мусор в неинициализированной переменной.
Но это не совсем мусор: это просто данные ранее выполнившейся функции.
Чтобы более наглядно представить себе процесс работы со стеком вызовов, можете воспользоваться
этим визуализатором. Там примеры фиксированные. Если же хотите на собственных примерах посмотреть - можете поиграться
тут.
"Ну окей. Мусор берется из данных предыдущих вызовов функций.
Но до main'а-то ничего не вызывается! Откуда там мусор?"
На самом деле вызывается, вы просто не видите этого. Чтобы подготовить программу к запуску, надо немало потрудиться и эти труды обязательно будут отражены на стеке.
"Ладно, выкрутился. Но зачем вообще этот мусор оставлять,
почему нельзя память занулять?"
Да можно, почему нельзя. Просто это не делается.
Концепция С/С++ - не плати за то, чем не пользуешься.
"Очищение" памяти отнимает дополнительное драгоценное время исполнения программы. И никто его не хочет тратить на такую ненужную вещь, как обнуление памяти.
Инициализируйте свои переменные и проблем с вывозом мусора не будет.
Understand the root cause. Stay cool.
#cppcore #os
Обсуждение 39
Обсуждение не доступно в веб-версии. Чтобы написать комментарий, перейдите в приложение Telegram.
Обсудить в Telegram