Láthatósági terület

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2021. január 30-án felülvizsgált verziótól ; az ellenőrzések 9 szerkesztést igényelnek .

A hatókör ( angolul  terjedő ) a programozásban a program  egy része , amelyen belül egy programentitás (általában változó , adattípus vagy függvény ) neveként deklarált azonosító ehhez az entitáshoz társítva marad, azaz lehetővé teszi, hogy hogy önmagán keresztül hivatkozzon rá. Egy objektumazonosítót akkor mondunk "láthatónak" a program egy bizonyos helyén, ha az adott objektumra az adott helyen hivatkozni lehet. A hatókörön kívül ugyanaz az azonosító társítható egy másik változóhoz vagy függvényhez, vagy szabad (egyikhez sem társítható). A hatókör lehet, de nem kell, hogy azonos legyen annak az objektumnak a hatókörével, amelyhez a név hozzá van rendelve.

Az azonosító kötés ( angol  kötés ) egyes programozási nyelvek terminológiájában egy programobjektum  meghatározásának folyamata, amelyhez való hozzáférés a program egy adott helyén és a végrehajtás egy adott pillanatában azonosítót ad. Ez a fogalom lényegében a hatókör szinonimája , de kényelmesebb lehet, ha figyelembe vesszük a programvégrehajtás egyes szempontjait.

A hatókörök egymásba illeszkednek, és egy hierarchiát alkotnak , a lokális, egy funkcióval (vagy akár annak egy részével) korlátozott hatókörtől a globális hatókörig, amelynek azonosítói az egész programban elérhetők. Ezenkívül egy adott programozási nyelv szabályaitól függően a hatóköröket kétféleképpen lehet megvalósítani: lexikailag (statikusan) vagy dinamikusan .

A hatókör a jelölőnyelvek esetében is hasznos lehet : például a HTML -ben a vezérlőnév hatóköre form (HTML) <form>-tól </form>-ig [1] .

Hatókör típusok

Egy beágyazott függvények és OOP használata nélküli monolitikus (egy modulos) programokban csak kétféle hatókör lehet: globális és lokális. Más típusok csak akkor léteznek, ha vannak bizonyos szintaktikai mechanizmusok a nyelvben.

Az OOP nyelvekben a fentieken túl speciális hatóköri korlátozások is támogathatók, amelyek csak az osztálytagokra vonatkoznak ( az osztályon belül deklarált vagy ahhoz kapcsolódó azonosítók):

Hatásköri módszerek

A legegyszerűbb esetekben a hatókört az határozza meg, hogy hol van deklarálva az azonosító. Azokban az esetekben, amikor a nyilatkozat helye nem tudja egyértelműen meghatározni a hatályt, speciális finomításokat alkalmazunk.

A fenti lista nem meríti ki az adott programozási nyelvben elérhető hatókör meghatározásának minden árnyalatát. Így például a moduláris hatókör és az OOP osztály tagjainak deklarált láthatóságának kombinációinak különböző értelmezései lehetségesek. Egyes nyelveken (például C++) egy osztálytag privát vagy védett hatókörének deklarálása korlátozza a hozzáférést minden olyan kód számára, amely nem kapcsolódik az osztály metódusaihoz. Más esetekben (Object Pascal) az osztály minden tagja, beleértve a privátokat és a védetteket is, teljes mértékben elérhető abban a modulban, amelyben az osztály deklarálva van, és a hatókör-korlátozások csak az ezt importáló többi modulra vonatkoznak.

Hierarchia és egyértelművé tétel

A hatókörök egy programban természetesen réteges struktúrát alkotnak, egyes hatókörök pedig beágyazódnak másokba. A területek hierarchiája általában a halmaz összes vagy néhány szintjén épül fel: "globális - csomag - moduláris - osztályok - helyi" (a konkrét sorrend kissé eltérhet a különböző nyelveken).

A csomagok és névterek több szintű beágyazást is tartalmazhatnak, így a hatókörük is beágyazódik. A modul és az osztály hatókörei közötti kapcsolat nyelvenként nagyon eltérő lehet. A helyi névterek is beágyazhatók, még akkor is, ha a nyelv nem támogatja a beágyazott függvényeket és eljárásokat. Így például a C++ nyelvben nincsenek beágyazott függvények, hanem minden összetett utasítás (amely kapcsos zárójelekbe zárt parancshalmazt tartalmaz) saját lokális hatókört alkot, amelyben lehetőség van a változóinak deklarálására.

A hierarchikus struktúra lehetővé teszi a kétértelműségek feloldását, amelyek akkor merülnek fel, ha ugyanazt az azonosítót egynél több értékben használják egy programban. A kívánt objektum keresése mindig abból a hatókörből indul, amelyben az azonosítóhoz hozzáférő kód található. Ha van egy objektum a kívánt azonosítóval az adott hatókörben, akkor ez az objektum kerül felhasználásra. Ha nincs, akkor a fordító a befoglaló hatókörben látható azonosítók között folytatja a keresést, ha ott sincs, akkor a következő hierarchiaszinten.

program Példa1 ; var a , b , c : Integer ; (* Globális változók. *) eljárás f1 ; var b , c : Integer (* Az f1 eljárás helyi változói. *) kezdődik a := 10 ; (* Globális változás a. *) b := 20 ; (* Változások helyi b. *) c := 30 ; (* Megváltoztatja a helyi c. *) writeln ( ' 4: ' , a , ',' , b , ',' , c ) ; vége ; eljárás f2 ; var b , c : Integer (* Az f2 eljárás helyi változói. *) procedúra f21 ; var c : Integer (* Eljárás helyi változó f21. *) begin a := 1000 ; (* Globális változások a. *) b := 2000 ; (* Megváltoztatja az f2 eljárás helyi b értékét. *) c := 3000 ; (* Megváltoztatja az f21 eljárás helyi c értékét.*) writeln ( ' 5: ' , a , ',' , b , ',' , c ) ; vége ; kezdődik a := 100 ; (* Globális változások a. *) b := 200 ; (* Változások helyi b. *) c := 300 ; (* Megváltoztatja a helyi c. *) writeln ( ' 6: ' , a , ',' , b , ',' , c ) ; f21 ; writeln ( ' 7: ' , a , ',' , b , ',' , c ) ; vége ; begin (* Globális változók inicializálása. *) a := 1 ; b := 2 ; c := 3 ; writeln ( ' 1: ' , a , ',' , b , ',' , c ) ; f1 ; writeln ( ' 2: ' , a , ',' , b , ',' , c ) ; f2 ; writeln ( ' 3: ' , a , ',' , b , ',' , c ) ; vége .

Tehát a fenti Pascal program futtatásakor a következő kimenetet kapja:

1:1,2,3 4:10,20,30 2:10,2,3 6: 100 200 300 5: 1000,2000,3000 7: 1000,2000,300 3:1000,2,3

Egy függvényben a és változók f1a lokális hatókörben vannak, így azok változásai nem érintik az azonos nevű globális változókat. Egy függvény csak egy változót tartalmaz a lokális hatókörében , így módosítja mind a globális , mind a lokális értéket a befoglaló függvényben . bcf21cabf2

Lexikális vs. dinamikus hatókörök

A korlátozott hatókörű és csak az aktuális függvényen belül létező lokális változók használata segít elkerülni az elnevezési ütközéseket két azonos nevű változó között. Két nagyon eltérő megközelítés létezik azonban arra a kérdésre, hogy mit jelent egy funkcióban „belül lenni”, és ennek megfelelően két lehetőség van a helyi hatókör megvalósítására:

  • lexikális hatókör , vagy lexikai hatókör ( angol  lexikai hatókör ), vagy lexikai (statikus) kötés ( angol  lexikai (statikus) kötés ): egy függvény lokális hatóköre ennek a függvénynek a definíciójának szövegére korlátozódik (a változó neve értéke van a függvény törzsén belül, és azon kívül definiálatlannak tekintendő).
  • dinamikus hatókör , vagy dinamikus kontextus ( eng.  dynamic range ), vagy dinamikus kötés ( angol  dinamikus kötés ): a helyi hatókört a függvény végrehajtási ideje korlátozza (a név elérhető a függvény végrehajtása közben, és eltűnik, amikor a függvény visszaadja a vezérlést az azt hívó kódhoz).

A "tiszta" függvények esetében, amelyek csak saját paramétereiken és helyi változóikon működnek, a lexikális és dinamikus hatókör mindig ugyanaz. Problémák merülnek fel, ha egy függvény külső neveket használ, például globális változókat vagy olyan függvények lokális változóit, amelyeknek része vagy amelyekből hívják. Tehát, ha egy függvény olyan függvényt fhív meg, amely nincs beágyazva g, akkor a lexikális megközelítéssel a függvény g nem fér hozzá a függvény lokális változóihoz f. A dinamikus megközelítéssel azonban a függvény g hozzáférhet a függvény helyi változóihoz f, mivel gfutási időben hívták meg f.

Vegyük például a következő programot:

x = 1 függvény g () { echo $x ; x = 2_ _ } function f () { local x = 3 ; g ; } f # 1-et vagy 3-at nyomtat? echo $x # 1-et vagy 2-t fog kiadni?

A függvény g()megjeleníti és módosítja a változó értékét x, de ez a változó g()sem nem paraméter, sem nem lokális változó, vagyis hozzá kell rendelni a hatókörből származó értékhez, amely tartalmazza a -t g(). Ha a nyelv, amelyen a program íródott, lexikális hatókört használ, akkor a «x»benne lévő nevet egy globális változóhoz g()kell társítani . A from-ból meghívott függvény kiírja a global kezdeti értékét , majd megváltoztatja azt, és a megváltozott értéket a program utolsó sora írja ki. Vagyis a program először 1-et, majd 2 -t jelenít meg. A függvény szövegében a lokális függvény változásai ezt a kimenetet semmilyen módon nem befolyásolják, mivel ez a változó sem a globális hatókörben, sem a függvényben nem látható . xg()f() хxf()g()

Ha a nyelv dinamikus hatóköröket használ, akkor a név «x»belsőleg a függvény helyi változójához van g()társítva , mivel belülről hívják meg, és belép a hatókörébe. Itt a függvény a függvény lokális változóját fogja megjeleníteni és megváltoztatni, de ez semmilyen módon nem befolyásolja a globális x értékét, így a program először 3-at, majd 1-et jelenít meg. Mivel ebben az esetben a program íródik a dinamikus megközelítést alkalmazó bash -ban a valóságban pontosan ez fog megtörténni. xf()g() f()g()xf()

Mind a lexikális, mind a dinamikus kötésnek megvannak az előnyei és hátrányai. A gyakorlatban az egyik és a másik közötti választást a fejlesztő hozza meg mind saját preferenciái, mind a tervezett programozási nyelv jellege alapján. A legtöbb tipikus magas szintű imperatív nyelv, amelyet eredetileg fordító használatára terveztek (a célplatform kódjába vagy a virtuális gép bájtkódjába, mindegy), statikus (lexikális) hatókört valósítanak meg, mivel ez kényelmesebben valósítható meg a fordítóprogram. A fordító olyan lexikai kontextussal dolgozik, amely statikus, és nem változik a program végrehajtása során, és a névre mutató hivatkozás feldolgozásával könnyen meg tudja határozni azt a címet a memóriában, ahol a névhez tartozó objektum található. A dinamikus kontextus nem elérhető a fordító számára (mivel a program végrehajtása során változhat, mert ugyanaz a függvény sok helyen hívható, és nem mindig kifejezetten), ezért a dinamikus hatókör biztosítása érdekében a fordítónak dinamikus támogatást kell adnia az objektumdefinícióhoz arra a kódra, amelyre az azonosító utal. Ez lehetséges, de csökkenti a program sebességét, további memóriát igényel és bonyolítja a fordítót.

Az értelmezett nyelvek esetében (például szkript ) a helyzet alapvetően más. Az értelmező közvetlenül a végrehajtáskor dolgozza fel a programszöveget, és belső végrehajtást támogató struktúrákat tartalmaz, beleértve a változó- és függvénynevek táblázatait valós értékekkel és objektumcímekkel. Könnyebb és gyorsabb az értelmező számára a dinamikus linkelés (egy egyszerű lineáris keresés egy azonosító táblában) végrehajtása, mint a lexikai hatókör állandó nyomon követése. Ezért az értelmezett nyelvek gyakrabban támogatják a dinamikus névkötést.

Névkötési jellemzők

A névkötés dinamikus és lexikális megközelítésén belül is előfordulhatnak árnyalatok, amelyek egy-egy programozási nyelv sajátosságaihoz vagy akár annak megvalósításához köthetők. Példaként vegyünk két C-szerű programozási nyelvet: a JavaScriptet és a Go . A nyelvek szintaktikailag meglehetősen közel állnak egymáshoz, és mindkettő lexikális hatókört használ, de ennek ellenére eltérnek a megvalósítás részleteiben.

A helyi név hatókörének kezdete

A következő példa két szövegesen hasonló kódrészletet mutat be JavaScriptben és Goban. Mindkét esetben scopea "global" karakterlánccal inicializált változót deklarálják a globális hatókörben, és f()először a hatókör értékét vezetik le a függvényben, majd egy azonos nevű változó lokális deklarációját a "local" karakterlánccal inicializálják. , és végül a rendszer újra kikövetkezteti az értéket scope. f()Az alábbiakban minden esetben a függvény végrehajtásának tényleges eredménye látható .

JavaScript megy
var hatókör = "global" ; function f () { alert ( hatókör ); // ? var hatókör = "helyi" ; riasztás ( hatókör ); } var hatókör = "global" func f () { fmt . println ( hatókör ) //? var Score = "local" fmt . println ( hatókör ) }
undefined
local
globális
lokális

Könnyen belátható, hogy a különbség abban rejlik, hogy a kérdőjellel ellátott megjegyzéssel megjelölt sorban milyen érték jelenik meg.

  • A JavaScriptben a helyi változó hatóköre a teljes függvény , beleértve a deklaráció előtti részét is; ebben az esetben ennek a változónak az inicializálása csak annak a sornak a feldolgozása során történik, ahol a változó található. Az első híváskor a alert(scope)helyi változó hatóköre már létezik és elérhető, de még nem kapott értéket, vagyis a nyelv szabályai szerint speciális értéke van undefined. Ezért a megjelölt sorban az "undefined" fog megjelenni.
  • A Go egy hagyományosabb megközelítést használ ehhez a nyelvtípushoz, ahol a név hatóköre a deklarált sorban kezdődik. Ezért a függvényen belül f(), de egy lokális változó deklarálása előttscope ez a változó nem elérhető, és a kérdőjellel jelölt parancs a globális változó értékét írja ki scope, azaz "global".

Láthatóság letiltása

A lexikai hatókör szemantikájának egy másik árnyalata az úgynevezett "blokk láthatóság" megléte vagy hiánya, vagyis az a képesség, hogy egy lokális változót nemcsak függvényen, eljáráson vagy modulon belül deklaráljunk, hanem egy különálló blokkon belül is. parancsok (C-szerű nyelveken - zárójelek között {}). A következő példa azonos kódra két nyelven, különböző eredményeket adva a függvény végrehajtásához f().

JavaScript megy
függvény f () { var x = 3 ; figyelmeztetés ( x ); for ( var i = 10 ; i < 30 ; i += 10 ) { var x = i ; figyelmeztetés ( x ); } figyelmeztetés ( x ); // ? } func f () { var x = 3 fmt . Println ( x ) i : = 10 esetén ; i < 30 ; i += 10 { var x = i fmt . println ( x ) } fmt . println ( x ) // ? }
3
10
20
20
3
10
20
3

f()A különbség abban van, hogy a megjegyzésben kérdőjellel jelölt függvény utolsó utasítása milyen értéket ad ki .

  • A JavaScript nem rendelkezett blokk hatókörrel (az ES6 bevezetése előtt), és a helyi változó újradeklarálása ugyanúgy működik, mint egy normál hozzárendelés. A hurkon belüli xértékek hozzárendelése megváltoztatja az egyetlen helyi változót , amelyet a függvény elején deklaráltak. Ezért a ciklus vége után a változó megtartja a ciklusban utoljára hozzárendelt értéket. Ennek eredményeként ez az érték jelenik meg.iforxx
  • A Go-ban egy utasításblokk helyi hatókört képez, a cikluson belül deklarált változó x pedig egy új változó, amelynek hatóköre csak a ciklus törzse; felülírja xa függvény elején deklarált. Ez a "kétszeresen lokális" változó a ciklus minden lépésében új értéket kap, és a kimenetre kerül, de változásai nem érintik a cikluson kívül deklarált változót x. A ciklus vége után a benne deklarált változó хmegszűnik, és az első xismét láthatóvá válik. Értéke változatlan marad, és ennek eredményeként megjelenik.

Objektumok láthatósága és létezése

Az azonosító láthatóságát nem szabad egyenlővé tenni annak az értéknek a meglétével, amelyhez az azonosító társul. A név láthatósága és egy objektum létezése közötti kapcsolatot a program logikája és az objektum tárolási osztálya befolyásolja. Az alábbiakban néhány tipikus példát mutatunk be.

  • Azon változók esetében, amelyek memóriája dinamikusan (a kupacban ) kerül lefoglalásra és felszabadításra, a láthatóság és a létezés bármilyen aránya lehetséges. Egy változó deklarálható, majd inicializálható, ebben az esetben a névnek megfelelő objektum valóban később jelenik meg, mint a hatókör bejegyzése. De az objektum előre elkészíthető, tárolható, majd változóhoz rendelhető, azaz korábban is megjelenhet. Ugyanez a törléssel: a dinamikus objektumhoz tartozó változó esetén a delete parancs meghívása után maga a változó látható marad, de az értéke nem létezik, és az elérése megjósolhatatlan eredményekhez vezet. Másrészt, ha nem hívják meg a delete parancsot, akkor a dinamikus memóriában lévő objektum akkor is fennmaradhat, ha a rá hivatkozó változó kikerült a hatókörből.
  • A statikus tárolási osztályú helyi változóknál (C és C++ nyelven) az érték (logikailag) a program indításakor jelenik meg. Ebben az esetben a név csak a tartalmi függvény végrehajtása során kerül hatókörbe. Ezenkívül a függvények közötti intervallumokban az érték megmarad.
  • Az automatikus (C terminológiában) változók, amelyek egy függvénybe való belépéskor jönnek létre, és kilépéskor megsemmisülnek, addig léteznek, amíg a nevük látható. Vagyis számukra a rendelkezésre állás és a létezés ideje gyakorlatilag egybeesőnek tekinthető.

Példák

Xi

// Elindul a globális hatókör. int countOfUser = 0 ; int main () { // Mostantól új hatókört deklarálunk, amelyben a globális látható. int userNumber [ 10 ]; } #include <stdio.h> int a = 0 ; // globális változó int main () { printf ( "%d" , a ); // a 0-s szám jelenik meg { int a = 1 ; // az a lokális változó deklarálva van, az a globális változó nem látható printf ( "%d" , a ); // az 1-es szám jelenik meg { int a = 2 ; // továbbra is lokális változó a blokkban, az a globális változó nem látható, és az előző lokális változó nem látható printf ( "%d" , a ); // a 2-es szám jelenik meg } } }

Jegyzetek

  1. HTML nyelvi specifikáció Archív másolat 2012. december 4-én a Wayback Machine -nél , fordító: A. Piramidin, intuit.ru, ISBN 978-5-94774-648-8 , 17. Előadás: Űrlapok.
  2. Hatókör . Letöltve: 2013. március 11. Az eredetiből archiválva : 2019. november 26..