Ukazatel (počítačové programování) - Pointer (computer programming)

Považuji prohlášení o přiřazení a proměnné ukazatele za „nejcennější poklady“ počítačové vědy.

Donald Knuth , strukturované programování, s přejděte na příkazy

Ukazatel a ukazuje na adresu paměti přidruženou k proměnné b . V tomto diagramu výpočetní architektura používá stejný adresní prostor a datový primitiv pro ukazatele i ukazatele; tato potřeba by neměla být.

Ve vědě o počítačích , je ukazatel je objekt v mnoha programovacích jazycích , který ukládá adresu paměti . Může to být hodnota jiné hodnoty umístěné v paměti počítače nebo v některých případech hardwaru počítače mapovaného v paměti . Ukazatel odkazuje na umístění v paměti a získání hodnoty uložené v tomto místě se nazývá dereferencování ukazatele. Analogicky lze číslo stránky v rejstříku knihy považovat za ukazatel na odpovídající stránku; dereferencování takového ukazatele by bylo provedeno převrácením na stránku s daným číslem stránky a čtením textu nalezeného na této stránce. Skutečný formát a obsah proměnné ukazatele závisí na základní architektuře počítače .

Použití ukazatelů výrazně zlepšuje výkon pro opakující se operace, jako je procházení iterovatelných datových struktur (např. Řetězce , vyhledávací tabulky , řídicí tabulky a stromové struktury). Zejména je často mnohem levnější v čase a prostoru kopírovat a dereferovat ukazatele, než kopírovat a přistupovat k datům, na která ukazatele ukazují.

Ukazatele se také používají k uchovávání adres vstupních bodů pro volané podprogramy v procedurálním programování a pro propojení za běhu s dynamickými knihovnami odkazů (DLL) . V objektově orientovaném programování se ukazatele na funkce používají pro metody vazby , často pomocí tabulek virtuálních metod .

Ukazatel je jednoduchá, konkrétnější implementace abstraktnějšího referenčního datového typu . Několik jazyků, zejména jazyků nízké úrovně , podporuje určitý typ ukazatele, i když některé mají při používání více omezení než jiné. Zatímco „ukazatel“ byl používán k odkazování na odkazy obecně, vhodněji se vztahuje na datové struktury, jejichž rozhraní výslovně umožňuje manipulaci s ukazatelem (aritmeticky přes aritmetika ukazatele ) jako adresa paměti, na rozdíl odmagickéhosouborucookieneboschopnosti,která to neumožňuje. Protože ukazatele umožňují chráněný i nechráněný přístup k adresám paměti, jsou s jejich používáním spojena rizika, zejména v druhém případě. Primitivní ukazatele jsou často uloženy ve formátu podobnémcelému číslu; pokus o dereference nebo „vyhledání“ takového ukazatele, jehož hodnota není platnou adresou paměti, však může způsobitzhrouceníprogramu(nebo obsahovat neplatná data). Aby se tento potenciální problém zmírnil, jsou z hlediskabezpečnosti typuukazatele považovány za samostatný typ parametrizovaný typem dat, na která ukazují, i když je základní reprezentací celé číslo. Mohou být také přijata další opatření (jako jeověřováníakontrola hranic), aby se ověřilo, že proměnná ukazatele obsahuje hodnotu, která je jak platnou adresou paměti, tak v číselném rozsahu, který je procesor schopen adresovat.

Dějiny

V roce 1955 sovětská počítačová vědkyně Kateryna Juščenko vynalezla programovací jazyk adres , který umožňoval nepřímé adresování a adresy nejvyšší úrovně - analogické s ukazateli. Tento jazyk byl široce používán na počítačích Sovětského svazu. Mimo Sovětský svaz to však nebylo známé a Haroldovi Lawsonovi se obvykle připisuje vynález ukazatele v roce 1964. V roce 2000 byla Lawsonovi udělena cena Computer Pioneer Award od IEEE „[f] nebo vynalezla proměnnou ukazatele a zavedla tento koncept do PL/I, čímž poprvé poskytla schopnost flexibilně zpracovávat propojené seznamy v obecném účelu jazyk na vysoké úrovni “. Jeho klíčový článek o pojmech se objevil v červnovém čísle CACM s názvem: Zpracování seznamu PL/I. Podle Oxfordského anglického slovníku se slovo ukazatel poprvé objevilo v tisku jako ukazatel zásobníku v technickém memorandu společnosti System Development Corporation .

Formální popis

V počítačové vědě je ukazatel jakousi referencí .

Dat primitivní (nebo jen primitivní ) je jakýkoli údaj, který lze číst nebo zápis do paměti počítače pomocí jednoho přístupu do paměti (například, jak byte a slova jsou primitiva).

Dat agregát (nebo jen agregát ) je skupina primitiva, které jsou logicky souvislé v paměti a které jsou zobrazeny společně jako jeden vztažný bod (například agregát může být 3 logicky souvislých bajtů, jejichž hodnoty, které představují 3 souřadnic bod v prostoru). Pokud je agregát zcela složen ze stejného typu primitiva, může být agregát nazýván maticí ; v jistém smyslu je primitivum vícebajtových slov pole bytů a některé programy používají slova tímto způsobem.

V kontextu těchto definic je bajt nejmenším primitivem; každá adresa paměti určuje jiný bajt. Adresa paměti počátečního bajtu nulového bodu je považována za adresu paměti (nebo adresu základní paměti ) celého data.

Paměť ukazatel (nebo jen ukazatel ) je primitivní, jehož hodnota je určena k použití jako adresa paměti; říká se, že ukazatel ukazuje na adresu paměti . Rovněž se říká, že ukazatel ukazuje na datum [v paměti], když je hodnota ukazatele adresou paměti data.

Obecněji je ukazatel jakousi referencí a říká se, že ukazatel odkazuje na datum uložené někde v paměti ; k získání tohoto data je dereference ukazatele . Funkce, která odděluje ukazatele od jiných druhů odkazů, spočívá v tom, že hodnota ukazatele má být interpretována jako adresa paměti, což je koncept spíše nízké úrovně.

Reference slouží jako úroveň indirection: Hodnota ukazatele určuje, která adresa paměti (tj. Který datum) má být použita při výpočtu. Protože indirection je základním aspektem algoritmů, ukazatele jsou často vyjádřeny jako základní datový typ v programovacích jazycích ; ve staticky (nebo silně ) napsaných programovacích jazycích typ ukazatele určuje typ vztažného bodu, na který ukazatel ukazuje.

Architektonické kořeny

Ukazatele jsou velmi tenkou abstrakcí nad možnostmi adresování, které poskytuje většina moderních architektur . V nejjednodušším režimu, což je adresa nebo číselný index , je přiřazen ke každé jednotce paměti v systému, kde jednotka je typicky jeden byte nebo slovo - v závislosti na tom, zda architektura je byte-adresovatelné nebo word-adresovatelné - efektivně transformuje veškerou paměť do velmi velkého pole . Systém by pak také poskytl operaci k získání hodnoty uložené v paměťové jednotce na dané adrese (obvykle s využitím obecných registrů stroje ).

V obvyklém případě je ukazatel dostatečně velký, aby pojal více adres, než kolik je jednotek paměti v systému. To zavádí možnost, že se program může pokusit o přístup k adrese, která neodpovídá žádné jednotce paměti, buď proto, že není nainstalováno dostatek paměti (tj. Mimo rozsah dostupné paměti), nebo architektura takové adresy nepodporuje. První případ může být na určitých platformách, jako je architektura Intel x86 , nazýván poruchou segmentace (segfault). Druhý případ je možný v současné implementaci AMD64 , kde jsou ukazatele 64 bitů dlouhé a adresy se rozšiřují pouze na 48 bitů. Ukazatele musí vyhovovat určitým pravidlům (kanonické adresy), takže pokud je dereferencován nekanonický ukazatel, procesor vyvolá obecnou chybu ochrany .

Na druhou stranu některé systémy mají více jednotek paměti, než kolik je adres. V tomto případě se používá složitější schéma, jako je segmentace paměti nebo stránkování, pro použití různých částí paměti v různých časech. Poslední inkarnace architektury x86 podporují až 36 bitů adres fyzické paměti, které byly mapovány do 32bitového lineárního adresního prostoru prostřednictvím mechanismu stránkování PAE . Současně lze tedy přistupovat pouze k 1/16 možné celkové paměti. Jiným příkladem v téže rodiny počítače byl 16-ti bitový chráněný režim na 80286 procesoru, který, ačkoli podporovat pouze 16 MB fyzické paměti, mohl přistupovat až 1 GB virtuální paměti, ale kombinace 16bitové a adresu segmentu registry ztěžovaly přístup k více než 64 kB v jedné datové struktuře.

Aby bylo zajištěno konzistentní rozhraní, poskytují některé architektury I/O mapované v paměti , které umožňuje některým adresám odkazovat na jednotky paměti, zatímco jiné odkazují na registry zařízení jiných zařízení v počítači. Existují analogické koncepty, jako jsou offsety souborů, indexy polí a odkazy na vzdálené objekty, které slouží ke stejným účelům jako adresy pro jiné typy objektů.

Využití

Ukazatele jsou přímo podporovány bez omezení v jazycích jako PL/I , C , C ++ , Pascal , FreeBASIC a implicitně ve většině montážních jazyků . Používají se především pro konstrukci odkazů , které jsou zase zásadní pro konstrukci téměř všech datových struktur , stejně jako pro předávání dat mezi různými částmi programu.

Ve funkčních programovacích jazycích, které se do značné míry spoléhají na seznamy, jsou datové odkazy spravovány abstraktně pomocí primitivních konstrukcí, jako jsou nevýhody a odpovídající prvky car a cdr , které lze považovat za specializované ukazatele na první a druhou součást buňky Cons. To dává vzniknout určité idiomatické „příchuti“ funkčního programování. Strukturováním dat v takových seznamech záporů tyto jazyky usnadňují rekurzivní prostředky pro vytváření a zpracování dat-například rekurzivním přístupem k hlavičkovým a koncovým prvkům seznamů seznamů; např. „převzetí auta z cdr z cdr“. Naproti tomu správa paměti založená na dereferenci ukazatelů při určité aproximaci pole adres paměti usnadňuje zpracování proměnných jako slotů, do kterých lze imperativně přiřadit data .

Při práci s poli kritická operace vyhledávání obvykle zahrnuje fázi nazvanou výpočet adresy, která zahrnuje konstrukci ukazatele na požadovaný datový prvek v poli. V jiných datových strukturách, jako jsou propojené seznamy , se ukazatele používají jako odkazy pro explicitní propojení jedné části struktury s jinou.

Ukazatele se používají k předávání parametrů odkazem. To je užitečné v případě, že programátor chce, aby změny funkce parametru byly viditelné pro volajícího funkce. To je také užitečné pro vrácení více hodnot z funkce.

Ukazatele lze také použít k přidělení a zrušení přidělení dynamických proměnných a polí v paměti. Protože proměnná se často stane nadbytečnou poté, co splnila svůj účel, je plýtvání pamětí její udržováním, a proto je vhodné ji uvolnit (pomocí odkazu na původní ukazatel), když již není potřeba. Pokud tak neučiníte, může dojít k nevracení paměti (pokud je volná paměť k dispozici postupně, nebo v závažných případech rychle, klesá kvůli akumulaci mnoha nadbytečných bloků paměti).

Ukazatele C.

Základní syntaxe pro definování ukazatele je:

int *ptr;

To deklaruje ptrjako identifikátor objektu následujícího typu:

  • ukazatel, který ukazuje na objekt typu int

Obvykle se to stručněji uvádí jako „ ptrje ukazatelem int“.

Protože jazyk C neurčuje implicitní inicializaci pro objekty s automatickým trváním úložiště, je třeba často dbát na to, aby adresa, na kterou ptrbody platí; proto se někdy navrhuje, aby byl ukazatel explicitně inicializován na hodnotu nulového ukazatele , která je tradičně zadána v jazyce C se standardizovaným makrem NULL:

int *ptr = NULL;

Dereferencování nulového ukazatele v C produkuje nedefinované chování , které by mohlo být katastrofické. Většina implementací však jednoduše zastaví provádění dotyčného programu, obvykle s poruchou segmentace .

Zbytečná inicializace ukazatelů by však mohla bránit analýze programu, a tím skrývat chyby.

V každém případě, jakmile je ukazatel deklarován, dalším logickým krokem je, aby na něco ukázal:

int a = 5;
int *ptr = NULL;

ptr = &a;

Tím se přiřadí hodnota adresy ana ptr. Pokud je například auloženo na paměťovém místě 0x8130, pak hodnota ptrbude po přiřazení 0x8130. Pro zrušení odkazu na ukazatel se znovu použije hvězdička:

*ptr = 8;

To znamená, vezměte obsah ptr(což je 0x8130), „vyhledejte“ tuto adresu v paměti a nastavte její hodnotu na 8. Pokud ase k ní později dostanete znovu, její nová hodnota bude 8.

Tento příklad může být jasnější, pokud je paměť prozkoumána přímo. Předpokládejme, že aje umístěn na adrese 0x8130 v paměti a ptrna 0x8134; předpokládejme také, že se jedná o 32bitový stroj, takže int je široký 32 bitů. Následuje to, co by bylo v paměti po provedení následujícího fragmentu kódu:

int a = 5;
int *ptr = NULL;
Adresa Obsah
0x8130 0x00000005
0x8134 0x00000000

(NULL pointer ukázaný tady je 0x00000000). Přiřazením adresy ana ptr:

 ptr = &a;

poskytuje následující hodnoty paměti:

Adresa Obsah
0x8130 0x00000005
0x8134 0x00008130

Poté dereferencováním ptrkódováním:

 *ptr = 8;

počítač převezme obsah ptr(což je 0x8130), „vyhledá“ tuto adresu a přiřadí 8 tomuto umístění, čímž získá následující paměť:

Adresa Obsah
0x8130 0x00000008
0x8134 0x00008130

Je jasné, že přístup apřinese hodnotu 8, protože předchozí instrukce upravila obsah apomocí ukazatele ptr.

Použití v datových strukturách

Při nastavování datových struktur, jako jsou seznamy , fronty a stromy, je nutné mít ukazatele, které pomohou spravovat implementaci a řízení struktury. Typickými příklady ukazatelů jsou počáteční ukazatele, koncové ukazatele a ukazatele zásobníku . Tyto ukazatele mohou být buď absolutní (skutečná fyzická adresa nebo virtuální adresa ve virtuální paměti ) nebo relativní ( posun od absolutní počáteční adresy („základna“), která obvykle používá méně bitů než úplná adresa, ale obvykle bude vyžadovat jeden další vyřešit aritmetickou operaci).

Relativní adresy jsou formou manuální segmentace paměti a sdílejí mnoho jejích výhod a nevýhod. Dvoubajtový offset obsahující 16bitové celé číslo bez znaménka lze použít k zajištění relativního adresování až 64 KiB (2 16 bajtů) datové struktury. To lze snadno rozšířit na 128, 256 nebo 512 KiB, pokud je adresa, na kterou je odkazováno, nucena zarovnat na hranici polovičního slova, slova nebo dvou slov (ale vyžaduje další bitovou operaci „posun vlevo“ -o 1, 2 nebo 3 bity - za účelem úpravy offsetu o faktor 2, 4 nebo 8 před jeho přidáním k základní adrese). Obecně jsou však taková schémata velkým problémem a pro pohodlí programátora jsou upřednostňovány absolutní adresy (a základem je plochý adresní prostor ).

Jednobajtový offset, jako je hexadecimální hodnota ASCII znaku (např. X'29 '), lze použít k označení alternativní celočíselné hodnoty (nebo indexu) v poli (např. X'01'). Tímto způsobem lze znaky velmi efektivně překládat z „ nezpracovaných dat “ do použitelného sekvenčního indexu a poté na absolutní adresu bez vyhledávací tabulky .

C pole

V C je indexování pole formálně definováno z hlediska aritmetiky ukazatele; to znamená, že jazyková specifikace vyžaduje, aby array[i]byla ekvivalentní *(array + i). V C tedy lze pole chápat jako ukazatele na po sobě jdoucí oblasti paměti (bez mezer) a syntaxe pro přístup k maticím je stejná pro tu, kterou lze použít k dereferenci ukazatelů. Pole arraylze například deklarovat a použít následujícím způsobem:

int array[5];      /* Declares 5 contiguous integers */
int *ptr = array;  /* Arrays can be used as pointers */
ptr[0] = 1;        /* Pointers can be indexed with array syntax */
*(array + 1) = 2;  /* Arrays can be dereferenced with pointer syntax */
*(1 + array) = 2;  /* Pointer addition is commutative */
array[2] = 4;      /* Subscript operator is commutative */

Tím se přidělí blok pěti celých čísel a pojmenuje blok array, který funguje jako ukazatel na blok. Dalším běžným používáním ukazatelů je ukazovat na dynamicky přidělovanou paměť z malloc, která vrací po sobě jdoucí blok paměti ne menší než požadovanou velikost, kterou lze použít jako pole.

Zatímco většina operátorů na polích a ukazatelích je ekvivalentní, výsledek sizeofoperátoru se liší. V tomto případě sizeof(array)vyhodnotí 5*sizeof(int)(velikost pole), zatímco sizeof(ptr)vyhodnotí sizeof(int*), velikost samotného ukazatele.

Výchozí hodnoty pole lze deklarovat jako:

int array[5] = {2, 4, 3, 1, 5};

Pokud arrayje umístěn v paměti začínající na adrese 0x1000 na 32bitovém počítači s malým endianem, pak paměť bude obsahovat následující (hodnoty jsou v hexadecimálním formátu, jako jsou adresy):

0 1 2 3
1000 2 0 0 0
1004 4 0 0 0
1008 3 0 0 0
100C 1 0 0 0
1010 5 0 0 0

Zde je zastoupeno pět celých čísel: 2, 4, 3, 1 a 5. Těchto pět celých čísel zabírá 32 bitů (4 bajty), přičemž každý nejméně významný bajt je uložen jako první (jedná se o málo endianovou architekturu CPU ) a jsou uloženy postupně začínající na adrese 0x1000.

Syntaxe pro C s ukazateli je:

  • array znamená 0x1000;
  • array + 1znamená 0x1004: „+ 1“ znamená přidat velikost 1 int, což jsou 4 bajty;
  • *arrayznamená dereferenci obsahu array. Vzhledem k obsahu jako adresy paměti (0x1000) vyhledejte hodnotu v tomto umístění (0x0002);
  • array[i]znamená číslo prvku i, založené na 0, do arraykterého je přeloženo *(array + i).

Posledním příkladem je přístup k obsahu array. Rozebrat to:

  • array + ije paměťové umístění (i) tého prvku array, začínající na i = 0;
  • *(array + i) vezme tuto adresu paměti a dereferences ji přístup k hodnotě.

C propojený seznam

Níže je příklad definice propojeného seznamu v C.

/* the empty linked list is represented by NULL
 * or some other sentinel value */
#define EMPTY_LIST  NULL

struct link {
    void        *data;  /* data of this link */
    struct link *next;  /* next link; EMPTY_LIST if there is none */
};

Tato rekurzivní definice ukazatele je v podstatě stejná jako referenční rekurzivní definice z programovacího jazyka Haskell :

 data Link a = Nil
             | Cons a (Link a)

Nilje prázdný seznam a Cons a (Link a)je buňkou proti typu as dalším odkazem také typu a.

Definice s odkazy je však zkontrolována podle typu a nepoužívá potenciálně matoucí hodnoty signálu. Z tohoto důvodu se s datovými strukturami v jazyce C obvykle pracuje pomocí funkcí obálky , u nichž se pečlivě kontroluje správnost.

Předávací adresa pomocí ukazatelů

Ukazatele lze použít k předávání proměnných podle jejich adresy, což umožňuje změnu jejich hodnoty. Zvažte například následující kód C :

/* a copy of the int n can be changed within the function without affecting the calling code */
void passByValue(int n) {
    n = 12;
}

/* a pointer m is passed instead. No copy of the value pointed to by m is created */
void passByAddress(int *m) {
    *m = 14;
}

int main(void) {
    int x = 3;

    /* pass a copy of x's value as the argument */
    passByValue(x);
    // the value was changed inside the function, but x is still 3 from here on

    /* pass x's address as the argument */
    passByAddress(&x);
    // x was actually changed by the function and is now equal to 14 here

    return 0;
}

Dynamické přidělování paměti

V některých programech závisí požadovaná paměť na tom, co může uživatel zadat. V takových případech musí programátor přidělit paměť dynamicky. To se provádí přidělením paměti na haldě, nikoli na zásobníku , kde jsou obvykle uloženy proměnné (proměnné lze ukládat také do registrů CPU, ale to je jiná věc). Dynamické přidělení paměti lze provést pouze pomocí ukazatelů a názvy (jako u běžných proměnných) nelze zadat.

Ukazatele se používají k ukládání a správě adres dynamicky přidělených bloků paměti. Takové bloky se používají k ukládání datových objektů nebo polí objektů. Většina strukturovaných a objektově orientovaných jazyků poskytuje oblast paměti, nazývanou halda nebo volné úložiště , ze které jsou objekty dynamicky přidělovány.

Následující kód C níže ukazuje, jak jsou dynamicky přidělovány a odkazovány objekty struktury. Standardní knihovna C poskytuje funkci malloc()pro přidělování paměťové bloky z haldy. Trvá velikost objektu, který má být přidělen jako parametr, a vrátí ukazatel na nově přidělený blok paměti vhodný pro uložení objektu, nebo vrátí nulový ukazatel, pokud se přidělení nezdařilo.

/* Parts inventory item */
struct Item {
    int         id;     /* Part number */
    char *      name;   /* Part name   */
    float       cost;   /* Cost        */
};

/* Allocate and initialize a new Item object */
struct Item * make_item(const char *name) {
    struct Item * item;

    /* Allocate a block of memory for a new Item object */
    item = malloc(sizeof(struct Item));
    if (item == NULL)
        return NULL;

    /* Initialize the members of the new Item */
    memset(item, 0, sizeof(struct Item));
    item->id =   -1;
    item->name = NULL;
    item->cost = 0.0;

    /* Save a copy of the name in the new Item */
    item->name = malloc(strlen(name) + 1);
    if (item->name == NULL) {
        free(item);
        return NULL;
    }
    strcpy(item->name, name);

    /* Return the newly created Item object */
    return item;
}

Níže uvedený kód ukazuje, jak jsou objekty paměti dynamicky uvolňovány, tj. Vráceny do haldy nebo do volného úložiště. Standardní knihovna C poskytuje funkci free()pro opětovné umístění dříve přiděleného bloku paměti a jeho vrácení zpět na haldu.

/* Deallocate an Item object */
void destroy_item(struct Item *item) {
    /* Check for a null object pointer */
    if (item == NULL)
        return;

    /* Deallocate the name string saved within the Item */
    if (item->name != NULL) {
        free(item->name);
        item->name = NULL;
    }

    /* Deallocate the Item object itself */
    free(item);
}

Hardware mapovaný na paměti

U některých výpočetních architektur lze ukazatele použít k přímé manipulaci s pamětí nebo zařízeními mapovanými na paměť.

Přiřazování adres ukazatelům je neocenitelný nástroj při programování mikrokontrolérů . Níže je jednoduchý příklad, který deklaruje ukazatel typu int a inicializuje jej na hexadecimální adresu, v tomto příkladu konstantu 0x7FFF:

int *hardware_address = (int *)0x7FFF;

V polovině 80. let bylo používání systému BIOS pro přístup k video schopnostem počítačů pomalé. Aplikace, které byly náročné na zobrazení, se obvykle používaly pro přímý přístup k video paměti CGA přetypováním hexadecimální konstanty 0xB8000 na ukazatel na pole 80 nepodepsaných 16bitových hodnot int. Každá hodnota se skládala z kódu ASCII v dolním bajtu a barvy ve vysokém bajtu. Chcete -li tedy umístit písmeno „A“ na řádek 5, sloupec 2 v jasně bílé na modrou, napíše se kód jako následující:

#define VID ((unsigned short (*)[80])0xB8000)

void foo(void) {
    VID[4][1] = 0x1F00 | 'A';
}

Použití v řídicích tabulkách

Řídicí tabulky, které se používají k řízení toku programu, obvykle hojně využívají ukazatele. Ukazatele, obvykle vložené do záznamu v tabulce, mohou být například použity k uchování vstupních bodů k podprogramům, které mají být provedeny, na základě určitých podmínek definovaných ve stejném záznamu tabulky. Ukazatele však mohou být jednoduše indexy na jiné oddělené, ale přidružené tabulky obsahující pole skutečných adres nebo samotné adresy (v závislosti na dostupných konstrukcích programovacího jazyka). Lze je také použít k nasměrování na dřívější položky tabulky (jako při zpracování smyčky) nebo vpřed k přeskočení některých záznamů v tabulce (jako při přepnutí nebo „předčasném“ opuštění smyčky). Pro tento druhý účel může být „ukazatel“ jednoduše samotné číslo položky tabulky a lze jej jednoduchou aritmetikou převést na skutečnou adresu.

Zadané ukazatele a casting

V mnoha jazycích mají ukazatele další omezení, že objekt, na který ukazují, má konkrétní typ . Například ukazatel může být deklarován tak, aby ukazoval na celé číslo ; jazyk se poté pokusí zabránit programátoru v nasměrování na objekty, které nejsou celá čísla, jako jsou čísla s plovoucí desetinnou čárkou , čímž se odstraní některé chyby.

Například v C.

int *money;
char *bags;

moneyby byl celočíselný ukazatel a byl bagsby ukazatelem znaku. Následující by poskytlo upozornění kompilátoru na „přiřazení z nekompatibilního typu ukazatele“ pod GCC

bags = money;

protože moneya bagsbyly deklarovány s různými typy. Potlačit varování kompilátoru, musí být výslovně uvedeno, že jste si skutečně přejete, aby byl úkol podle typecasting ji

bags = (char *)money;

který říká, že přetypovat celočíselný ukazatel moneyna ukazatel char a přiřadit bags.

Návrh normy C z roku 2005 vyžaduje, aby seslání ukazatele odvozeného z jednoho typu na jiný typ zachovalo správnost zarovnání pro oba typy (6.3.2.3 Ukazatele, odst. 7):

char *external_buffer = "abcdef";
int *internal_data;

internal_data = (int *)external_buffer;  // UNDEFINED BEHAVIOUR if "the resulting pointer
                                         // is not correctly aligned"

V jazycích, které umožňují aritmetiku ukazatelů, aritmetika na ukazatelích bere v úvahu velikost typu. Například přidání celočíselného čísla na ukazatel vytvoří další ukazatel, který ukazuje na adresu, která je o toto číslo vyšší než je velikost typu. To nám umožňuje snadno vypočítat adresu prvků pole daného typu, jak bylo ukázáno v příkladu C polí výše. Když je ukazatel jednoho typu přetypován na jiný typ jiné velikosti, měl by programátor očekávat, že aritmetika ukazatele bude vypočtena odlišně. Například v C, pokud moneypole začíná na 0x2000 a sizeof(int)je 4 bajty, zatímco sizeof(char)je 1 bajt, pak money + 1bude ukazovat na 0x2004, ale bags + 1bude ukazovat na 0x2001. Mezi další rizika přetypování patří ztráta dat, když se „široká“ data zapisují do „úzkých“ míst (např. bags[0] = 65537;), Neočekávané výsledky při bitovém posunu hodnot a problémy se srovnáváním, zejména u hodnot se znaménkem a bez znaménka.

Ačkoli není obecně možné v době kompilace určit, která přetypování jsou bezpečná, některé jazyky ukládají informace o typu běhu, které lze použít k potvrzení, že tyto nebezpečné přetypování jsou platné za běhu. Jiné jazyky pouze přijímají konzervativní sbližování bezpečných vrhů, nebo vůbec žádné.

Hodnota ukazatelů

V C a C ++ je výsledek porovnání mezi ukazateli nedefinovaný. V těchto jazycích a LLVM je pravidlo vykládáno tak, že „jen proto, že dva ukazatele ukazují na stejnou adresu, neznamená to, že jsou si rovny a lze je zaměňovat“, je rozdíl mezi ukazateli označován jako jejich provenience . Ačkoli přetypování na celočíselný typ, jako je uintptr_tnabídka porovnání, samotné přetypování je definováno implementací. Další konverze na bajty a aritmetiku navíc odhodí optimalizátory, kteří se snaží sledovat používání ukazatelů, což je problém, který se v akademickém výzkumu stále objasňuje.

Zajištění bezpečnosti ukazatelů

Protože ukazatel umožňuje programu pokusit se získat přístup k objektu, který nemusí být definován, ukazatele mohou být původem různých chyb programování . Užitečnost ukazatelů je však tak velká, že bez nich může být obtížné provádět programovací úkoly. V důsledku toho mnoho jazyků vytvořilo konstrukty navržené tak, aby poskytovaly některé užitečné funkce ukazatelů bez některých jejich nástrah , někdy také označovaných jako nebezpečí ukazatele . V tomto kontextu jsou ukazatele, které přímo adresují paměť (jak se používá v tomto článku), označovány jakoraw pointer s, na rozdíl odchytrých ukazatelůnebo jiných variant.

Jedním z hlavních problémů ukazatelů je to, že pokud s nimi lze přímo manipulovat jako s číslem, lze je upozornit na nepoužité adresy nebo data, která se používají k jiným účelům. Mnoho jazyků, včetně většiny funkčních programovacích jazyků a nejnovějších imperativních jazyků, jako je Java , nahrazuje ukazatele více neprůhledným typem odkazu, obvykle označovaným jako prostý odkaz , který lze použít pouze k odkazování na objekty a nemanipulovat s čísly, což tomu brání. typ chyby. Indexování pole je zpracováno jako speciální případ.

Ukazatel, kterému není přiřazena žádná adresa, se nazývá divoký ukazatel . Jakýkoli pokus o použití takových neinicializovaných ukazatelů může způsobit neočekávané chování, buď proto, že počáteční hodnota není platná adresa, nebo proto, že jeho použití může poškodit jiné části programu. Výsledkem je často chyba segmentace , narušení úložiště nebo divoká větev (pokud se používá jako ukazatel funkce nebo adresa pobočky).

V systémech s explicitní alokací paměti je možné vytvořit visící ukazatel uvolněním oblasti paměti, do které ukazuje. Tento typ ukazatele je nebezpečný a subtilní, protože oblast uvolněné paměti může obsahovat stejná data jako před přidělením, ale může být poté znovu přidělena a přepsána nesouvisejícím kódem, který dřívější kód neznal. Jazyky s uvolňováním paměti tomuto typu chyb zabraňují, protože zrušení přidělení se provádí automaticky, když v oboru již nejsou žádné odkazy.

Některé jazyky, jako C ++ , podporují inteligentní ukazatele , které kromě toho, že fungují jako reference , používají jednoduchou formu počítání referencí, která pomáhá sledovat přidělování dynamické paměti. Při absenci referenčních cyklů, kdy se objekt odkazuje na sebe nepřímo prostřednictvím sekvence chytrých ukazatelů, tyto eliminují možnost visících ukazatelů a úniků paměti. Řetězy Delphi podporují počítání referencí nativně.

Programovací jazyk Rust zavádí kontrolu půjčit , ukazatel životnosti a optimalizaci založený kolem volitelné typy pro ukazatele null eliminovat ukazatel chyby, aniž by se uchýlil k garbage collection .

Speciální druhy ukazatelů

Druhy definované hodnotou

Nulový ukazatel

Nulový ukazatel má hodnotu rezervovaná pro indikování, že ukazatel neodkazuje na platný objekt. Nulové ukazatele se běžně používají k vyjádření podmínek, jako je konec seznamu neznámé délky nebo neprovedení nějaké akce; toto použití nulových ukazatelů lze porovnat s typy s možnou hodnotou null a s hodnotou Nic v typu možnosti .

Visící ukazatel

Visící ukazatel je ukazatel, který neukazuje na platný objekt a následně mohou havárii programu nebo se chovají zvláštně. V programovacích jazycích Pascal nebo C mohou ukazatele, které nejsou konkrétně inicializovány, ukazovat na nepředvídatelné adresy v paměti.

Následující příklad kódu ukazuje visící ukazatel:

int func(void) {
    char *p1 = malloc(sizeof(char)); /* (undefined) value of some place on the heap */
    char *p2;       /* dangling (uninitialized) pointer */
    *p1 = 'a';      /* This is OK, assuming malloc() has not returned NULL. */
    *p2 = 'b';      /* This invokes undefined behavior */
}

Zde p2může ukazovat kamkoli v paměti, takže provedení přiřazení *p2 = 'b';může poškodit neznámou oblast paměti nebo spustit chybu segmentace .

Divoká větev

Pokud se jako adresa vstupního bodu programu nebo spuštění funkce, která nevrací nic a je také neinicializovaná nebo poškozená, používá ukazatel, je -li přesto na tuto adresu provedeno volání nebo skok , „ divoká větev“ “se prý stalo. Jinými slovy, divoká větev je ukazatel funkce, který je divoký (visící).

Následky jsou obvykle nepředvídatelné a chyba se může projevit několika různými způsoby v závislosti na tom, zda je ukazatel „platnou“ adresou a zda na této adrese je (shodou okolností) platná instrukce (opcode) či nikoli. Detekce divoké větve může představovat jedno z nejobtížnějších a frustrujících debugovacích cvičení, protože většina důkazů již mohla být zničena předem nebo provedením jedné nebo více nevhodných instrukcí v místě pobočky. Pokud je k dispozici, simulátor sady instrukcí může obvykle nejen detekovat divokou větev, než začne platit, ale také poskytnout úplnou nebo částečnou stopu její historie.

Druhy definované strukturou

Autorelativní ukazatel

Autorelative ukazatel je ukazatel, jehož hodnota je interpretována jako posun od adresy samotného ukazatele; pokud tedy datová struktura má člen autorelativního ukazatele, který ukazuje na nějakou část samotné datové struktury, pak může být datová struktura přemístěna do paměti, aniž by bylo nutné aktualizovat hodnotu automatického relativního ukazatele.

Citovaný patent také používá termín self-relative pointer, aby znamenal totéž. Význam tohoto pojmu byl však použit jinými způsoby:

  • znamenat odsazení od adresy struktury spíše než od adresy ukazatele samotného;
  • znamenat ukazatel obsahující vlastní adresu, který může být užitečný pro rekonstrukci kolekce datových struktur, které na sebe navzájem ukazují, v libovolné oblasti paměti.

Umístěný ukazatel

Ukazatel založený je ukazatel, jehož hodnota je posun od hodnoty jiného ukazatele. To lze použít k ukládání a načítání datových bloků a přiřazení adresy začátku bloku základnímu ukazateli.

Druhy definované použitím nebo datovým typem

Vícenásobné směrování

V některých jazycích může ukazatel odkazovat na jiný ukazatel, což vyžaduje více dereferenčních operací, aby se dostal na původní hodnotu. I když každá úroveň indirection může zvýšit náklady na výkon, někdy je to nutné k zajištění správného chování pro komplexní datové struktury . Například v jazyce C je typické definovat propojený seznam pomocí prvku, který obsahuje ukazatel na další prvek seznamu:

struct element {
    struct element *next;
    int            value;
};

struct element *head = NULL;

Tato implementace používá ukazatel na první prvek v seznamu jako náhradní pro celý seznam. Pokud je na začátek seznamu přidána nová hodnota, headmusí být změněna tak, aby odkazovala na nový prvek. Vzhledem k tomu, že argumenty C jsou vždy předávány podle hodnoty, použití dvojité indirekce umožňuje správnou implementaci vložení a má žádoucí vedlejší účinek odstranění speciálního kódu případu, který by řešil vložení na začátek seznamu:

// Given a sorted list at *head, insert the element item at the first
// location where all earlier elements have lesser or equal value.
void insert(struct element **head, struct element *item) {
    struct element **p;  // p points to a pointer to an element
    for (p = head; *p != NULL; p = &(*p)->next) {
        if (item->value <= (*p)->value)
            break;
    }
    item->next = *p;
    *p = item;
}

// Caller does this:
insert(&head, item);

V tomto případě, pokud je hodnota hodnoty itemmenší než hodnota head, je volající headřádně aktualizován na adresu nové položky.

Základní příklad je v argumentu argv na hlavní funkci v C (a C ++) , který je v prototypu uveden jako char **argv- to proto, že proměnná argvsamotná je ukazatelem na řadu řetězců (pole polí), takže *argvje ukazatel na 0. řetězec (podle konvence název programu) a **argvje 0. znakem 0. řetězce.

Ukazatel funkce

V některých jazycích může ukazatel odkazovat na spustitelný kód, tj. Může ukazovat na funkci, metodu nebo proceduru. Ukazatel funkce uloží adresa funkce po dobu tří měsíců. I když lze toto zařízení použít k dynamickému volání funkcí, je to často oblíbená technika autorů virů a jiného škodlivého softwaru.

int sum(int n1, int n2) {   // Function with two integer parameters returning an integer value
    return n1 + n2;
}

int main(void) {
    int a, b, x, y;
    int (*fp)(int, int);    // Function pointer which can point to a function like sum
    fp = &sum;              // fp now points to function sum
    x = (*fp)(a, b);        // Calls function sum with arguments a and b
    y = sum(a, b);          // Calls function sum with arguments a and b
}

Zadní ukazatel

Ve dvojitě propojených seznamech nebo stromových strukturách zadní ukazatel držený na prvku „ukazuje zpět“ na položku odkazující na aktuální prvek. Ty jsou užitečné pro navigaci a manipulaci na úkor většího využití paměti.

Simulace pomocí indexu pole

Je možné simulovat chování ukazatele pomocí indexu do (obvykle jednorozměrného) pole.

V první řadě pro jazyky, které nepodporují odkazy konkrétně, ale dělat podpůrné pole je pole může být myšlenka a zpracovány tak, jako by byl celý rozsah paměti (v rámci konkrétního pole) a jakýkoli index k ní může být myšlenka jako ekvivalent k obecnému registru v jazyce sestavení (který ukazuje na jednotlivé bajty, ale jehož skutečná hodnota je relativní ke začátku pole, nikoli k jeho absolutní adrese v paměti). Za předpokladu, že pole je, řekněme, souvislá 16 megabajtová datová struktura znaků , jednotlivé bajty (nebo řetězec souvislých bajtů v poli) lze přímo adresovat a manipulovat pomocí názvu pole s 31bitovým celým číslem bez znaménka jako simulovaným ukazatelem (to je docela podobné příkladu polí C uvedených výše). Aritmetiku ukazatele lze simulovat sčítáním nebo odčítáním z indexu s minimální dodatečnou režií ve srovnání s aritmetikou skutečných ukazatelů.

Je dokonce teoreticky možné pomocí výše uvedené techniky společně s vhodným simulátorem instrukční sady simulovat jakýkoli strojový kód nebo přechodný ( bajtový kód ) libovolného procesoru /jazyka v jiném jazyce, který vůbec nepodporuje ukazatele (například Java / JavaScript ). Aby toho bylo dosaženo, může být binární kód původně načten do souvislých bajtů pole, aby simulátor „četl“, interpretoval a konal zcela v paměti obsažené ve stejném poli. Pokud je to nutné, aby se zcela zabránilo problémům s přetečením vyrovnávací paměti , lze pro překladač obvykle provést kontrolu mezí (nebo pokud ne, ručně kódovat v simulátoru).

Podpora v různých programovacích jazycích

Ada

Ada je silně typovaný jazyk, kde jsou zadány všechny ukazatele a jsou povoleny pouze bezpečné převody typů. Všechny ukazatele jsou ve výchozím nastavení inicializovány nulla jakýkoli pokus o přístup k datům pomocí nullukazatele způsobí vyvolání výjimky . Ukazatele v Ada se nazývají typy přístupu . Ada 83 nepovolila aritmetiku na typech přístupu (ačkoli mnoho prodejců kompilátorů to poskytlo jako nestandardní funkci), ale Ada 95 podporuje „bezpečnou“ aritmetiku na typech přístupu prostřednictvím balíčku System.Storage_Elements.

ZÁKLADNÍ

Několik starých verzí BASIC pro platformu Windows mělo podporu pro STRPTR () pro vrácení adresy řetězce a pro VARPTR () pro vrácení adresy proměnné. Visual Basic 5 měl také podporu pro OBJPTR () pro vrácení adresy rozhraní objektu a pro operátor ADDRESSOF pro vrácení adresy funkce. Typy všech těchto typů jsou celá čísla, ale jejich hodnoty jsou ekvivalentní hodnotám drženým typy ukazatelů.

Novější dialekty BASIC , jako FreeBASIC nebo BlitzMax , mají vyčerpávající implementace ukazatel, nicméně. Ve FreeBASIC se s aritmetikou ANYukazatelů (ekvivalent C void*) zachází, jako by ANYukazatel byl o šířce bajtů. ANYukazatele nelze dereferencovat, jako v C. Také přetypování mezi ANYa jiné ukazatele typu nebude generovat žádná varování.

dim as integer f = 257
dim as any ptr g = @f
dim as integer ptr i = g
assert(*i = 257)
assert( (g + 4) = (@f + 1) )

C a C ++

V C a C ++ jsou ukazatele proměnné, které ukládají adresy a mohou mít hodnotu null . Každý ukazatel má typ, na který ukazuje, ale lze libovolně přetypovat mezi typy ukazatelů (nikoli však mezi ukazatel funkce a ukazatel objektu). Speciální typ ukazatele nazývaný „prázdný ukazatel“ umožňuje ukazování na jakýkoli (nefunkční) objekt, ale je omezen skutečností, že jej nelze dereferencovat přímo (bude přetypován). Adresu samotnou lze často přímo manipulovat přenesením ukazatele na a z integrálního typu dostatečné velikosti, ačkoli výsledky jsou definovány implementací a mohou skutečně způsobit nedefinované chování; zatímco dřívější standardy C neměly integrální typ, který byl zaručeně dostatečně velký, C99 specifikuje název uintptr_t typedef definovaný v <stdint.h>, ale implementace jej nemusí poskytovat.

C ++ plně podporuje ukazatele C a casting C. Podporuje také novou skupinu operátorů castingu, které pomáhají zachytit některá nechtěná nebezpečná seslání při kompilaci. Vzhledem k tomu, C ++ 11 je standardní knihovny C ++ také inteligentní ukazatele ( unique_ptr, shared_ptra weak_ptr), které mohou být použity v některých situacích, jako bezpečnější alternativu primitivních ukazatelů C. C ++ také podporuje jinou formu odkazu, zcela odlišnou od ukazatele, nazývanou jednoduše odkaz nebo typ odkazu .

Aritmetika ukazatele , tj. Schopnost upravit cílovou adresu ukazatele pomocí aritmetických operací (stejně jako srovnání velikosti), je jazykovým standardem omezena tak, aby zůstala v mezích jednoho objektu pole (nebo těsně za ním) a bude jinak vyvolat nedefinované chování . Sečtením nebo odečtením od ukazatele se ukazatel přesune o násobek velikosti jeho datového typu . Například přidáním 1 k ukazateli na 4bajtové celočíselné hodnoty se zvýší namířená adresa ukazatele na bajt o 4. To má za následek zvýšení ukazatele tak, aby ukazoval na další prvek v souvislém poli celých čísel-což je často zamýšlený výsledek. Aritmetiku ukazatele nelze provádět na voidukazatelích, protože prázdný typ nemá žádnou velikost, a proto nelze přidat špičatou adresu, přestože gcc a jiné kompilátory budou provádět bajtovou aritmetiku void*jako nestandardní rozšíření a budou s ním zacházet, jako by bylo char *.

Aritmetika ukazatele poskytuje programátorovi jediný způsob řešení různých typů: sčítání a odčítání počtu požadovaných prvků namísto skutečného posunu v bajtech. (Aritmetika char *ukazatele s ukazateli používá bajtové offsety, protože sizeof(char)podle definice je 1.) Definice C zejména výslovně deklaruje, že syntaxe a[n], která je n-tým prvkem pole a, je ekvivalentní *(a + n), což je obsah prvku, na který je poukázáno od a + n. To znamená, že n[a]je ekvivalentní a[n]a lze například psát a[3]nebo 3[a]stejně dobře přistupovat ke čtvrtému prvku pole a.

I když je aritmetika ukazatele silná, může být zdrojem počítačových chyb . Začínající programátory bývá matoucí a nutí je do různých kontextů: výrazem může být obyčejný aritmetický nebo aritmetický ukazatel a někdy je snadné zaměnit jeden s druhým. V reakci na to mnoho moderních počítačových jazyků vyšší úrovně (například Java ) neumožňuje přímý přístup do paměti pomocí adres. Také bezpečný C dialekt Cyklon řeší mnoho problémů s ukazateli. Další diskusi viz programovací jazyk C.

voidUkazatel , nebo void*, je podporována v ANSI C a C ++ jako obecný ukazatel typu. Ukazatel na voidmůže ukládat adresu jakéhokoli objektu (nikoli funkce) a v C je implicitně převeden na jakýkoli jiný typ ukazatele objektu při přiřazení, ale musí být explicitně přetypován, pokud je zrušen. K&R C slouží char*k účelu „ukazatel agnostického typu“ (před ANSI C).

int x = 4;
void* p1 = &x;
int* p2 = p1;       // void* implicitly converted to int*: valid C, but not C++
int a = *p2;
int b = *(int*)p1;  // when dereferencing inline, there is no implicit conversion

C ++ neumožňuje implicitní převod void*na jiné typy ukazatelů, a to ani v přiřazeních. Toto bylo návrhové rozhodnutí, jak se vyhnout neopatrným a dokonce nechtěným přetypováním, ačkoli většina kompilátorů při setkávání s jinými obsazeními zobrazuje pouze varování, nikoli chyby.

int x = 4;
void* p1 = &x;
int* p2 = p1;                     // this fails in C++: there is no implicit conversion from void*
int* p3 = (int*)p1;               // C-style cast
int* p4 = static_cast<int*>(p1);  // C++ cast

V C ++ neexistuje void&(odkaz na void) na doplnění void*(ukazatel na void), protože odkazy se chovají jako aliasy proměnných, na které ukazují, a nikdy nemůže existovat proměnná, jejíž typ je void.

Přehled syntaxe deklarace ukazatele

Tyto deklarace ukazatelů pokrývají většinu variant deklarací ukazatelů. Samozřejmě je možné mít trojité ukazatele, ale hlavní principy za trojitým ukazatelem již existují ve dvojitém ukazateli.

char cff [5][5];    /* array of arrays of chars */
char *cfp [5];      /* array of pointers to chars */
char **cpp;         /* pointer to pointer to char ("double pointer") */
char (*cpf) [5];    /* pointer to array(s) of chars */
char *cpF();        /* function which returns a pointer to char(s) */
char (*CFp)();      /* pointer to a function which returns a char */
char (*cfpF())[5];  /* function which returns pointer to an array of chars */
char (*cpFf[5])();  /* an array of pointers to functions which return a char */

() A [] mají vyšší prioritu než *.

C#

V programovacím jazyce C# jsou ukazatele podporovány pouze za určitých podmínek: jakýkoli blok kódu včetně ukazatelů musí být označen unsafeklíčovým slovem. Takové bloky obvykle vyžadují ke spuštění vyšší oprávnění zabezpečení. Syntaxe je v podstatě stejná jako v C ++ a uvedená adresa může být buď spravovaná, nebo nespravovaná paměť. Ukazatele na spravovanou paměť (libovolný ukazatel na spravovaný objekt) však musí být deklarován pomocí fixedklíčového slova, které brání tomu, aby sběrač odpadků přesunul špičatý objekt jako součást správy paměti, když je ukazatel v rozsahu, a tím udržel adresu ukazatele platnou.

Výjimkou je použití IntPtrstruktury, která je bezpečným spravovaným ekvivalentem int*a nevyžaduje nebezpečný kód. Tento typ se často vrací při použití metod z System.Runtime.InteropServices, například:

// Get 16 bytes of memory from the process's unmanaged memory
IntPtr pointer = System.Runtime.InteropServices.Marshal.AllocHGlobal(16);

// Do something with the allocated memory

// Free the allocated memory
System.Runtime.InteropServices.Marshal.FreeHGlobal(pointer);

Rozhraní .NET obsahuje mnoho tříd a metod v oborech názvů Systema System.Runtime.InteropServices(jako je například Marshaltřída), které převádějí typy .NET (například System.String) na az mnoha nespravovaných typů a ukazatelů (například LPWSTRnebo void*) a umožňují komunikaci s nespravovaným kódem . Většina takových metod má stejné požadavky na oprávnění zabezpečení jako nespravovaný kód, protože mohou ovlivnit libovolná místa v paměti.

COBOL

Tyto COBOL programovací jazyk stojany ukazatele na proměnné. Primitivní nebo skupinové (záznamové) datové objekty deklarované v rámci LINKAGE SECTIONprogramu jsou ve své podstatě založeny na ukazateli, přičemž jedinou pamětí přidělenou v rámci programu je prostor pro adresu datové položky (obvykle jedno paměťové slovo). Ve zdrojovém kódu programu se tyto datové položky používají stejně jako všechny ostatní WORKING-STORAGEproměnné, ale k jejich obsahu se implicitně přistupuje nepřímo prostřednictvím jejich LINKAGEukazatelů.

Prostor paměti pro každý namířený datový objekt je obvykle přidělován dynamicky pomocí externích CALLpříkazů nebo prostřednictvím vestavěných rozšířených jazykových konstrukcí, jako jsou příkazy EXEC CICSnebo EXEC SQL.

Rozšířené verze COBOL také poskytují proměnné ukazatele deklarované s USAGE IS POINTERklauzulemi. Hodnoty těchto proměnných ukazatelů jsou stanoveny a upravovány pomocí příkazů SETa SET ADDRESS.

Některé rozšířené verze COBOL také poskytují PROCEDURE-POINTERproměnné, které jsou schopné ukládat adresy spustitelného kódu .

PL/I

Jazyk PL/I poskytuje plnou podporu ukazatelů na všechny datové typy (včetně ukazatelů na struktury), rekurze , multitasking , zpracování řetězců a rozsáhlé vestavěné funkce . PL/I byl ve srovnání s programovacími jazyky své doby docela skok vpřed. Ukazatele PL/I jsou netypické, a proto pro dereferencování nebo přiřazování ukazatele není nutné žádné odesílání. Syntaxe deklarace pro ukazatel je DECLARE xxx POINTER;, která deklaruje ukazatel s názvem "xxx". Ukazatele se používají s BASEDproměnnými. Proměnnou na základě lze deklarovat pomocí výchozího lokátoru ( DECLARE xxx BASED(ppp);nebo bez ( DECLARE xxx BASED;), kde xxx je proměnná na základě, což může být proměnná prvku, struktura nebo pole a ppp je výchozí ukazatel). Taková proměnná může být adresou bez explicitního odkazu na ukazatel ( xxx=1;nebo může být adresována s explicitním odkazem na výchozí lokátor (ppp) nebo na jakýkoli jiný ukazatel ( qqq->xxx=1;).

Aritmetika ukazatele není součástí standardu PL/I, ale mnoho kompilátorů umožňuje výrazy formuláře ptr = ptr±expression. IBM PL/I má také vestavěnou funkci PTRADDpro provádění aritmetiky. Aritmetika ukazatele se vždy provádí v bajtech.

Kompilátory IBM Enterprise PL/I mají novou formu zadaného ukazatele nazvanou a HANDLE.

D

D programovací jazyk je derivát C a C ++, které plně podporuje C ukazatele a C obsadit.

Eiffelova

Eiffel objektově orientovaný jazyk používá hodnoty a referenční sémantiku bez ukazatele aritmetiky. Přesto jsou k dispozici třídy ukazatelů. Nabízejí aritmetiku ukazatelů, casting, explicitní správu paměti, propojení se softwarem od jiného výrobce než Eiffel a další funkce.

Fortran

Fortran-90 představil schopnost silně zadaného ukazatele. Ukazatele Fortran obsahují více než jen jednoduchou adresu paměti. Také zapouzdřují dolní a horní hranici rozměrů pole, kroky (například pro podporu libovolných sekcí pole) a další metadata. Operátor sdružení , =>se používá k přidružit POINTERk proměnné, která má TARGETatribut. Příkaz Fortran-90 ALLOCATElze také použít k přiřazení ukazatele k bloku paměti. Následující kód lze například použít k definování a vytvoření struktury propojeného seznamu:

type real_list_t
  real :: sample_data(100)
  type (real_list_t), pointer :: next => null ()
end type

type (real_list_t), target :: my_real_list
type (real_list_t), pointer :: real_list_temp

real_list_temp => my_real_list
do
  read (1,iostat=ioerr) real_list_temp%sample_data
  if (ioerr /= 0) exit
  allocate (real_list_temp%next)
  real_list_temp => real_list_temp%next
end do

Fortran-2003 přidává podporu pro ukazatele procedur. Fortran-2003 také jako součást funkce C interoperability podporuje vnitřní funkce pro převod ukazatelů ve stylu C na ukazatele Fortran a zpět.

Jít

Go má ukazatele. Jeho syntaxe deklarace je ekvivalentní syntaxi C, ale je napsána obráceně a končí typem. Na rozdíl od C má Go sběr odpadu a nepovoluje aritmetiku ukazatele. Referenční typy, jako v C ++, neexistují. Některé vestavěné typy, jako jsou mapy a kanály, jsou v rámečku (tj. Interně jsou ukazateli na proměnlivé struktury) a jsou inicializovány pomocí makefunkce. V přístupu ke sjednocené syntaxi mezi ukazateli a neukazateli byl ->operátor arrow ( ) vynechán: operátor tečky na ukazateli odkazuje na pole nebo metodu dereferencovaného objektu. Toto však funguje pouze s 1 úrovní přesměrování.

Jáva

V Javě neexistuje žádná explicitní reprezentace ukazatelů . Místo toho jsou pomocí odkazů implementovány složitější datové struktury, jako jsou objekty a pole . Jazyk neposkytuje žádné explicitní operátory manipulace s ukazatelem. Je však stále možné, aby se kód pokusil zrušit odkaz na nulovou referenci (nulový ukazatel), což má za následek vyvolání výjimky za běhu . Prostor obsazený neexistuje odkaz paměťových objektů se získává automaticky garbage collector při běhu.

Modula-2

Ukazatele jsou implementovány velmi podobně jako v Pascalu, stejně jako VARparametry ve volání procedur. Modula-2 je ještě silněji napsaná než Pascal, s méně způsoby, jak uniknout typovému systému. Některé z variant Modula-2 (například Modula-3 ) zahrnují sběr odpadu.

Oberon

Stejně jako u Modula-2 jsou k dispozici ukazatele. Stále existuje méně způsobů, jak se vyhnout typovému systému, a tak je Oberon a jeho varianty stále bezpečnější s ohledem na ukazatele než Modula-2 nebo jeho varianty. Stejně jako u Modula-3 je sběr odpadků součástí jazykové specifikace.

Pascal

Na rozdíl od mnoha jazyků, které obsahují ukazatele, standardní ISO Pascal umožňuje ukazatelům pouze odkazovat na dynamicky vytvořené proměnné, které jsou anonymní, a neumožňuje jim odkazovat na standardní statické nebo místní proměnné. Nemá aritmetiku ukazatele. Ukazatele také musí mít přidružený typ a ukazatel na jeden typ není kompatibilní s ukazatelem na jiný typ (např. Ukazatel na znak není kompatibilní s ukazatelem na celé číslo). To pomáhá eliminovat problémy typu bezpečnostních spojená s jinými implementacemi ukazatel, zejména těch, které používají pro PL / I nebo C . Odstraňuje také některá rizika způsobená visícími ukazateli , ale schopnost dynamicky uvolnit odkazovaný prostor pomocí disposestandardního postupu (který má stejný účinek jako funkce freeknihovny nalezená v C ) znamená, že riziko visících ukazatelů nebylo zcela odstraněny.

V některých komerčních a open source implementacích kompilátoru Pascal (nebo derivátů) - jako Free Pascal , Turbo Pascal nebo Object Pascal v Embarcadero Delphi - však ukazatel může odkazovat na standardní statické nebo lokální proměnné a lze jej přetypovat z jednoho typu ukazatele na další. Kromě toho ukazatel aritmetický neomezený: přičtením nebo odečtením od ukazatel pohybuje se tímto počtem bytů v obou směrech, ale za použití Inc, nebo Decstandardních postupů se přesune ukazatel podle velikosti datového typu je deklarován bodu na. Pod názvem je také uveden netypový ukazatel Pointer, který je kompatibilní s jinými typy ukazatelů.

Perl

Perl programovací jazyk podporuje ukazatele, i když používá jen zřídka, v podobě balení a rozbalit funkce. Ty jsou určeny pouze pro jednoduché interakce s kompilovanými knihovnami OS. Ve všech ostatních případech Perl používá odkazy , které jsou zadány a neumožňují žádnou formu aritmetiky ukazatele. Používají se ke konstrukci složitých datových struktur.

Viz také

Reference

externí odkazy