Чи ініціалізовані змінні delphi зі значенням за замовчуванням?


103

Я новачок у Delphi, і я проводив кілька тестів, щоб побачити, до яких об’єктних змінних та стекових змінних ініціалізовано за замовчуванням:

TInstanceVariables = class
  fBoolean: boolean; // always starts off as false
  fInteger: integer; // always starts off as zero
  fObject: TObject; // always starts off as nil
end;

Це поведінка, до якої я звик з інших мов, але мені цікаво, чи безпечно на неї покладатися в Delphi? Наприклад, мені цікаво, чи це може залежати від налаштування компілятора, чи може працювати по-іншому на різних машинах. Чи нормально покладатися на ініціалізовані за замовчуванням значення для об'єктів, або ви чітко встановлюєте всі змінні екземпляра в конструкторі?

Що стосується змінних стеків (рівня процедури), то мої тести показують, що неітіалізовані булеві знаки є істинними, неіціалізовані цілі числа - 2129993264, а неініалізовані об'єкти - просто недійсні покажчики (тобто не нульові). Я здогадуюсь, норма - це завжди встановлювати змінні на рівні процедури, перш ніж звертатися до них?


3
Дві примітки: 1. Записи не ініціалізуються. 2. Посилальні підраховані змінні завжди ініціалізуються. АЛЕ! у функції, що повертає рядок, "Результат" не ініціалізується в порожній рядок, як ви могли очікувати. Це тому, що "Результат" не є локальним варіантом. Отже, завжди робіть: Результат: = '';
InTheNameOfScience


Чи відповідає це на ваше запитання? Які змінні ініціалізуються, коли в Delphi?
InTheNameOfScience

Відповіді:


105

Так, це документально підтверджена поведінка:

  • Поля об’єкта завжди ініціалізуються на 0, 0.0, '', False, nil або все, що стосується.

  • Глобальні змінні також завжди ініціалізуються на 0 тощо;

  • Локальні перелічені * змінні завжди ініціалізуються до нуля або '';

  • Локальні невідраховані * змінні * неініціалізовані, тому вам доведеться призначити значення, перш ніж ви зможете їх використовувати.

Я пам'ятаю, що Баррі Келлі десь написав визначення для "посилається на посилання", але більше не може його знайти, тому це слід робити тим часом:

reference-count ==, які самі посилаються, або прямо чи опосередковано містять поля (для записів) або елементи (для масивів), які посилаються на облік як: string, variant, interface або динамічний масив або статичний масив, що містить такі типи.

Примітки:

  • record само по собі недостатньо, щоб стати посиланням
  • Я ще не пробував цього з дженериками

2
Як зазначив Джакомо в коментарях нижче, це все пояснено у файлах довідки Delphi за адресою ms-help: //borland.bds4/bds4ref/html/Variables.htm. У Delphi 2009 я виявив ту саму інформацію, шукаючи довідку щодо "змінних" (досить смішно, я спробував багато пошукових запитів, але не думав пробувати цю).
МБ.

8
Локальні змінні ініціалізуються (0 дол. США), якщо вони керованого типу, такі як рядки, інтерфейси, динамічні масиви чи варіанти
Francesca

5
Хоча є виняток! Якщо ви заміните конструктор і не викликаєте успадкований конструктор, є ймовірність, що деякі поля в кінцевому підсумку виявляться неініціалізованими! (Особливо для старих версій Delphi.) Оскільки TObject.Create відповідає за нульову передачу всіх даних, не викликаючи, що це призводить до можливих невідомих даних.
Wim ten Brink

18
@WimtenBrink Я думаю, ви помиляєтесь. Ініціалізація не робиться всередині TObject.Create, що є недійсним методом, але в class function TObject.InitInstance(Instance: Pointer): TObject;якому ЗАВЖДИ викликається перед будь-яким викликом конструктора, навіть для старих версій Delphi. Ваш коментар є неправильним та заплутаним в IMHO.
Арно Бушез

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

27

Глобальні змінні, які не мають явного ініціалізатора, виділяються у розділі BSS у виконуваному файлі. Вони фактично не займають жодного місця в EXE; розділ BSS - це спеціальний розділ, який ОС виділяє і очищає до нуля. На інших операційних системах існують подібні механізми.

Ви можете залежати від ініціалізації глобальних змінних.


21

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


Дякую. "Нуль" мене трохи бентежить - це означає, що рядки є '', а інтерфейси нульові?
МБ.

4
Так, саме так. nil = 0 (на рівні асемблера) і '' = nil (умова Delphi).
габ.

1
"якщо рядок або інтерфейс" не є повним описом реальності. Наприклад, динамічні масиви також ініціалізуються. Загалом, правило полягає в тому, що змінні керованих (посилаються на число) типів ініціалізуються, навіть якщо локальні.
Андреас Рейбранд

16

Так само як побічна примітка (як ви новачок у Delphi): глобальні змінні можуть бути ініціалізовані безпосередньо при їх оголошенні:

var myGlobal:integer=99;

2
Оскільки 10.3 те саме стосується локальних змінних
Edijs Kolesnikovičs

1
Якщо це не зроблено явно, вони ініціалізуються на 0, 0.0, False, nil, [] тощо.
Andreas Rejbrand

7

Ось цитата Рея Лішнерса Дельфі в горішковій главі 2

"Коли Delphi вперше створює об'єкт, усі поля починаються порожніми, тобто вказівники ініціалізуються до нуля, рядки та динамічні масиви порожні, числа мають значення нуля, булеві поля - помилкові, а варіанти - непризначені. (Докладні відомості див. У розділі NewInstance та InitInstance у розділі 5.) "

Це правда, що мінливі локальні змінні потрібно ініціалізувати ... Я б вважав коментар вище, що "Глобальні змінні ініціалізуються" як сумнівні, поки не отримали посилання - я не вірю в це.

редагувати ... Баррі Келлі каже, що ви можете залежати від того, щоб вони були нульовими ініціалізованими, а оскільки він є в команді компілятора Delphi, я вважаю, що це стоїть :) Дякую Баррі.


1
У довідці delphi 2006 ви можете знайти його тут: ms-help: //borland.bds4/bds4ref/html/Variables.htm "Якщо явно не ініціалізувати глобальну змінну, компілятор ініціалізує її до 0. Дані екземпляра об'єкта ( поля) також ініціалізовано до 0. "
Джакомо Деглі Еспості,

Оскаржений через "я не вірю в це". Це програмування, а не релігія. І Джакомо просто продемонстрував правду.
InTheNameOfScience

6

Глобальні змінні та дані екземпляра об'єкта (поля) завжди ініціалізуються до нуля. Локальні змінні в процедурах і методах не ініціалізуються в Win32 Delphi; їх вміст не визначений, поки ви не призначите їм значення в коді.


5

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


4
Звичайно, ви можете. І ти повинен. Ініціалізувати все до 0 / '' / false / nil у кожному конструкторі просто непотрібно. Ініціалізація глобальних змінних, з іншого боку, не така дурна - я ніколи не можу згадати, чи вони ініціалізовані чи ні (так як я їх не дуже використовую).
габ.

2
Якщо Delphi дозволить вам ініціалізувати змінну в ту саму точку, коли ви її оголосили (наприклад, var fObject: TObject = nil), я схилюсь би погодитися, що ініціалізація до значення, ймовірно, є хорошою ідеєю. Але мені здається небагато зробити це в конструкторі для кожного об'єктного поля.
МБ.

4

З довідкового файлу Delphi 2007:

ms-help: //borland.bds5/devcommon/variables_xml.html

"Якщо ви явно не ініціалізуєте глобальну змінну, компілятор ініціалізує її до 0."


3

У мене є одна маленька хватка з наданими відповідями. Delphi нулі виділяє простір пам’яті глобалів та новостворених об’єктів. Хоча це НОРМАЛЬНО означає, що вони ініціалізовані, є один випадок, коли їх немає: перелічені типи з конкретними значеннями. Що робити, якщо нуль не є юридичною цінністю ??


1
Нуль - це завжди юридичні цінності, це 1-е значення перерахунку. ви можете бачити його з ord (MyFirstEnumValue).
Франческа

Це поверне перше значення в переліченому типі.
skamradt

6
Нуль не завжди є юридичним значенням, якщо ви явно присвоюєте значення переліченню. У такому випадку вона все ще ініціалізується на 0, і ви маєте незаконне значення. Але перерахунки - це лише синтаксичний цукор, пофарбований на звичайні цілі типи, тому це насправді нічого не порушує. Переконайтеся, що ваш код може впоратися з цим.
Мейсон Уілер

2
@ François: Якщо ви не визначите перерахунок так:TOneTwoThree = (One=1, Two=2, Three=3);
fnkr

0

Щойно введені (починаючи з Delphi 10.3) вбудовані змінні полегшують управління початковими значеннями.

procedure TestInlineVariable;
begin
  var index: Integer := 345;
  ShowMessage(index.ToString);
end;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.