Czy zmienne delphi są domyślnie inicjowane wartością?


103

Jestem nowy w Delphi i przeprowadziłem kilka testów, aby zobaczyć, jakie zmienne obiektu i zmienne stosu są domyślnie inicjowane:

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

Do takiego zachowania jestem przyzwyczajony w innych językach, ale zastanawiam się, czy można na nim bezpiecznie polegać w Delphi? Na przykład zastanawiam się, czy może to zależeć od ustawień kompilatora, czy może działać inaczej na różnych komputerach. Czy normalne jest poleganie na domyślnych zainicjowanych wartościach obiektów, czy też jawnie ustawiasz wszystkie zmienne instancji w konstruktorze?

Jeśli chodzi o zmienne stosu (na poziomie procedury), moje testy pokazują, że zjednolicone wartości logiczne są prawdziwe, zjednostkowane liczby całkowite to 2129993264, a niezinicjalizowane obiekty są po prostu nieprawidłowymi wskaźnikami (tj. Nie są zerowe). Domyślam się, że normą jest zawsze ustawianie zmiennych na poziomie procedury przed uzyskaniem do nich dostępu?


3
Dwie uwagi: 1. Rekordy nie są inicjowane. 2. Zmienne liczone jako odwołania są zawsze inicjalizowane. !ALE! w funkcji, która zwraca ciąg, „Wynik” nie jest inicjowany jako pusty ciąg, jak można się spodziewać. Dzieje się tak, ponieważ „Wynik” nie jest zmienną lokalną. Dlatego zawsze rób: Wynik: = '';
InTheNameOfScience


Odpowiedzi:


105

Tak, to jest udokumentowane zachowanie:

  • Pola obiektów są zawsze inicjalizowane na 0, 0,0, '', False, nil lub cokolwiek ma zastosowanie.

  • Zmienne globalne są również zawsze inicjalizowane na 0 itd .;

  • Lokalne zmienne liczone jako referencje * są zawsze inicjalizowane na zero lub '';

  • Zmienne lokalne nie liczone jako odwołania * nie są zainicjowane, więc przed użyciem należy przypisać wartość.

Pamiętam, że Barry Kelly napisał gdzieś definicję „zliczonych referencji”, ale nie mogę jej już znaleźć, więc w międzyczasie powinno wystarczyć:

zliczane jako odwołania ==, które same są liczone jako odwołania lub bezpośrednio lub pośrednio zawierają pola (dla rekordów) lub elementy (dla tablic), które są liczone jako odwołania, takie jak: string, variant, interface lub tablica dynamiczna lub tablica statyczna zawierająca takie typy.

Uwagi:

  • record samo w sobie nie wystarczy, aby zostać zaliczonym do referencji
  • Nie próbowałem jeszcze tego z lekami generycznymi

2
Jak zauważył Giacomo w komentarzach poniżej, wszystko to jest wyjaśnione w plikach pomocy Delphi pod adresem ms-help: //borland.bds4/bds4ref/html/Variables.htm. W Delphi 2009 znalazłem te same informacje, przeszukując pomoc dla „zmiennych” (co zabawne, próbowałem wielu wyszukiwań, ale nie pomyślałem, aby spróbować tego).
MB.

8
Zmienne lokalne SĄ zainicjalizowane ($ 0), jeśli są typu zarządzanego, takiego jak łańcuchy, interfejsy, tablice dynamiczne lub warianty
Francesca,

5
Ale jest wyjątek! Kiedy nadpisujesz konstruktor i nie wywołujesz odziedziczonego konstruktora, istnieje szansa, że ​​niektóre pola nie zostaną zainicjowane! (Szczególnie ze starszymi wersjami Delphi.) Od TObject.Create jest odpowiedzialny za zerowanie wszystkich danych, a nie wywołanie tego skutkuje możliwymi nieznanymi danymi.
Wim ten Brink

18
@WimtenBrink Myślę, że się mylisz. Inicjalizacja nie jest wykonywana wewnątrz TObject.Create, co jest metodą void, ale w class function TObject.InitInstance(Instance: Pointer): TObject;której jest ZAWSZE wywoływana przed jakimkolwiek wywołaniem konstruktora, nawet w przypadku starszych wersji Delphi. Twój komentarz jest błędny i mylący IMHO.
Arnaud Bouchez

7
Nie zapominaj, że w funkcji, która zwraca ciąg, „Wynik” nie jest inicjowany jako pusty ciąg, jak można się spodziewać. Dzieje się tak, ponieważ „Wynik” nie jest zmienną lokalną.
InTheNameOfScience

27

Zmienne globalne, które nie mają jawnego inicjatora, są przydzielane w sekcji BSS w pliku wykonywalnym. W rzeczywistości nie zajmują miejsca w pliku EXE; sekcja BSS jest specjalną sekcją, którą system operacyjny przydziela i czyści do zera. W innych systemach operacyjnych istnieją podobne mechanizmy.

Możesz polegać na inicjalizacji zmiennych globalnych.


21

Pola klas mają domyślnie zero. Jest to udokumentowane, więc możesz na nim polegać. Zmienne stosu lokalnego są niezdefiniowane, chyba że łańcuch lub interfejs, są ustawione na zero.


Dzięki. „Zero” trochę mnie dezorientuje - czy to oznacza, że ​​łańcuchy to „”, a interfejsy są zerowe?
MB.

4
Tak, dokładnie to. nil = 0 (na poziomie asemblera) i '' = nil (konwencja Delphi).
gabr

1
„chyba że łańcuch lub interfejs” nie jest pełnym opisem rzeczywistości. Inicjowane są na przykład tablice dynamiczne. Mówiąc bardziej ogólnie, reguła jest taka, że ​​zmienne typu zarządzanego (liczone jako odwołania) są inicjowane, nawet jeśli są lokalne.
Andreas Rejbrand

16

Na marginesie (ponieważ jesteś nowy w Delphi): Zmienne globalne można zainicjować bezpośrednio podczas ich deklarowania:

var myGlobal:integer=99;

2
Od 10.3 to samo dotyczy zmiennych lokalnych
Edijs Kolesnikovičs

1
A jeśli nie zostanie to zrobione jawnie, zostaną zainicjalizowane na 0, 0,0, False, nil, [] itd.
Andreas Rejbrand

7

Oto cytat z Raya Lischners Delphi w pigułce Rozdział 2

„Kiedy Delphi po raz pierwszy tworzy obiekt, wszystkie pola są na początku puste, to znaczy wskaźniki są inicjalizowane na zero, łańcuchy i tablice dynamiczne są puste, liczby mają wartość zero, pola logiczne mają wartość Fałsz, a warianty są ustawione na Nieprzypisane. (Aby uzyskać szczegółowe informacje, patrz NewInstance i InitInstance w rozdziale 5). "

Prawdą jest, że zmienne lokalne w zakresie muszą zostać zainicjalizowane… Powyższy komentarz, że „Zmienne globalne są inicjalizowane”, traktowałbym jako wątpliwy, dopóki nie otrzymam odniesienia - nie wierzę w to.

edytuj ... Barry Kelly mówi, że możesz polegać na ich inicjalizacji zerowej, a ponieważ jest w zespole kompilatorów Delphi, wierzę, że tak jest :) Dzięki Barry.


1
W pomocy delphi 2006 można ją znaleźć tutaj: ms-help: //borland.bds4/bds4ref/html/Variables.htm "Jeśli nie zainicjujesz jawnie zmiennej globalnej, kompilator zainicjuje ją na 0. Dane instancji obiektu ( pola) są również inicjalizowane na 0. ”
Giacomo Degli Esposti,

Negocjowany z powodu „Nie wierzę w to”. To jest programowanie, a nie religia. A Giacomo właśnie pokazał prawdę.
InTheNameOfScience

6

Zmienne globalne i dane (pola) instancji obiektu są zawsze inicjowane na zero. Zmienne lokalne w procedurach i metodach nie są inicjowane w Win32 Delphi; ich zawartość jest niezdefiniowana, dopóki nie przypiszesz im wartości w kodzie.


5

Nawet jeśli język oferuje domyślne inicjalizacje, uważam, że nie należy na nich polegać. Inicjalizacja do wartości sprawia, że ​​jest ona znacznie bardziej przejrzysta dla innych programistów, którzy mogą nie wiedzieć o domyślnych inicjalizacjach w języku, i zapobiega problemom między kompilatorami.


4
Oczywiście, że możesz. A powinieneś. Inicjowanie wszystkiego na 0 / '' / false / nil w każdym konstruktorze jest po prostu niepotrzebne. Z drugiej strony, inicjalizacja zmiennych globalnych nie jest taka głupia - po raz pierwszy nie pamiętam, czy są zainicjowane, czy nie (ponieważ nie używam ich zbyt często).
gabr,

2
Jeśli Delphi pozwoli Ci zainicjować zmienną w tym samym momencie, w którym ją deklarujesz (np. Var fObject: TObject = nil), byłbym skłonny zgodzić się, że inicjalizacja do wartości jest prawdopodobnie dobrym pomysłem. Ale wydaje mi się, że robi to trochę za dużo w konstruktorze dla każdego pola obiektu.
MB.

4

Z pliku pomocy Delphi 2007:

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

„Jeśli nie zainicjujesz jawnie zmiennej globalnej, kompilator zainicjuje ją na 0.”


3

Mam mały problem z udzielonymi odpowiedziami. Delphi zeruje przestrzeń pamięci globali i nowo utworzonych obiektów. Chociaż to NORMALNIE oznacza, że ​​są zainicjowane, jest jeden przypadek, w którym tak nie jest: typy wyliczeniowe z określonymi wartościami. A jeśli zero nie jest wartością prawną?


1
Zero jest zawsze wartością prawną, jest to pierwsza wartość wyliczenia. możesz to zobaczyć za pomocą ord (MyFirstEnumValue).
Francesca,

Zwróciłby pierwszą wartość wyliczonego typu.
skamradt

6
Zero nie zawsze jest poprawną wartością, jeśli jawnie przypiszesz wartości do wyliczenia. W takim przypadku jest nadal zainicjowany na 0, a masz niedozwoloną wartość. Ale wyliczenia są po prostu cukrem syntaktycznym pomalowanym na normalne typy liczb całkowitych, więc to naprawdę niczego nie psuje. Upewnij się, że Twój kod sobie z tym poradzi.
Mason Wheeler,

2
@ François: Nie, jeśli zdefiniujesz wyliczenie w ten sposób:TOneTwoThree = (One=1, Two=2, Three=3);
fnkr

0

Nowo wprowadzone (od Delphi 10.3) zmienne wbudowane ułatwiają kontrolę wartości początkowych.

procedure TestInlineVariable;
begin
  var index: Integer := 345;
  ShowMessage(index.ToString);
end;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.