Неинициализированная переменная

В вычислительной технике неинициализированная переменная — это переменная , которая объявлена, но не установлена ​​в определенное известное значение перед использованием. Она будет иметь некоторое значение, но непредсказуемое. Таким образом, это ошибка программирования и распространенный источник ошибок в программном обеспечении.

Пример языка C

Обычное предположение, которое делают начинающие программисты, заключается в том, что все переменные устанавливаются в известное значение, например, ноль, когда они объявляются. Хотя это верно для многих языков, это верно не для всех из них, и поэтому существует вероятность ошибки. Такие языки, как C, используют стековое пространство для переменных, а набор переменных, выделенных для подпрограммы, известен как стековый кадр . Хотя компьютер выделяет соответствующее количество пространства для стекового кадра, он обычно делает это просто путем корректировки значения указателя стека и не устанавливает саму память в какое-либо новое состояние (обычно из соображений эффективности). Следовательно, любое содержимое этой памяти в данный момент будет отображаться как начальные значения переменных, которые занимают эти адреса.

Вот простой пример на языке 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 new_student. Если мы посмотрим внимательнее, то в начале инициализируются , ageи semester. student_numberНо инициализация элементов first_nameи last_nameневерна. Это происходит потому, что если длина first_nameи last_nameмассивов символов меньше 16 байт, во время strcpy, [1] мы не можем полностью инициализировать все 16 байт памяти, зарезервированной для каждого из этих элементов. Следовательно, после memcpy()преобразования полученной структуры в output, [2] мы допускаем утечку некоторой стековой памяти в вызывающую функцию.

struct student { unsigned int age ; unsigned int semester ; char first_name [ 16 ]; char last_name [ 16 ]; unsigned 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 ( "Error! \n " ); return -1 ; }                      // Мы гарантируем, что длина строк меньше 16 байт (включая нулевой байт) // чтобы избежать переполнений if ( strlen ( first_name ) > 15 || strlen ( last_name ) > 15 ) { printf ( "first_name и last_name не могут быть длиннее 16 символов! \n " ); return -1 ; }               // Инициализация членов структуры student new_student ; new_student . age = 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 )); return 0 ; }      

В любом случае, даже если переменная неявно инициализируется значением по умолчанию , например 0, это обычно не является правильным значением. Инициализированный не означает правильный, если значение является значением по умолчанию. (Однако инициализация по умолчанию значением 0 является правильной практикой для указателей и массивов указателей, поскольку она делает их недействительными до того, как они будут фактически инициализированы своим правильным значением.) В языке C переменные со статической продолжительностью хранения, которые не инициализируются явно, инициализируются нулем (или null для указателей). [3]

Неинициализированные переменные не только являются частой причиной ошибок, но этот тип ошибок особенно серьезен, поскольку он может быть невоспроизводимым: например, переменная может оставаться неинициализированной только в некоторой ветви программы. В некоторых случаях программы с неинициализированными переменными могут даже проходить тесты программного обеспечения .

Воздействия

Неинициализированные переменные являются мощными ошибками, поскольку они могут быть использованы для утечки произвольной памяти или для достижения произвольной перезаписи памяти или для получения выполнения кода, в зависимости от случая. При эксплуатации программного обеспечения, которое использует рандомизацию адресного пространства (ASLR), часто требуется знать базовый адрес программного обеспечения в памяти. Эксплуатация неинициализированной переменной таким образом, чтобы заставить программное обеспечение утечь указатель из своего адресного пространства, может быть использована для обхода ASLR.

Использование в языках

Неинициализированные переменные являются особой проблемой в таких языках, как ассемблер, C и C++ , которые были разработаны для системного программирования . Разработка этих языков включала в себя философию дизайна, в которой конфликты между производительностью и безопасностью обычно решались в пользу производительности. На программиста возлагалось бремя осознания опасных проблем, таких как неинициализированные переменные.

В других языках переменные часто инициализируются известными значениями при создании. Примеры включают:

  • VHDL инициализирует все стандартные переменные в специальное значение 'U'. Он используется в моделировании, для отладки, чтобы позволить пользователю знать, когда неважные начальные значения, через многозначную логику , влияют на вывод.
  • В Java нет неинициализированных переменных. Поля классов и объектов, не имеющих явного инициализатора, а также элементы массивов автоматически инициализируются значением по умолчанию для своего типа (false для булевых, 0 для всех числовых типов, null для всех ссылочных типов). [4] Локальные переменные в Java должны быть определенно назначены до того, как к ним будет получен доступ, иначе возникнет ошибка компиляции.
  • Python инициализирует локальные переменные значением NULL(отличным от None) и вызывает исключение UnboundLocalErrorпри доступе к такой переменной перед (повторной) инициализацией допустимым значением.
  • D инициализирует все переменные, если программист явно не указал иное.

Даже в языках, где разрешены неинициализированные переменные, многие компиляторы будут пытаться определить использование неинициализированных переменных и сообщать о них как об ошибках времени компиляции . Некоторые языки помогают в этой задаче, предлагая конструкции для обработки инициализации переменных; например, в C# есть особый вид параметров вызова по ссылке для подпрограмм (указанных как вместо обычного ), утверждающих, что переменная может быть неинициализирована при входе, но будет инициализирована впоследствии.outref

Смотрите также

Ссылки

  1. ^ strcpy
  2. ^ memcpy()
  3. ^ "ISO/IEC 9899:TC3 (Текущий стандарт C)" (PDF) . 2007-09-07. стр. 126 . Получено 2008-09-26 .Раздел 6.7.8, пункт 10.
  4. ^ "Спецификация языка Java: 4.12.5 Начальные значения переменных". Sun Microsystems . Получено 2008-10-18 .

Дальнейшее чтение

  • «CWE-457 Использование неинициализированной переменной».
Получено с "https://en.wikipedia.org/w/index.php?title=Неинициализированная_переменная&oldid=1140996374"