Jump to content

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

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

Пример языка 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)
{
    // If any of these pointers are Null, we fail.
    if (!output || !first_name || !last_name)
    {
        printf("Error!\n");
        return -1;
    }

    // We make sure the length of the strings are less than 16 bytes (including the null-byte)
    // in order to avoid overflows
    if (strlen(first_name) > 15 ||  strlen(last_name) > 15) {
      printf("first_name and last_name cannot be longer than 16 characters!\n");
      return -1;
    }

    // Initializing the members
    struct 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);

    //copying the result to output
    memcpy(output, &new_student, sizeof(struct student));
    return 0;
}

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

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

Воздействие

[ редактировать ]

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

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

[ редактировать ]

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

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

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

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

См. также

[ редактировать ]
  1. ^ стркпи
  2. ^ мемкпи()
  3. ^ «ISO/IEC 9899:TC3 (текущий стандарт C)» (PDF) . 07.09.2007. п. 126 . Проверено 26 сентября 2008 г. Раздел 6.7.8, пункт 10.
  4. ^ «Спецификация языка Java: 4.12.5 Начальные значения переменных» . Сан Микросистемс . Проверено 18 октября 2008 г.

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

[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 97a9e241472b4933312a4cb758d1faca__1677088380
URL1:https://arc.ask3.ru/arc/aa/97/ca/97a9e241472b4933312a4cb758d1faca.html
Заголовок, (Title) документа по адресу, URL1:
Uninitialized variable - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)