Jak ważna jest inicjalizacja zmiennych?
Czy właściwa inicjalizacja pozwala uniknąć wycieków pamięci lub ma zalety związane z wydajnością?
Jak ważna jest inicjalizacja zmiennych?
Czy właściwa inicjalizacja pozwala uniknąć wycieków pamięci lub ma zalety związane z wydajnością?
null
) przez popularne kompilatory, ale są losowymi śmieciami podczas kompilacji do wydania. (chociaż moja znajomość C ++ pochodzi z około 10 lat temu, sprawy mogły się zmienić)Odpowiedzi:
Niezainicjowane zmienne powodują, że program nie jest deterministyczny. Za każdym razem, gdy program działa, może zachowywać się inaczej. Niepowiązane zmiany środowiska operacyjnego, pory dnia, fazy księżyca i permutacji takich wpływają na to, jak i kiedy manifestują się te demony. Program może uruchomić milion razy, zanim pojawi się defekt, mogą to zrobić za każdym razem lub uruchomić kolejny milion. Wiele problemów sprowadza się do „usterki” i są ignorowane lub raporty o błędach od klientów są zamykane jako „nie do odtworzenia”. Jak często restartowałeś komputer, aby „naprawić” problem? Jak często mówiłeś do klienta: „Nigdy nie widziałem, żeby tak się stało, daj mi znać, jeśli zobaczysz to ponownie” - mając nadzieję (wiedząc), że nie będzie!
Ponieważ odtworzenie wady może być prawie niemożliwe w środowisku testowym, prawie niemożliwe jest jej znalezienie i naprawienie.
Ujawnienie błędu może zająć lata, zwykle w kodzie uważanym za niezawodny i stabilny. Zakłada się, że defekt ma nowszy kod - jego odnalezienie może potrwać znacznie dłużej. Zmiana w kompilatorze, przełącznik kompilatora, a nawet dodanie wiersza kodu może zmienić zachowanie.
Inicjowanie zmiennych ma ogromną przewagę wydajności, nie tylko dlatego, że program, który działa poprawnie, jest nieskończenie szybszy niż ten, który tego nie robi, ale programiści spędzają mniej czasu na szukaniu i naprawianiu defektów, których nie powinno być, a więcej na wykonywaniu „prawdziwej” pracy.
Inną znaczącą zaletą inicjowania zmiennych jest to, że autor kodu musi zdecydować, na czym je zainicjować. Nie zawsze jest to trywialne ćwiczenie, a gdy nie jest trywialne, może wskazywać na zły projekt.
Wycieki pamięci to inny problem, ale właściwa inicjalizacja może nie tylko pomóc w ich zapobieganiu, ale może również pomóc w ich wykryciu i znalezieniu źródła - jest w dużej mierze zależne od języka i to naprawdę osobne pytanie warte dalszych badań, niż jestem w stanie udzielić w tej odpowiedzi.
Edycja: W niektórych językach (np. C #) nie można używać niezainicjowanych zmiennych, ponieważ program nie będzie się kompilował ani nie zgłosi błędu po uruchomieniu, jeśli zostanie wykonany. Jednak wiele języków o tych cechach posiada interfejsy do potencjalnie niebezpiecznego kodu, dlatego należy zachować ostrożność, używając takich interfejsów no, aby wprowadzić niezainicjowane zmienne.
źródło
Inicjowanie zmiennej, jak wskazał Telastyn, może zapobiec błędom. Jeśli zmienna jest typem odniesienia, zainicjowanie jej może zapobiec błędom odniesienia o wartości zerowej w linii.
Zmienna dowolnego typu, która ma wartość inną niż null, zajmie trochę pamięci do przechowywania wartości domyślnej.
źródło
Próba użycia niezainicjowanej zmiennej jest zawsze błędem, więc sensowne jest zminimalizowanie prawdopodobieństwa wystąpienia tego błędu.
Prawdopodobnie najpopularniejszym podejściem języków programowania w celu złagodzenia problemu jest automatyczna inicjalizacja do wartości domyślnej, więc przynajmniej jeśli zapomnisz zainicjować zmienną, będzie to coś podobnego
0
zamiast czegoś podobnego0x16615c4b
.To rozwiązuje duży odsetek błędów, jeśli i tak potrzebujesz zmiennej zainicjowanej na zero. Jednak użycie zmiennej, która została zainicjowana na niepoprawną wartość, jest tak samo złe, jak użycie zmiennej, która nie została w ogóle zainicjowana. W rzeczywistości czasami może być nawet gorzej, ponieważ błąd może być bardziej subtelny i trudny do wykrycia.
Funkcjonalne języki programowania rozwiązują ten problem, nie tylko nie dopuszczając niezainicjowanych wartości, ale całkowicie uniemożliwiając ponowne przypisanie. To eliminuje problem i okazuje się, że nie jest tak surowym ograniczeniem, jak mogłoby się wydawać. Nawet w językach niefunkcjonalnych, jeśli czekasz na zadeklarowanie zmiennej, dopóki nie uzyskasz poprawnej wartości do jej zainicjowania, twój kod jest zwykle bardziej niezawodny.
Jeśli chodzi o wydajność, jest to prawdopodobnie nieistotne. W najgorszym przypadku z niezainicjowanymi zmiennymi masz jedno dodatkowe przypisanie i przywiązujesz trochę pamięci na dłużej niż to konieczne. Dobre kompilatory mogą optymalizować różnice w wielu przypadkach.
Wycieki pamięci są całkowicie niezwiązane, chociaż odpowiednio zainicjowane zmienne są w zasięgu przez krótszy okres czasu, a zatem może być nieco mniej prawdopodobne, że programista wycieknie.
źródło
Inicjalizacja oznacza, że wartość początkowa ma znaczenie. Jeśli wartość początkowa ma znaczenie, to tak, oczywiście musisz upewnić się, że została ona zainicjowana. Jeśli to nie ma znaczenia, oznacza to, że zostanie zainicjowane później.
Niepotrzebna inicjalizacja powoduje zmarnowane cykle procesora. Podczas gdy te zmarnowane cykle mogą nie mieć znaczenia w niektórych programach, w innych programach, każdy pojedynczy cykl jest ważny, ponieważ szybkość ma zasadnicze znaczenie. Dlatego bardzo ważne jest, aby zrozumieć, jakie są cele związane z wydajnością i czy należy zainicjować zmienne, czy nie.
Wycieki pamięci to zupełnie inna kwestia, która zazwyczaj wiąże się z funkcją alokatora pamięci w celu wydania i późniejszego przetworzenia bloków pamięci. Pomyśl o poczcie. Idź i poproś o skrzynkę pocztową. Dają ci jeden. Prosisz o kolejny. Dają ci jeszcze jeden. Zasadą jest, że kiedy skończysz używać skrzynki pocztowej, musisz ją zwrócić. Jeśli zapomnisz go oddać, nadal myślą, że go masz, a pudełko nie może być ponownie wykorzystane przez nikogo innego. Jest więc część pamięci związana i nieużywana, i to jest tak zwane wyciek pamięci. Jeśli nadal będziesz pytać o pola, zabraknie ci pamięci. Upraszczałem to, ale to jest podstawowa idea.
źródło
Jak powiedzieli inni, zależy to od języka. Ale zademonstruję moje pomysły Java (i Effective Java) na temat inicjowania zmiennych. Powinny one być użyteczne w wielu innych językach wyższego poziomu.
Stałe i zmienne klasowe
Zmienne klasy - oznaczone
static
w Javie - są jak stałe. Te zmienne powinny normalnie być ostateczne i inicjowane bezpośrednio po definicji przy użyciu=
lub z poziomu bloku inicjalizującego klasęstatic { // initialize here }
.Pola
Podobnie jak w wielu językach wyższego poziomu i językach skryptowych automatycznie przypisana zostanie wartość domyślna. Dla liczb, a
char
będzie to wartość zerowa. Tak będzie w przypadku ciągów znaków i innych obiektównull
. Teraznull
jest niebezpieczny i powinien być używany oszczędnie. Dlatego te pola powinny zostać ustawione na prawidłową wartość tak szybko, jak to możliwe. Konstruktor jest zwykle idealnym miejscem do tego. Aby upewnić się, że zmienne są ustawione podczas konstruktora, a nie zmieniane później, możesz oznaczyć jefinal
słowem kluczowym.Spróbuj oprzeć się pokusie użycia
null
jako jakiejś flagi lub specjalnej wartości. Lepiej np. Dołączyć określone pole do przechowywania stanu. Dobrym wyborem byłoby pole z nazwą,state
która używa wartościState
wyliczenia.Parametry metody
Ponieważ zmiany wartości parametrów (odniesienia do obiektów lub podstawowych typów, takich jak liczby całkowite itp.) Nie będą widoczne dla osoby wywołującej, parametry należy oznaczyć jako
final
. Oznacza to, że wartości samej zmiennej nie można zmienić. Zauważ, że wartość modyfikowalnych instancji obiektów może ulec zmianie, odwołanie nie może zostać zmieniony, aby wskazywał na inny obiekt lubnull
chociaż.Zmienne lokalne
Zmienne lokalne nie są automatycznie inicjowane; muszą zostać zainicjowane przed użyciem ich wartości. Jedną z metod upewnienia się, że zmienna została zainicjowana, jest bezpośrednia inicjalizacja jakiejś wartości domyślnej. Jest jednak coś, co powinno nie robić. Przez większość czasu wartość domyślna nie jest wartością, której można oczekiwać.
O wiele lepiej jest definiować zmienną dokładnie tam, gdzie jej potrzebujesz. Jeśli zmienna ma przyjmować tylko jedną wartość (co jest prawdziwe w przypadku większości zmiennych w dobrym kodzie), możesz ją oznaczyć
final
. Dzięki temu zmienna lokalna jest przypisywana dokładnie raz, a nie zero razy lub dwa razy. Przykład:Pamiętaj, że wiele języków ostrzeże Cię, jeśli zmienna pozostanie niezainicjowana przed użyciem. Sprawdź specyfikacje językowe i fora, aby sprawdzić, czy nie musisz się niepotrzebnie martwić.
źródło
Nie ma problemu z niezainicjowaniem zmiennych.
Problem pojawia się tylko wtedy, gdy czytasz zmienną, która nie została jeszcze zapisana.
W zależności od kompilatora i / lub rodzaju zmiennej inicjalizacja jest wykonywana podczas uruchamiania aplikacji. Albo nie.
Powszechnym zastosowaniem jest nie poleganie na automatycznej inicjalizacji.
źródło
Inicjowanie zmiennych (pośrednio lub jawnie) ma kluczowe znaczenie. Brak inicjalizacji zmiennej jest zawsze błędem (mogą być jednak inicjowane niejawnie. Patrz poniżej). Nowoczesne kompilatory, takie jak kompilator C # (jako przykład) traktują to jako błąd i nie pozwalają na wykonanie kodu. Niezainicjowana zmienna jest po prostu bezużyteczna i szkodliwa. O ile nie tworzysz generatora liczb losowych, oczekujesz, że fragment kodu da deterministyczny i powtarzalny wynik. Można to osiągnąć tylko wtedy, gdy zaczniesz pracować z zainicjowanymi zmiennymi.
Naprawdę interesujące jest pytanie, czy zmienna jest inicjowana automatycznie, czy też trzeba to zrobić ręcznie. To zależy od używanego języka. Na przykład w języku C # pola, tj. „Zmienne” na poziomie klasy, są zawsze automatycznie inicjowane do wartości domyślnej dla tego typu zmiennej
default(T)
. Ta wartość odpowiada wzorowi bitowemu złożonemu ze wszystkich zer. Jest to część specyfikacji języka, a nie tylko techniczny szczegół implementacji języka. Dlatego możesz bezpiecznie na nim polegać. Bezpiecznie jest nie inicjować zmiennej jawnie, jeśli (i tylko jeśli) specyfikacja języka stwierdza, że jest ona inicjowana niejawnie.Jeśli potrzebujesz innej wartości, musisz jawnie zainicjować zmienną. Jednak; w C # zmienne lokalne, tj. zmienne zadeklarowane w metodach, nie są inicjowane automatycznie i zawsze należy jawnie zainicjować zmienną.źródło