В вычислениях неинициализированная переменная - это переменная, объявляется, но перед его использованием не устанавливается определенное известное значение. Это будет иметь некоторую ценность, но не предсказуемую. Таким образом, это ошибка программирования и частый источник ошибок в программном обеспечении.
Обычное предположение, которое делают начинающие программисты, состоит в том, что все переменные устанавливаются в известное значение, например ноль, при их объявлении. Хотя это верно для многих языков, но не для всех, и поэтому существует вероятность ошибки. Такие языки, как C, используют пространство стека для переменных, а набор переменных, выделенный для подпрограммы, известен как кадр стека. Хотя компьютер выделяет соответствующий объем пространства для кадра стека, обычно он делает это просто путем регулировки значения указателя stack и не устанавливает память в любое новое состояние (обычно из соображений эффективности). Следовательно, любое содержимое этой памяти в то время будет отображаться как начальные значения переменных, которые занимают эти адреса.
Вот простой пример на C:
void count (void) {int k, i; for (i = 0; i < 10; i++) { k = k + 1; } printf("%d", k); }
Конечное значение k
не определено. Ответ, что оно должно быть равно 10, предполагает, что оно началось с нуля, что может быть или не соответствовать действительности. Обратите внимание, что в Например, переменная i
инициализируется нулем первым предложением оператора for
.
Другой пример может быть при работе со структурами . В приведенном ниже фрагменте кода у нас есть структура struct student
, которая содержит некоторые переменные, описывающие информацию об учащемся. Функция register_student
вызывает утечку памяти, поскольку не может полностью инициализировать элементы struct student_student
. Если мы внимательно рассмотрим, в начале инициализируются age
, semester
и student_number
. Но инициализация члены first_name
и last_name
неверны. Это связано с тем, что если длина символьных массивов first_name
и last_name
меньше 16 байтов, во время strcpy
нам не удается полностью инициализировать все 16 байтов памяти, зарезервированной для каждого из этих членов. Следовательно, после memcpy()
'преобразования результирующей структуры в output
мы пропускаем некоторую память стека вызывающей стороне.
struct student {unsigned int age; беззнаковый int семестр; char first_name [16]; char last_name [16]; беззнаковый int student_number; }; int register_student (struct student * output, int age, char * first_name, char * last_name) {// Если какой-либо из этих указателей имеет значение Null, мы терпим неудачу. if (! output ||! first_name ||! last_name) {printf ("Ошибка! \ n"); возврат -1; } // Мы убеждаемся, что длина строк меньше 16 байтов (включая нулевой байт) // во избежание переполнения if (strlen (first_name)>15 || strlen (last_name)>15) {printf ( "first_name и last_name не могут быть длиннее 16 символов! \ n"); возврат -1; } // Инициализация членов struct student new_student; new_student.age = возраст; new_student.semester = 1; new_student.student_number = get_new_student_number (); strcpy (new_student.first_name, first_name); strcpy (new_student.last_name, last_name); // копирование результата в вывод memcpy (output, new_student, sizeof (struct student)); возврат 0; }
. В любом случае, даже когда переменная неявно инициализируется значением по умолчанию, например 0, это обычно неправильное значение. Инициализация не означает правильность, если значение установлено по умолчанию. (Однако инициализация по умолчанию 0 является правильной практикой для указателей и массивов указателей, поскольку она делает их недействительными до того, как они фактически инициализируются своим правильным значением.) В C переменные со статической продолжительностью хранения, которые не инициализированы явно, инициализируются как ноль (или null для указателей).
Не только неинициализированные переменные являются частой причиной ошибок, но и этот вид ошибки особенно серьезен, потому что он может быть невоспроизводимым: например, переменная может оставаться только неинициализированной в какой-то ветке программы. В некоторых случаях программы с неинициализированными переменными могут даже пройти тесты программного обеспечения.
Неинициализированные переменные - серьезные ошибки, поскольку они могут использоваться для утечки произвольной памяти или для достижения произвольной перезаписи памяти или для получения кода исполнение, в зависимости от случая. При использовании программного обеспечения, которое использует рандомизацию структуры адресного пространства, часто требуется знать базовый адрес программного обеспечения в памяти. Использование неинициализированной переменной для того, чтобы заставить программное обеспечение утечь указатель из его адресного пространства , можно использовать для обхода ASLR.
Неинициализированные переменные представляют собой особую проблему в таких языках, как ассемблер, C и C ++, которые были разработаны для системного программирования. Разработка этих языков включала в себя философию дизайна, в которой конфликты между производительностью и безопасностью обычно разрешались в пользу производительности. На программиста возложена обязанность знать об опасных проблемах, таких как неинициализированные переменные.
В других языках переменные часто инициализируются известными значениями при создании. Примеры включают:
NULL
(в отличие от None
) и вызывает UnboundLocalError
, когда к такой переменной осуществляется доступ до (повторной) инициализации допустимым значением.Даже в языках, где не инициализировано разрешены переменные, многие компиляторы будут пытаться идентифицировать использование неинициализированных переменных и сообщать о них как о ошибках времени компиляции.