Инициализируются ли переменные 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 и т. Д .;

  • Локальные переменные с подсчетом ссылок * всегда инициализируются значением nil или "";

  • Локальные переменные без подсчета ссылок * не инициализируются, поэтому вам необходимо присвоить значение, прежде чем вы сможете их использовать.

Я помню, что Барри Келли где-то написал определение для «подсчитываемых по ссылкам», но больше не могу его найти, так что пока что это нужно делать:

с подсчетом ссылок ==, которые сами подсчитываются по ссылкам или прямо или косвенно содержат поля (для записей) или элементы (для массивов) с подсчетом ссылок, например: string, variant, interface или динамический массив или статический массив, содержащий такие типы.

Ноты:

  • record сам по себе недостаточно, чтобы стать подсчитываемым
  • Я еще не пробовал это с дженериками
Джакомо Дельи Эспости
источник
2
Как отметил Джакомо в комментариях ниже, все это объясняется в файлах справки Delphi по адресу ms-help: //borland.bds4/bds4ref/html/Variables.htm. В Delphi 2009 я нашел ту же информацию, выполнив поиск в справке по «переменным» (как ни странно, я пробовал много поисков, но я не подумал попробовать это).
МБ.
8
Локальные переменные Инициализируются ($ 0), если они относятся к управляемому типу, например, к строкам, интерфейсам, динамическим массивам или вариантам
Франческа
5
Но есть исключение! Когда вы переопределяете конструктор и не вызываете унаследованный конструктор, есть вероятность, что некоторые поля окажутся неинициализированными! (Особенно со старыми версиями Delphi.) Поскольку TObject.Create отвечает за обнуление всех данных, не вызывая его, вы можете получить неизвестные данные.
Wim ten Brink
18
@WimtenBrink Я думаю, вы ошибаетесь. Инициализация не выполняется внутри TObject.Create, что является недействительным методом, но в class function TObject.InitInstance(Instance: Pointer): TObject;котором ВСЕГДА вызывается перед любым вызовом конструктора, даже для более старых версий Delphi. Ваш комментарий ИМХО неправильный и запутанный.
Арно Буше
7
Не забывайте, что в функции, возвращающей строку, «Результат» не инициализируется пустой строкой, как вы могли ожидать. Это потому, что «Результат» не является локальной переменной.
InTheNameOfScience
27

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

Вы можете полагаться на то, что глобальные переменные инициализируются нулем.

Барри Келли
источник
21

Поля класса по умолчанию равны нулю. Это задокументировано, так что вы можете на это положиться. Переменные локального стека не определены, если только строка или интерфейс не установлены в ноль.

Мартин Лизен
источник
Спасибо. "Ноль" меня немного сбивает с толку - означает ли это, что строки равны "", а интерфейсы равны нулю?
МБ.
4
Да, именно так. nil = 0 (на уровне ассемблера) и '' = nil (соглашение Delphi).
gabr
1
«Если строка или интерфейс» не является полным описанием реальности. Например, динамические массивы также инициализируются. В более общем смысле правило состоит в том, что переменные управляемых (подсчитываемых по ссылкам) типов инициализируются, даже если они локальны.
Андреас Рейбранд
16

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

var myGlobal:integer=99;
Генрих Ульбрихт
источник
2
Начиная с версии 10.3, то же самое относится и к локальным переменным
Эдийс Колесникович
1
И если это не сделано явно, они инициализируются значениями 0, 0.0, False, nil, [] и т. Д.
Андреас Рейбранд
7

Вот цитата из Рэя Лишнерса Delphi в двух словах, глава 2

«Когда Delphi впервые создает объект, все поля начинаются пустыми, то есть указатели инициализируются значением nil, строки и динамические массивы пусты, числа имеют нулевое значение, логические поля имеют значение False, а для вариантов установлено значение Unassigned. (Подробнее см. NewInstance и InitInstance в главе 5.) "

Это правда, что локальные переменные в области видимости должны быть инициализированы ... Я бы рассматривал комментарий выше, что «Глобальные переменные инициализируются» как сомнительный, пока не будет предоставлена ​​ссылка - я в это не верю.

edit ... Барри Келли говорит, что вы можете рассчитывать на то, что они инициализированы нулями, и, поскольку он работает в команде компиляторов Delphi, я считаю, что это так :) Спасибо, Барри.

Дрю Гибсон
источник
1
В справке по delphi 2006 вы можете найти ее здесь: ms-help: //borland.bds4/bds4ref/html/Variables.htm «Если вы явно не инициализируете глобальную переменную, компилятор инициализирует ее значением 0. Данные экземпляра объекта ( поля) также инициализируются значением 0. "
Джакомо Дегли Эспости,
Проголосовали против, потому что "Я этому не верю". Это программирование, а не религия. А Джакомо только что продемонстрировал правду.
InTheNameOfScience,
6

Глобальные переменные и данные экземпляра объекта (поля) всегда инициализируются нулем. Локальные переменные в процедурах и методах не инициализируются в Win32 Delphi; их содержание не определено, пока вы не присвоите им значение в коде.

Ондрей Келле
источник
5

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

Томас Оуэнс
источник
4
Конечно вы можете. И ты должен. Инициализировать все до 0 / '' / false / nil в каждом конструкторе просто не нужно. С другой стороны, инициализация глобальных переменных не так уж глупа - я никогда не могу вспомнить, инициализированы они или нет (так как я их не использую).
gabr
2
Если Delphi позволяет вам инициализировать переменную в том же месте, где вы ее объявляете (например, var fObject: TObject = nil), я был бы склонен согласиться с тем, что инициализация значением, вероятно, является хорошей идеей. Но мне кажется, что делать это в конструкторе для каждого поля объекта - это непросто.
МБ.
4

Из файла справки Delphi 2007:

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

«Если вы явно не инициализируете глобальную переменную, компилятор инициализирует ее значением 0.»

Ондрей Келле
источник
3

У меня есть одна небольшая претензия к полученным ответам. Delphi обнуляет пространство памяти глобальных переменных и вновь созданных объектов. Хотя это НОРМАЛЬНО означает, что они инициализированы, в одном случае это не так: перечислимые типы с определенными значениями. Что, если ноль не является допустимым значением?

Лорен Пехтель
источник
1
Ноль всегда допустимое значение, это 1-е значение перечисления. вы можете увидеть это с помощью ord (MyFirstEnumValue).
Francesca
Он вернет первое значение перечислимого типа.
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;
Яцек Кравчик
источник