Наскільки важливо ініціалізувати змінні?
Чи належна ініціалізація дозволяє уникнути витоку пам’яті чи має переваги у роботі?
null
) загальними компіляторами, але є випадковим сміттям при компілюванні для випуску. (хоча мої знання C ++
Наскільки важливо ініціалізувати змінні?
Чи належна ініціалізація дозволяє уникнути витоку пам’яті чи має переваги у роботі?
null
) загальними компіляторами, але є випадковим сміттям при компілюванні для випуску. (хоча мої знання C ++
Відповіді:
Неініціалізовані змінні роблять програму недетермінованою. Кожен раз, коли програма працює, вона може вести себе по-різному. Непов'язані зміни в операційному середовищі, часу доби, фазі Місяця та перестановках таких впливають на те, як і коли проявляються ці демони. Програма може запускатися мільйон разів до появи дефекту, вони можуть робити це кожен раз або виконувати ще один мільйон. Багато проблем ставляться до "збоїв" і ігноруються, або ж повідомлення про дефекти клієнтів, закритих як "Невідтворювані". Як часто ви перезавантажували машину, щоб "виправити" проблему? Як часто ви говорили клієнтові "Ніколи не бачив, щоб це сталося, дайте мені знати, якщо ви побачите це знову" - сподіваючись (знаючи), добре вони не стануть!
Оскільки відтворення дефекту може бути поруч із неможливим у тестовому середовищі, його наступне неможливо знайти та виправити.
Це може зайняти роки, коли помилка вийде на поверхню, як правило, у коді вважається надійною та стабільною. Припускається, що дефект є в більш пізньому коді - відстеження його може зайняти значно більше часу. Зміна компілятора, перемикач компілятора, навіть додавання рядка коду можуть змінити поведінку.
Ініціалізація змінних має величезну перевагу в продуктивності не лише тому, що програма, яка працює правильно, нескінченно швидша, ніж така, яка цього не робить, але розробники витрачають менше часу на пошук та виправлення дефектів, яких не повинно бути там, і більше часу роблять «справжню» роботу.
Інша істотна перевага ініціальних змінних полягає в тому, що оригінальний автор коду повинен вирішити, до чого їх ініціалізувати. Це не завжди тривіальна вправа, а коли не банальна, може бути свідченням поганої конструкції.
Витік пам’яті - це інша проблема, але правильна ініціалізація може не лише допомогти у їх запобіганні, але також може допомогти у їх виявленні та пошуку джерела - його сильно залежної від мови, і це справді окреме питання, гідне подальшого вивчення, ніж я в змозі дати у цій відповіді.
Редагування: У деяких мовах (наприклад, C #) неможливо використовувати неініціалізовані змінні, оскільки програма не буде компілювати або повідомляти про помилку при виконанні, якщо це зроблено. Однак у багатьох мовах з цими характеристиками є інтерфейси для потенційно небезпечного коду, тому при використанні таких інтерфейсів слід бути обережними, не вводити неініціалізовані змінні.
Ініціалізація змінної, як вказувала Теластин, може запобігти появі помилок. Якщо змінна є еталонним типом, ініціалізація може запобігти нульовим помилкам посилання вниз по лінії.
Змінна будь-якого типу, яка не має значення за замовчуванням, займе деяку пам’ять для зберігання значення за замовчуванням.
Спроба використовувати неініціалізовану змінну - це завжди помилка, тому має сенс мінімізувати ймовірність виникнення цієї помилки.
Напевно, найпоширенішими мовами програмування підходу для усунення проблеми є автоматична ініціалізація до значення за замовчуванням, тому принаймні, якщо ви забудете ініціалізувати змінну, це буде щось на зразок 0
чогось подібного 0x16615c4b
.
Це вирішує великий відсоток помилок, якщо вам все-таки потрібна змінна ініціалізована до нуля. Однак використання змінної, яка була ініціалізована на неправильне значення, так само погано, як і використання такої, яка взагалі не була ініціалізована. Насправді іноді це може бути і гірше, адже помилка може бути більш тонкою і важко виявити.
Функціональні мови програмування вирішують цю проблему не лише забороняючи неініціалізовані значення, але й взагалі забороняючи перепризначення. Це усуває проблему і виявляється не таким серйозним обмеженням, як ви могли б подумати. Навіть у нефункціональних мовах, якщо ви зачекаєте оголосити змінну, поки у вас не буде правильного значення для її ініціалізації, ваш код, як правило, набагато надійніший.
Що стосується продуктивності, це, мабуть, незначно. У гіршому випадку з неініціалізованими змінними у вас є одне додаткове завдання і пов'язати деяку пам'ять довше, ніж потрібно. Хороші компілятори можуть оптимізувати відмінності у багатьох випадках.
Витоки пам'яті абсолютно не пов'язані між собою, хоча правильно ініціалізовані змінні, як правило, знаходяться протягом більш короткого періоду часу, і тому програміст може дещо менше шансів випадково витікати.
Ініціалізація означає, що початкове значення має значення. Якщо початкове значення має значення, то так, явно ви повинні переконатися, що воно ініціалізовано. Якщо це не має значення, це означає, що він буде ініціалізований пізніше.
Непотрібна ініціалізація спричинює витрачені цикли процесора. Незважаючи на те, що ці витрачені цикли можуть не мати значення в певних програмах, в інших програмах кожен цикл є важливим, оскільки швидкість є головним фактором. Тому дуже важливо зрозуміти, які є цілі ефективності, і чи потрібно ініціалізувати змінні чи ні.
Витік пам’яті - це зовсім інша проблема, яка, як правило, передбачає функцію виділення пам'яті для видачі та пізніше переробки блоків пам'яті. Подумайте про поштове відділення. Ви йдете і просите поштову скриньку. Вони дають вам одне. Ви просите ще одного. Вони дають вам ще один. Правило полягає в тому, що коли ви закінчите користуватися поштовою скринькою, яку вам потрібно повернути. Якщо ви забудете повернути його, вони все ще думають, що у вас є, і коробку ніхто більше не може використовувати. Таким чином, частина пам'яті пов'язана і не використовується, і саме це називається витоком пам'яті. Якщо ви в якийсь момент будете просити коробки, у вас не вистачить пам’яті. Я це надто спростив, але це основна ідея.
Як казали інші, це залежить від мови. Але я продемонструю свої ідеї 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
}
}
Зауважте, що багато мов попереджають вас, якщо змінна залишається неініціалізованою перед використанням. Перевірте мовні специфікації та форуми, щоб побачити, чи не турбуєтесь ви непотрібно.
З неініціалізаційними змінними не виникає проблем.
Проблема полягає лише тоді, коли ви читаєте змінну, яка ще не була записана.
Залежно від компілятора та / або від типу змінної, ініціалізація виконується при запуску програми. Чи ні.
Зазвичай це не покладатися на автоматичну ініціалізацію.
Ініціалізація змінних (неявно або явно) має вирішальне значення. Не ініціалізація змінної завжди є помилкою (однак вони можуть бути ініціалізовані неявно, проте див. Нижче). Сучасні компілятори, такі як компілятор C # (як приклад), трактують це як помилку і не дозволяють виконувати код. Неініціалізована змінна просто марно і шкідливо. Якщо ви не створюєте генератор випадкових чисел, ви очікуєте, що від фрагмента коду вийде детермінований і відтворюваний результат. Цього можна досягти лише в тому випадку, якщо ви почнете працювати з ініціалізованими змінними.
Дійсно цікаве питання полягає в тому, чи змінна ініціалізується автоматично, чи потрібно робити це вручну. Це залежить від мови, що використовується. Наприклад, у C # поля, тобто "змінні" на рівні класу, завжди автоматично ініціалізуються до значення за замовчуванням для цього типу змінної default(T)
. Це значення відповідає бітовій схемі, що складається з усіх нулів. Це частина мовної специфікації, а не лише технічна деталь реалізації мови. Тому можна сміливо покладатися на це. Безпечно не ініціалізувати змінну явно, якщо (і лише якщо) мовна специфікація зазначає, що вона ініціалізується неявно.Якщо ви хочете інше значення, ви повинні явно ініціалізувати змінну. Однак; у C # локальні змінні, тобто змінні, оголошені методами, не ініціалізуються автоматично, і ви завжди повинні ініціалізувати змінну явно.