Наскільки важливо ініціалізувати змінну


9

Наскільки важливо ініціалізувати змінні?

Чи належна ініціалізація дозволяє уникнути витоку пам’яті чи має переваги у роботі?


14
Це залежить від мови. У деяких мовах досить важливо запобігати помилкам, в іншому це просто добре зробити, щоб сприяти читанню.
Теластин

Дякую Теластин за ваш внесок. Чи можете ви поставити випадок, коли він стає важливим залежно від мови?
Вівек

4
С ++ тут ​​горезвісний. У налагодженні локальні змінні ініціалізуються до 0 (або null) загальними компіляторами, але є випадковим сміттям при компілюванні для випуску. (хоча мої знання C ++
починаються

Це справа одного разу спаленого-двічі-сором’язливого. Оскільки я бачив / мав помилки, викликані неініціалізованими змінними, особливо покажчиками, це стало звичкою. Для продуктивності це зазвичай не має значення. Для витоку пам’яті насправді не проблема.
Майк Данлаве

1
@Telastyn - це гірше. Невизначена поведінка не обмежується станом сміття, все може статися. Компілятор може припустити, що шляхи, які читають неініціалізовані змінні, недоступні, і усувають "неспоріднені" ефекти, що виникають на цьому шляху.
Калет

Відповіді:


7

Неініціалізовані змінні роблять програму недетермінованою. Кожен раз, коли програма працює, вона може вести себе по-різному. Непов'язані зміни в операційному середовищі, часу доби, фазі Місяця та перестановках таких впливають на те, як і коли проявляються ці демони. Програма може запускатися мільйон разів до появи дефекту, вони можуть робити це кожен раз або виконувати ще один мільйон. Багато проблем ставляться до "збоїв" і ігноруються, або ж повідомлення про дефекти клієнтів, закритих як "Невідтворювані". Як часто ви перезавантажували машину, щоб "виправити" проблему? Як часто ви говорили клієнтові "Ніколи не бачив, щоб це сталося, дайте мені знати, якщо ви побачите це знову" - сподіваючись (знаючи), добре вони не стануть!

Оскільки відтворення дефекту може бути поруч із неможливим у тестовому середовищі, його наступне неможливо знайти та виправити.

Це може зайняти роки, коли помилка вийде на поверхню, як правило, у коді вважається надійною та стабільною. Припускається, що дефект є в більш пізньому коді - відстеження його може зайняти значно більше часу. Зміна компілятора, перемикач компілятора, навіть додавання рядка коду можуть змінити поведінку.

Ініціалізація змінних має величезну перевагу в продуктивності не лише тому, що програма, яка працює правильно, нескінченно швидша, ніж така, яка цього не робить, але розробники витрачають менше часу на пошук та виправлення дефектів, яких не повинно бути там, і більше часу роблять «справжню» роботу.

Інша істотна перевага ініціальних змінних полягає в тому, що оригінальний автор коду повинен вирішити, до чого їх ініціалізувати. Це не завжди тривіальна вправа, а коли не банальна, може бути свідченням поганої конструкції.

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

Редагування: У деяких мовах (наприклад, C #) неможливо використовувати неініціалізовані змінні, оскільки програма не буде компілювати або повідомляти про помилку при виконанні, якщо це зроблено. Однак у багатьох мовах з цими характеристиками є інтерфейси для потенційно небезпечного коду, тому при використанні таких інтерфейсів слід бути обережними, не вводити неініціалізовані змінні.


6
Багато мов програмування автоматично встановлюють свої змінні на якесь заздалегідь задане значення, тому значна частина сказаного тут не стосується цих мов.
Роберт Харві

2
Просто щоб ще раз сказати те, що сказав @RobertHarvey, нічого з цього не стосується C #. Немає переваги в ініціалізації змінних при оголошенні їх, і неможливо використовувати неініціалізовану змінну, тому ви не можете звинувачувати в цьому невідтворювані помилки. (Це є можливість використовувати поле неініціалізованих класу, але він отримує набір значення за замовчуванням , і генерує попередження в цьому випадку)
Bobson

4
@mattnz - Справа в тому, що для мов, які ведуть себе як C # (або Java), деякі з цих порад є оманливими або відвертими неправильними. В якості мови агностика питання, він повинен мати мову агностик відповідь, що означає адресацію мов , які роблять ручки инициализируются змінними безпечно, а також ті , які цього не роблять.
Бобсон

1
Я також додам, що неініціалізовану проблему змінної не важко знайти, оскільки будь-який наполовину гідний компілятор / статичний аналізатор попередить про них
jk.

1
Для Java (і, мабуть, C #) передчасна ініціалізація місцевих жителів є непотрібною і, певно, призводить до появи більше помилок. Наприклад, встановлення змінної до нуля перед її призначенням умовно перешкоджає можливості компілятора сказати вам, що один із шляхів через код може не призвести до присвоєння змінної.
JimmyJames

7

Ініціалізація змінної, як вказувала Теластин, може запобігти появі помилок. Якщо змінна є еталонним типом, ініціалізація може запобігти нульовим помилкам посилання вниз по лінії.

Змінна будь-якого типу, яка не має значення за замовчуванням, займе деяку пам’ять для зберігання значення за замовчуванням.


6

Спроба використовувати неініціалізовану змінну - це завжди помилка, тому має сенс мінімізувати ймовірність виникнення цієї помилки.

Напевно, найпоширенішими мовами програмування підходу для усунення проблеми є автоматична ініціалізація до значення за замовчуванням, тому принаймні, якщо ви забудете ініціалізувати змінну, це буде щось на зразок 0чогось подібного 0x16615c4b.

Це вирішує великий відсоток помилок, якщо вам все-таки потрібна змінна ініціалізована до нуля. Однак використання змінної, яка була ініціалізована на неправильне значення, так само погано, як і використання такої, яка взагалі не була ініціалізована. Насправді іноді це може бути і гірше, адже помилка може бути більш тонкою і важко виявити.

Функціональні мови програмування вирішують цю проблему не лише забороняючи неініціалізовані значення, але й взагалі забороняючи перепризначення. Це усуває проблему і виявляється не таким серйозним обмеженням, як ви могли б подумати. Навіть у нефункціональних мовах, якщо ви зачекаєте оголосити змінну, поки у вас не буде правильного значення для її ініціалізації, ваш код, як правило, набагато надійніший.

Що стосується продуктивності, це, мабуть, незначно. У гіршому випадку з неініціалізованими змінними у вас є одне додаткове завдання і пов'язати деяку пам'ять довше, ніж потрібно. Хороші компілятори можуть оптимізувати відмінності у багатьох випадках.

Витоки пам'яті абсолютно не пов'язані між собою, хоча правильно ініціалізовані змінні, як правило, знаходяться протягом більш короткого періоду часу, і тому програміст може дещо менше шансів випадково витікати.


Завжди? Ви маєте на увазі, що "завжди", як у "Як фіксоване повідомлення Valgrind надає OpenSSL поруч із марною" marc.info/?t=114651088900003&r=1&w=2 ? Або ви маєте на увазі інше, «майже завжди» одне?
JensG

1
Я можу придумати три мови, які дозволяють неініціалізовані змінні без помилок, одна з яких використовує такі з мовною метою.
DougM

Мене зацікавила б конкретика. Я б підозрював, що в цих випадках змінні не є справді неініціалізованими, але ініціалізуються іншим способом, ніж безпосередньо програміст на сайті декларації. Або вони присвоюються якимось непрямими способами, перш ніж їх вилучити.
Карл Білефельдт

5

Ініціалізація означає, що початкове значення має значення. Якщо початкове значення має значення, то так, явно ви повинні переконатися, що воно ініціалізовано. Якщо це не має значення, це означає, що він буде ініціалізований пізніше.

Непотрібна ініціалізація спричинює витрачені цикли процесора. Незважаючи на те, що ці витрачені цикли можуть не мати значення в певних програмах, в інших програмах кожен цикл є важливим, оскільки швидкість є головним фактором. Тому дуже важливо зрозуміти, які є цілі ефективності, і чи потрібно ініціалізувати змінні чи ні.

Витік пам’яті - це зовсім інша проблема, яка, як правило, передбачає функцію виділення пам'яті для видачі та пізніше переробки блоків пам'яті. Подумайте про поштове відділення. Ви йдете і просите поштову скриньку. Вони дають вам одне. Ви просите ще одного. Вони дають вам ще один. Правило полягає в тому, що коли ви закінчите користуватися поштовою скринькою, яку вам потрібно повернути. Якщо ви забудете повернути його, вони все ще думають, що у вас є, і коробку ніхто більше не може використовувати. Таким чином, частина пам'яті пов'язана і не використовується, і саме це називається витоком пам'яті. Якщо ви в якийсь момент будете просити коробки, у вас не вистачить пам’яті. Я це надто спростив, але це основна ідея.


-1 Ви переосмислюєте, що означає ініціалізація в цьому контексті.
Пітер Б

@ Пітер В, я не розумію ваш коментар. Будь ласка, якщо ви хочете, скажіть, як я, "перегляньте, що означає ініціалізація в цьому контексті". Дякую
Еліптичний вигляд

Читайте власне речення, це кругові міркування: "Ініціалізація означає, що початкове значення має значення. Якщо початкове значення має значення, то так, явно, ви повинні переконатися, що воно ініціалізовано. Якщо це не має значення, це означає, що воно отримає ініціалізовано пізніше. "
Пітер Б

@ Pieter B, Деякі люди ініціалізуються як загальне правило, а не з програмної причини, тобто вони ініціалізують, чи має значення початкове значення чи ні. Чи не це серце ОК: Наскільки важливо ініціалізувати змінну? У всякому разі, ви тут проголосували.
Еліптичний вигляд

2

Як казали інші, це залежить від мови. Але я продемонструю свої ідеї Java (і Ефективна Java) щодо ініціалізації змінних. Вони повинні бути використані для багатьох інших мов вищого рівня.

Константи та змінні класу

Змінні класу - позначені staticна Java - схожі на константи. Ці змінні зазвичай повинні бути остаточними та ініціалізованими безпосередньо після визначення з використанням =або з блоку ініціалізатора класу static { // initialize here }.

Поля

Як і у багатьох мовах вищого рівня та сценаріїв, полям автоматично буде призначено значення за замовчуванням. Для чисел і charце буде нульове значення. Для рядків та інших об'єктів це буде null. Зараз nullце небезпечно, і його слід використовувати економно. Таким чином, ці поля повинні бути встановлені на дійсне значення якомога швидше. Конструктор зазвичай є ідеальним місцем для цього. Щоб переконатися, що змінні встановлені під час конструктора, а потім не змінені, ви можете позначити їх finalключовим словом.

Спробуйте чинити опір прагненню використовувати nullяк якийсь прапор чи спеціальне значення. Краще, наприклад, включити конкретне поле для утримання стану. Поле з назвою, stateяке використовує значення Stateперерахування, було б хорошим вибором.

Параметри методу

Оскільки зміни значень параметрів (будь-які посилання на об'єкти або базові типи, такі як цілі числа тощо), абонент не побачить, параметри повинні бути позначені як final. Це означає, що значення самої змінної змінити неможливо. Зверніть увагу , що значення змінних примірників об'єкта може бути змінено, посилання не може бути змінено , щоб вказувати на інший об'єкт або nullж.

Локальні змінні

Локальні змінні не ініціалізуються автоматично; їх потрібно ініціалізувати до того, як їх значення можна буде використовувати. Один із способів переконатися в ініціалізації вашої змінної - це безпосередньо ініціалізувати їх до якогось значення за замовчуванням. Це, однак , що ви повинні НЕ робити. Більшу частину часу значення за замовчуванням не є значенням, яке ви очікували.

Набагато краще лише визначити змінну точно там, де вам потрібна змінна. Якщо змінна повинна приймати лише одне значення (що справедливо для більшості змінних у хорошому коді), ви можете позначити змінну final. Це гарантує, що локальна змінна призначається рівно один раз, а не нуль разів або два рази. Приклад:

public static doMethod(final int x) {
    final int y; // no assignment yet, it's final so it *must* be assigned
    if (x < 0) {
        y = 0;
    } else if (x > 0) {
        y = x;
    } else {
        // do nothing <- error, y not assigned if x = 0
        // throwing an exception here is acceptable though
    }
}

Зауважте, що багато мов попереджають вас, якщо змінна залишається неініціалізованою перед використанням. Перевірте мовні специфікації та форуми, щоб побачити, чи не турбуєтесь ви непотрібно.


1

З неініціалізаційними змінними не виникає проблем.

Проблема полягає лише тоді, коли ви читаєте змінну, яка ще не була записана.

Залежно від компілятора та / або від типу змінної, ініціалізація виконується при запуску програми. Чи ні.

Зазвичай це не покладатися на автоматичну ініціалізацію.


0

Ініціалізація змінних (неявно або явно) має вирішальне значення. Не ініціалізація змінної завжди є помилкою (однак вони можуть бути ініціалізовані неявно, проте див. Нижче). Сучасні компілятори, такі як компілятор C # (як приклад), трактують це як помилку і не дозволяють виконувати код. Неініціалізована змінна просто марно і шкідливо. Якщо ви не створюєте генератор випадкових чисел, ви очікуєте, що від фрагмента коду вийде детермінований і відтворюваний результат. Цього можна досягти лише в тому випадку, якщо ви почнете працювати з ініціалізованими змінними.

Дійсно цікаве питання полягає в тому, чи змінна ініціалізується автоматично, чи потрібно робити це вручну. Це залежить від мови, що використовується. Наприклад, у C # поля, тобто "змінні" на рівні класу, завжди автоматично ініціалізуються до значення за замовчуванням для цього типу змінної default(T). Це значення відповідає бітовій схемі, що складається з усіх нулів. Це частина мовної специфікації, а не лише технічна деталь реалізації мови. Тому можна сміливо покладатися на це. Безпечно не ініціалізувати змінну явно, якщо (і лише якщо) мовна специфікація зазначає, що вона ініціалізується неявно.Якщо ви хочете інше значення, ви повинні явно ініціалізувати змінну. Однак; у C # локальні змінні, тобто змінні, оголошені методами, не ініціалізуються автоматично, і ви завжди повинні ініціалізувати змінну явно.


2
це не конкретне питання C #.
DougM

@DougM: Я знаю. Це не конкретна відповідь на C #, я просто взяв C # як приклад.
Олів'є Якот-Дескомбс

Не всі мови вимагають явної ініціалізації змінних. Ваше твердження "не ініціалізація - це завжди помилка" - помилкове, і явно не додає ясності до питання, яке існує. ви можете переглянути свою відповідь.
DougM

@DougM: Ви переглядали моє речення "Насправді цікавим питанням є те, чи змінна ініціалізована автоматично, чи потрібно робити це вручну?"
Олів'є Якот-Дескомб

ти маєш на увазі того, що похований на півдорозі посеред абзацу? так. Ви мусили зробити це більш помітним і додати кваліфікацію до заявки "завжди".
DougM
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.