Werden Delphi-Variablen standardmäßig mit einem Wert initialisiert?

103

Ich bin neu in Delphi und habe einige Tests durchgeführt, um festzustellen, auf welche Objektvariablen und Stapelvariablen standardmäßig initialisiert wird:

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

Dies ist das Verhalten, das ich von anderen Sprachen gewohnt bin, aber ich frage mich, ob es sicher ist, sich in Delphi darauf zu verlassen? Ich frage mich beispielsweise, ob dies möglicherweise von einer Compilereinstellung abhängt oder auf verschiedenen Computern unterschiedlich funktioniert. Ist es normal, sich auf initialisierte Standardwerte für Objekte zu verlassen, oder legen Sie explizit alle Instanzvariablen im Konstruktor fest?

Was Stapelvariablen (Prozedurebene) betrifft, zeigen meine Tests, dass unitialisierte Boolesche Werte wahr sind, unitialisierte Ganzzahlen 2129993264 sind und nicht initialisierte Objekte nur ungültige Zeiger sind (dh nicht Null). Ich vermute, die Norm ist, immer Variablen auf Prozedurebene festzulegen, bevor auf sie zugegriffen wird.

MB.
quelle
3
Zwei Hinweise: 1. Datensätze werden nicht initialisiert. 2. Referenzzählvariablen werden immer initialisiert. !ABER! In einer Funktion, die eine Zeichenfolge zurückgibt, wird 'Ergebnis' nicht wie erwartet als leere Zeichenfolge initialisiert. Dies liegt daran, dass 'Ergebnis' keine lokale Variable ist. Also immer tun: Ergebnis: = '';
InTheNameOfScience
Beantwortet das deine Frage? Welche Variablen werden in Delphi initialisiert?
InTheNameOfScience

Antworten:

105

Ja, dies ist das dokumentierte Verhalten:

  • Objektfelder werden immer mit 0, 0.0, '', False, nil oder was auch immer initialisiert initialisiert.

  • Globale Variablen werden immer auch auf 0 usw. initialisiert.

  • Lokale referenzgezählte * Variablen werden immer mit nil oder '' initialisiert.

  • Lokale nicht referenzgezählte * Variablen sind nicht initialisiert, daher müssen Sie einen Wert zuweisen, bevor Sie sie verwenden können.

Ich erinnere mich, dass Barry Kelly irgendwo eine Definition für "referenzgezählt" geschrieben hat, diese aber nicht mehr finden kann. Das sollte in der Zwischenzeit so sein:

referenzgezählt ==, die selbst referenzgezählt sind oder direkt oder indirekt Felder (für Datensätze) oder Elemente (für Arrays) enthalten, die referenzgezählt sind wie: string, variant, interface oder dynamisches Array oder statisches Array, das solche Typen enthält.

Anmerkungen:

  • record selbst reicht nicht aus, um als Referenz gezählt zu werden
  • Ich habe dies noch nicht mit Generika versucht
Giacomo Degli Esposti
quelle
2
Wie Giacomo in den Kommentaren unten hervorhob, wird dies alles in den Delphi-Hilfedateien unter ms-help erklärt: //borland.bds4/bds4ref/html/Variables.htm. In Delphi 2009 habe ich die gleichen Informationen gefunden, indem ich in der Hilfe nach "Variablen" gesucht habe (komischerweise habe ich viele Suchvorgänge versucht, aber ich habe nicht daran gedacht, diese zu versuchen).
MB.
8
Lokale Variablen werden initialisiert ($ 0), wenn sie von einem verwalteten Typ wie Zeichenfolgen, Schnittstellen, dynamischen Arrays oder Varianten sind
Francesca
5
Es gibt jedoch eine Ausnahme! Wenn Sie den Konstruktor überschreiben und den geerbten Konstruktor nicht aufrufen, besteht die Möglichkeit, dass einige Felder nicht initialisiert werden! (Insbesondere bei älteren Delphi-Versionen.) Da TObject.Create dafür verantwortlich ist, alle Daten auf Null zu setzen, führt dies nicht zu möglichen unbekannten Daten.
Wim ten Brink
18
@ WimtenBrink Ich denke du liegst falsch. Die Initialisierung erfolgt nicht innerhalb TObject.Createeiner ungültigen Methode, sondern class function TObject.InitInstance(Instance: Pointer): TObject;wird IMMER vor jedem Konstruktoraufruf aufgerufen, selbst bei älteren Delphi-Versionen. Ihr Kommentar ist meiner Meinung nach falsch und verwirrend.
Arnaud Bouchez
7
Vergessen Sie nicht, dass in einer Funktion, die eine Zeichenfolge zurückgibt, 'Ergebnis' nicht wie erwartet mit einer leeren Zeichenfolge initialisiert wird. Dies liegt daran, dass 'Ergebnis' keine lokale Variable ist.
InTheNameOfScience
27

Globale Variablen ohne expliziten Initialisierer werden im BSS-Abschnitt in der ausführbaren Datei zugewiesen. Sie nehmen in der EXE eigentlich keinen Platz ein. Der BSS-Abschnitt ist ein spezieller Abschnitt, den das Betriebssystem zuweist und auf Null löscht. Auf anderen Betriebssystemen gibt es ähnliche Mechanismen.

Sie können sich darauf verlassen, dass globale Variablen auf Null initialisiert werden.

Barry Kelly
quelle
21

Klassenfelder sind standardmäßig Null. Dies ist dokumentiert, damit Sie sich darauf verlassen können. Lokale Stapelvariablen sind undefiniert, es sei denn, Zeichenfolge oder Schnittstelle werden auf Null gesetzt.

Martin Liesén
quelle
Vielen Dank. "Zero" verwirrt mich ein bisschen - bedeutet das, dass Strings '' und Interfaces Null sind?
MB.
4
Ja genau das. nil = 0 (auf Assembler-Ebene) und '' = nil (Delphi-Konvention).
gabr
1
"es sei denn, Zeichenfolge oder Schnittstelle" ist keine vollständige Beschreibung der Realität. Beispielsweise werden auch dynamische Arrays initialisiert. Im Allgemeinen gilt die Regel, dass Variablen von verwalteten (referenzgezählten) Typen initialisiert werden, auch wenn sie lokal sind.
Andreas Rejbrand
16

Nur als Randnotiz (da Sie Delphi noch nicht kennen): Globale Variablen können direkt beim Deklarieren initialisiert werden:

var myGlobal:integer=99;
Heinrich Ulbricht
quelle
2
Seit 10.3 gilt das Gleiche für lokale Variablen
Edijs Kolesnikovičs
1
Und wenn dies nicht explizit erfolgt, werden sie auf 0, 0.0, False, nil, [] usw. initialisiert.
Andreas Rejbrand
7

Hier ist ein Zitat von Ray Lischners Delphi in Kürze, Kapitel 2

"Wenn Delphi zum ersten Mal ein Objekt erstellt, beginnen alle Felder leer, dh Zeiger werden auf Null initialisiert, Zeichenfolgen und dynamische Arrays sind leer, Zahlen haben den Wert Null, Boolesche Felder sind Falsch und Varianten werden auf Nicht zugewiesen gesetzt. (Weitere Informationen finden Sie unter NewInstance und InitInstance in Kapitel 5.) "

Es ist wahr, dass Variablen im lokalen Bereich initialisiert werden müssen ... Ich würde den obigen Kommentar, dass "Globale Variablen initialisiert werden", als zweifelhaft behandeln, bis sie mit einer Referenz versehen werden - das glaube ich nicht.

edit ... Barry Kelly sagt, dass Sie sich darauf verlassen können, dass sie auf Null initialisiert sind, und da er im Delphi-Compiler-Team ist, glaube ich, dass das so ist :) Danke Barry.

Drew Gibson
quelle
1
In der delphi 2006-Hilfe finden Sie sie hier: ms-help: //borland.bds4/bds4ref/html/Variables.htm "Wenn Sie eine globale Variable nicht explizit initialisieren, initialisiert der Compiler sie auf 0. Objektinstanzdaten ( Felder) werden ebenfalls auf 0 initialisiert. "
Giacomo Degli Esposti
Wegen "Ich glaube das nicht" abgelehnt. Dies ist Programmierung, keine Religion. Und Giacomo hat gerade die Wahrheit gezeigt.
InTheNameOfScience
6

Globale Variablen und Objektinstanzdaten (Felder) werden immer auf Null initialisiert. Lokale Variablen in Prozeduren und Methoden werden in Win32 Delphi nicht initialisiert. Ihr Inhalt ist undefiniert, bis Sie ihnen einen Wert im Code zuweisen.

Ondrej Kelle
quelle
5

Selbst wenn eine Sprache Standardinitialisierungen bietet, sollten Sie sich meiner Meinung nach nicht darauf verlassen. Das Initialisieren auf einen Wert macht es anderen Entwicklern viel klarer, die möglicherweise nichts über Standardinitialisierungen in der Sprache wissen, und verhindert Probleme zwischen Compilern.

Thomas Owens
quelle
4
Natürlich kannst du. Und du solltest. Es ist einfach unnötig, in jedem Konstruktor alles auf 0 / '' / false / nil zu initialisieren. Das Initialisieren globaler Variablen ist dagegen nicht so dumm - ich kann mich ausnahmsweise nie erinnern, ob sie initialisiert wurden oder nicht (da ich sie nicht oft benutze).
gabr
2
Wenn Sie in Delphi eine Variable an derselben Stelle initialisieren können, an der Sie sie deklarieren (z. B. var fObject: TObject = nil), würde ich gerne zustimmen, dass die Initialisierung auf einen Wert wahrscheinlich eine gute Idee ist. Aber für mich scheint es ein bisschen viel zu sein, dies im Konstruktor für jedes Objektfeld zu tun.
MB.
4

Aus der Delphi 2007-Hilfedatei:

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

"Wenn Sie eine globale Variable nicht explizit initialisieren, initialisiert der Compiler sie auf 0."

Ondrej Kelle
quelle
3

Ich habe einen kleinen Kritikpunkt mit den gegebenen Antworten. Delphi setzt den Speicherplatz der Globals und der neu erstellten Objekte auf Null. Während dies NORMAL bedeutet, dass sie initialisiert werden, gibt es einen Fall, in dem dies nicht der Fall ist: Aufzählungstypen mit bestimmten Werten. Was ist, wenn Null kein legaler Wert ist?

Loren Pechtel
quelle
1
Null ist immer ein gesetzlicher Wert, es ist der 1. Wert der Aufzählung. Sie können es mit ord (MyFirstEnumValue) sehen.
Francesca
Es würde den ersten Wert im Aufzählungstyp zurückgeben.
Skamradt
6
Null ist nicht immer ein zulässiger Wert, wenn Sie der Aufzählung explizit Werte zuweisen. In diesem Fall wird es immer noch auf 0 initialisiert, und Sie haben einen unzulässigen Wert. Aber Enums sind nur syntaktischer Zucker, der über normale Integer-Typen gemalt wird, also bricht dies nichts wirklich. Stellen Sie sicher, dass Ihr Code damit umgehen kann.
Mason Wheeler
2
@ François: Nicht, wenn Sie Ihre Aufzählung so definieren:TOneTwoThree = (One=1, Two=2, Three=3);
fnkr
0

Neu eingeführte (seit Delphi 10.3) Inline-Variablen erleichtern die Steuerung von Anfangswerten.

procedure TestInlineVariable;
begin
  var index: Integer := 345;
  ShowMessage(index.ToString);
end;
Jacek Krawczyk
quelle