Zmienna (informatyka)

W programowaniu komputerowym zmienna to abstrakcyjne miejsce przechowywania połączone z powiązaną nazwą symboliczną , która zawiera pewną znaną lub nieznaną ilość informacji określaną jako wartość ; lub mówiąc prościej, zmienna to nazwany kontener dla określonego zestawu bitów lub typu danych (takich jak integer , float , string itp.). Zmienną można ostatecznie powiązać lub zidentyfikować za pomocą adresu pamięci . Nazwa zmiennej to zwykły sposób odniesienia przechowywanej wartości, oprócz odwoływania się do samej zmiennej, w zależności od kontekstu. To oddzielenie nazwy i treści pozwala na używanie nazwy niezależnie od dokładnych informacji, które reprezentuje. Identyfikator w kodzie źródłowym komputera może być powiązany z wartością w czasie wykonywania programu , a zatem wartość zmiennej może się zmieniać w trakcie wykonywania programu .

Zmienne w programowaniu mogą nie odpowiadać bezpośrednio pojęciu zmiennych w matematyce . Ten ostatni jest abstrakcyjny i nie ma odniesienia do obiektu fizycznego, takiego jak miejsce przechowywania. Wartość zmiennej obliczeniowej niekoniecznie jest częścią równania lub formuły , jak w matematyce. Zmiennym w programowaniu komputerowym często nadawane są długie nazwy, aby były względnie opisowe ich zastosowania, podczas gdy zmienne w matematyce często mają zwięzłe, jedno- lub dwuznakowe nazwy dla zwięzłości transkrypcji i manipulacji.

Do lokalizacji przechowywania zmiennej może odwoływać się kilka różnych identyfikatorów, sytuacja znana jako aliasing . Przypisanie wartości do zmiennej za pomocą jednego z identyfikatorów spowoduje zmianę wartości, do której można uzyskać dostęp za pośrednictwem innych identyfikatorów.

Kompilatory muszą zastępować nazwy symboliczne zmiennych rzeczywistymi lokalizacjami danych. Podczas gdy nazwa, typ i lokalizacja zmiennej często pozostają stałe, dane przechowywane w lokalizacji mogą ulec zmianie podczas wykonywania programu.

Działania na zmiennej

W imperatywnych językach programowania wartości można ogólnie uzyskać lub zmienić w dowolnym momencie. W czysto funkcjonalnych i logicznych językach zmienne są powiązane z wyrażeniami i zachowują jedną wartość przez cały okres ich istnienia ze względu na wymagania przejrzystości referencyjnej . W językach imperatywnych to samo zachowanie wykazują (nazwane) stałe (stałe symboliczne), które zazwyczaj przeciwstawia się (normalnym) zmiennym.

W zależności od systemu typów języka programowania, zmienne mogą przechowywać tylko określony typ danych (np. integer lub string ). Alternatywnie typ danych może być powiązany tylko z bieżącą wartością, co pozwala pojedynczej zmiennej przechowywać wszystko, co obsługuje język programowania.

Zmienne to pojemniki do przechowywania wartości.

Zmienne i zakres:

  • Zmienne automatyczne : Każda zmienna lokalna w funkcji pojawia się tylko wtedy, gdy funkcja jest wywoływana, i znika, gdy funkcja jest kończona. Takie zmienne są znane jako zmienne automatyczne.
  • Zmienne zewnętrzne: są to zmienne zewnętrzne w stosunku do funkcji, do których można uzyskać dostęp za pomocą nazwy z dowolnej funkcji. Te zmienne istnieją na stałe; zamiast tego, że pojawiają się i znikają, gdy funkcje są wywoływane i zamykane, zachowują swoje wartości nawet po powrocie funkcji, które je ustawiły.

Identyfikatory odnoszące się do zmiennej

Identyfikator odnoszący się do zmiennej może służyć do uzyskiwania dostępu do zmiennej w celu odczytania wartości lub zmiany wartości lub edycji innych atrybutów zmiennej, takich jak uprawnienia dostępu, blokady , semafory itp.

Na przykład do zmiennej może odwoływać się identyfikator „ total_count ”, a zmienna może zawierać liczbę 1956. Jeśli do tej samej zmiennej odwołuje się również identyfikator „ r ” i jeśli używany jest ten identyfikator „ r ”, wartość zmienna zostanie zmieniona na 2009, to odczytanie wartości za pomocą identyfikatora „ total_count ” da wynik 2009, a nie 1956.

Jeśli do zmiennej odwołuje się tylko jeden identyfikator, ten identyfikator można po prostu nazwać nazwą zmiennej ; w przeciwnym razie możemy mówić o niej jako o jednej z nazw zmiennej . Na przykład w poprzednim przykładzie identyfikator „ total_count ” to nazwa danej zmiennej, a „ r ” to inna nazwa tej samej zmiennej.

Zakres i zakres

Zakres zmiennej opisuje, gdzie w tekście programu zmienna może być używana, podczas gdy zakres ( zwany także czasem życia ) zmiennej opisuje, kiedy podczas wykonywania programu zmienna ma (znaczącą) wartość . Zakres zmiennej wpływa na jej zakres. Zakres zmiennej jest właściwie właściwością nazwy zmiennej, a zasięg jest właściwością miejsca przechowywania zmiennej. Nie należy ich mylić z kontekstem (zwanym także środowiskiem ), która jest właściwością programu i różni się w zależności od punktu w tekście programu lub wykonaniu — patrz zakres: przegląd . Ponadto czas życia obiektu może pokrywać się ze zmiennym czasem życia, ale w wielu przypadkach nie jest z nim powiązany.

Zakres jest ważną częścią rozpoznawania nazwy zmiennej. Większość języków definiuje określony zakres dla każdej zmiennej (jak również każdej innej nazwanej jednostki), który może się różnić w ramach danego programu. Zasięg zmiennej to część tekstu programu, dla której nazwa zmiennej ma znaczenie i o której mówi się, że zmienna jest „widoczna”. Wejście do tego zakresu zazwyczaj rozpoczyna okres istnienia zmiennej (gdy wchodzi w kontekst), a wyjście z tego zakresu zazwyczaj kończy jego okres istnienia (ponieważ wychodzi poza kontekst). Na przykład zmienna z „ zakresem leksykalnym " ma znaczenie tylko w ramach określonej funkcji/ podprogramu standardowego lub dokładniej w bloku wyrażeń/instrukcji (zgodnie z zakresem funkcji lub zakresem bloku ); jest to rozdzielczość statyczna, wykonywana w czasie analizowania lub kompilacji. Alternatywnie, zmienna z zakresem dynamicznym jest rozwiązywany w czasie wykonywania na podstawie globalnego stosu powiązań, który zależy od określonego przepływu sterowania. Zmienne dostępne tylko w ramach określonych funkcji są nazywane „ zmiennymi lokalnymi ”. „ Zmienna globalna” ” lub o nieokreślonym zakresie, można się odwoływać w dowolnym miejscu programu.

Z drugiej strony zasięg jest aspektem zmiennej w czasie wykonywania ( dynamicznym ). Każde powiązanie zmiennej z wartością może mieć swój własny zakres w czasie wykonywania. Zakres powiązania to część czasu wykonywania programu, podczas której zmienna nadal odnosi się do tej samej wartości lub miejsca w pamięci. Działający program może wchodzić i wychodzić z danego zakresu wiele razy, jak w przypadku zamknięcia .

O ile język programowania nie zawiera funkcji wyrzucania elementów bezużytecznych , zmienna, której zakres na stałe przekracza jej zakres, może spowodować wyciek pamięci , przez co pamięć przydzielona dla zmiennej nigdy nie może zostać zwolniona, ponieważ zmienna, która byłaby używana do odniesienia się do niej w celu cofnięcia alokacji, nie jest już dostępny. Jednak może być dopuszczalne, aby powiązanie zmiennej wykraczało poza jego zakres, jak ma to miejsce w przypadku zamknięć Lispa i statycznych zmiennych lokalnych C ; kiedy wykonanie przechodzi z powrotem do zakresu zmiennej, zmienna może być ponownie użyta. Mówi się, że zmienna, której zakres zaczyna się przed zasięgiem, jest niezainicjowana i często ma niezdefiniowaną, arbitralną wartość, jeśli jest dostępna (patrz dziki wskaźnik ), ponieważ nie została jeszcze jawnie nadana konkretna wartość. Zmienna, której zakres kończy się przed zakresem, może stać się wiszącym wskaźnikiem i ponownie zostać uznana za niezainicjowaną, ponieważ jej wartość została zniszczona. Można powiedzieć, że zmienne opisane w poprzednich dwóch przypadkach są poza zakresem lub są nieograniczone . W wielu językach błędem jest próba użycia wartości zmiennej, gdy jest ona poza zakresem. W innych językach może to dać nieprzewidywalne rezultaty . Takiej zmiennej można jednak nadać nową wartość, co nadaje jej nowy zakres.

Aby zapewnić efektywne wykorzystanie przestrzeni, miejsce w pamięci potrzebne dla zmiennej może być przydzielane tylko wtedy, gdy zmienna jest używana po raz pierwszy i zwalniana, gdy nie jest już potrzebna. Zmienna jest potrzebna tylko wtedy, gdy znajduje się w zakresie, dlatego rozpoczęcie życia każdej zmiennej, gdy wchodzi ona w zakres, może zwolnić miejsce na nieużywane zmienne. Aby uniknąć marnowania takiej przestrzeni, kompilatory często ostrzegają programistów, jeśli zmienna jest zadeklarowana, ale nie jest używana.

Za dobrą praktykę programistyczną uważa się, aby zakres zmiennych był tak wąski, jak to tylko możliwe, aby różne części programu nie wchodziły ze sobą przypadkowo w interakcje poprzez wzajemne modyfikowanie zmiennych. Uniemożliwia to również działanie na odległość . Typowe techniki robienia tego polegają na tym, że różne sekcje programu używają różnych przestrzeni nazw lub nadanie poszczególnym zmiennym „prywatności” poprzez dynamiczne określanie zakresu zmiennych lub określanie zakresu zmiennych leksykalnych .

Wiele języków programowania stosuje wartość zarezerwowaną (często nazywaną null lub nil ), aby wskazać nieprawidłową lub niezainicjowaną zmienną.

Pisanie na maszynie

W językach o typie statycznym, takich jak Go lub ML , zmienna ma również typ , co oznacza, że ​​można w niej przechowywać tylko określone rodzaje wartości. Na przykład zmienna typu „ integer ” nie może przechowywać wartości tekstowych.

W językach z dynamicznym typowaniem, takich jak Python , typ zmiennej jest określany na podstawie jej wartości i może się zmieniać zgodnie z jej wartością. W Common Lisp obie sytuacje występują jednocześnie: Zmienna otrzymuje typ (jeśli jest niezadeklarowany, zakłada się, że jest to T , uniwersalny nadtyp ), który istnieje w czasie kompilacji. Wartości mają również typy, które można sprawdzać i odpytywać w czasie wykonywania.

Wpisywanie zmiennych umożliwia również rozwiązywanie polimorfizmów w czasie kompilacji. Różni się to jednak od polimorfizmu używanego w wywołaniach funkcji zorientowanych obiektowo (nazywanych funkcjami wirtualnymi w C++ ), który rozwiązuje wywołanie na podstawie typu wartości, w przeciwieństwie do nadtypów, które zmienna może mieć.

Zmienne często przechowują proste dane, takie jak liczby całkowite i ciągi literowe, ale niektóre języki programowania pozwalają zmiennej przechowywać również wartości innych typów danych . Takie języki mogą również umożliwiać funkcjom parametryczną polimorficzność . Funkcje te działają jak zmienne reprezentujące dane wielu typów. Na przykład funkcja o nazwie length może określać długość listy. Taka długości może być parametryczną polimorficzną poprzez włączenie zmiennej typu do jej sygnatury typu , ponieważ liczba elementów na liście jest niezależna od typów elementów.

Parametry

Parametry formalne (lub argumenty formalne ) funkcji są również nazywane zmiennymi. Na przykład w tym segmencie kodu Pythona

 
       


 >>>  def  addtwo  (  x  ):  ...  return  x  +  2  ...  >>>  addtwo  (  5  )  7 

zmienna o nazwie x jest parametrem , ponieważ otrzymuje wartość podczas wywoływania funkcji. Liczba całkowita 5 jest argumentem , który daje x swoją wartość. W większości języków parametry funkcji mają zasięg lokalny. Do tej konkretnej zmiennej o nazwie x można się odwoływać tylko w ramach funkcji addtwo (chociaż oczywiście inne funkcje również mogą mieć zmienne o nazwie x ).

Alokacja pamięci

Specyfika alokacji zmiennych i reprezentacji ich wartości jest bardzo zróżnicowana, zarówno między językami programowania, jak i implementacjami danego języka. Wiele implementacji językowych przydziela miejsce dla zmiennych lokalnych , których zasięg obejmuje pojedyncze wywołanie funkcji na stosie wywołań i których pamięć jest automatycznie odzyskiwana po powrocie funkcji. Mówiąc bardziej ogólnie, w wiązaniu nazw nazwa zmiennej jest powiązana z adresem określonego bloku (ciągłej sekwencji) bajtów w pamięci, a operacje na zmiennej manipulują tym blokiem. odniesienia jest bardziej powszechny w przypadku zmiennych, których wartości mają duże lub nieznane rozmiary podczas kompilacji kodu. Takie zmienne odwołują się do lokalizacji wartości zamiast do przechowywania samej wartości, która jest przydzielana z puli pamięci zwanej stertą .

Zmienne powiązane mają wartości. Wartość jest jednak abstrakcją, ideą; w implementacji wartość jest reprezentowana przez jakiś obiekt danych , który jest przechowywany gdzieś w pamięci komputera. Program lub środowisko wykonawcze musi zarezerwować pamięć dla każdego obiektu danych, a ponieważ pamięć jest skończona, zapewnić, że pamięć ta zostanie ponownie wykorzystana, gdy obiekt nie będzie już potrzebny do reprezentowania wartości jakiejś zmiennej.

Obiekty przydzielone ze sterty muszą zostać odzyskane — zwłaszcza gdy nie są już potrzebne. W zbieranych bezużytecznie (takich jak C# , Java , Python , Golang i Lisp ) środowisko wykonawcze automatycznie odzyskuje obiekty, gdy istniejące zmienne nie mogą się już do nich odwoływać. W językach bez śmieci, takich jak C , program (i programista) musi jawnie przydzielić pamięć, a następnie ją później zwolnić, aby odzyskać pamięć. Niezastosowanie się do tego prowadzi do wycieków pamięci , w którym sterta jest wyczerpywana w trakcie działania programu, grozi awaria wynikająca z wyczerpania dostępnej pamięci.

Gdy zmienna odnosi się do dynamicznie tworzonej struktury danych , dostęp do niektórych jej składników można uzyskać tylko pośrednio za pośrednictwem tej zmiennej. W takich okolicznościach moduły wyrzucania elementów bezużytecznych (lub analogiczne funkcje programu w językach, w których brakuje modułów wyrzucania elementów bezużytecznych) muszą radzić sobie z przypadkiem, w którym tylko część pamięci dostępnej ze zmiennej wymaga odzyskania.

Konwencje nazewnictwa

W przeciwieństwie do swoich matematycznych odpowiedników, zmienne programistyczne i stałe często przyjmują nazwy składające się z wielu znaków, np. COST lub total . Nazwy jednoznakowe są najczęściej używane tylko dla zmiennych pomocniczych; na przykład i , j , k dla zmiennych indeksów tablicowych .

Niektóre konwencje nazewnictwa są wymuszane na poziomie języka jako część składni języka, która obejmuje format prawidłowych identyfikatorów. W prawie wszystkich językach nazwy zmiennych nie mogą zaczynać się od cyfry (0–9) ani zawierać spacji. To, czy znaki interpunkcyjne są dozwolone w nazwach zmiennych, różni się w zależności od języka; wiele języków zezwala tylko na podkreślenie („_”) w nazwach zmiennych i zabrania wszelkich innych znaków interpunkcyjnych. W niektórych językach programowania sigile (symbole lub znaki interpunkcyjne) są umieszczane w identyfikatorach zmiennych w celu wskazania typu danych lub zakresu zmiennej.

Rozróżnianie wielkości liter w nazwach zmiennych również różni się w zależności od języka, a niektóre języki wymagają użycia określonej wielkości liter w nazwach niektórych jednostek; W większości współczesnych języków rozróżniana jest wielkość liter; niektóre starsze języki nie są. Niektóre języki rezerwują pewne formy nazw zmiennych do własnego użytku wewnętrznego; w wielu językach nazwy zaczynające się od dwóch znaków podkreślenia („__”) często należą do tej kategorii.

Jednak poza podstawowymi ograniczeniami narzuconymi przez język, nazewnictwo zmiennych jest w dużej mierze kwestią stylu. Na kodu maszynowego nazwy zmiennych nie są używane, więc wybrane dokładne nazwy nie mają znaczenia dla komputera. W ten sposób nazwy zmiennych je identyfikują, poza tym są tylko narzędziem dla programistów, aby programy były łatwiejsze do napisania i zrozumienia. Używanie źle dobranych nazw zmiennych może sprawić, że przeglądanie kodu będzie trudniejsze niż nazw nieopisowych, dlatego często zaleca się stosowanie jasnych nazw.

Programiści często tworzą i przestrzegają wytycznych dotyczących stylu kodu, które zawierają wskazówki dotyczące nazewnictwa zmiennych lub narzucają precyzyjny schemat nazewnictwa. Krótsze nazwy są szybsze do wpisania, ale są mniej opisowe; dłuższe nazwy często sprawiają, że programy są łatwiejsze do odczytania, a cel zmiennych łatwiejszy do zrozumienia. Jednak skrajna gadatliwość w nazwach zmiennych może również prowadzić do mniej zrozumiałego kodu.

Typy zmiennych (oparte na czasie życia)

Możemy klasyfikować zmienne na podstawie ich czasu życia. Różne typy zmiennych to statyczne, dynamiczne stosu, jawne dynamiczne sterty i niejawne dynamiczne sterty. Zmienna statyczna jest również znana jako zmienna globalna, jest powiązana z komórką pamięci przed rozpoczęciem wykonywania i pozostaje w tej samej komórce pamięci aż do zakończenia. Typowym przykładem są zmienne statyczne w językach C i C++. Zmienna dynamiczna stosu jest znana jako zmienna lokalna, która jest związana podczas wykonywania instrukcji deklaracji i jest zwalniana, gdy procedura powraca. Głównymi przykładami są zmienne lokalne w podprogramach C i metodach Java. Jawne zmienne dynamiczne sterty to bezimienne (abstrakcyjne) komórki pamięci, które są przydzielane i zwalniane przez jawne instrukcje czasu wykonywania określone przez programistę. Głównymi przykładami są obiekty dynamiczne w C++ (poprzez new i delete) oraz wszystkie obiekty w Javie. Niejawne zmienne sterty-dynamiczne są powiązane z pamięcią sterty tylko wtedy, gdy mają przypisane wartości. Alokacja i zwolnienie mają miejsce, gdy wartości są ponownie przypisywane do zmiennych. W rezultacie niejawne zmienne dynamiczne sterty mają najwyższy stopień elastyczności. Głównymi przykładami są niektóre zmienne w JavaScript, PHP i wszystkie zmienne w APL.

Zobacz też

Notatki