Pointer (datorprogrammering)

Jag anser att uppdragsförklaringar och pekarvariabler är bland datavetenskapens "mest värdefulla skatter".

Donald Knuth , Structured Programming, med gå till Statements

Peka a pekar på minnesadressen som är associerad med variabel b . I detta diagram använder datorarkitekturen samma adressutrymme och dataprimitiv för både pekare och icke-pekare; detta behov bör inte vara fallet.

Inom datavetenskap är en pekare ett objekt i många programmeringsspråk som lagrar en minnesadress . Detta kan vara det för ett annat värde som finns i datorns minne , eller i vissa fall det för minnesmappad datorhårdvara . En pekare refererar till en plats i minnet, och att erhålla värdet som är lagrat på den platsen är känt som att referera till pekaren. Som en analogi skulle ett sidnummer i en boks register kunna betraktas som en pekare till motsvarande sida; Att hänvisa till en sådan pekare skulle göras genom att bläddra till sidan med det givna sidnumret och läsa texten som finns på den sidan. Det faktiska formatet och innehållet i en pekarvariabel är beroende av den underliggande datorarkitekturen .

Att använda pekare förbättrar avsevärt prestandan för repetitiva operationer, som att korsa itererbara datastrukturer (t.ex. strängar , uppslagstabeller , kontrolltabeller och trädstrukturer ) . Framför allt är det ofta mycket billigare i tid och rum att kopiera och referera pekare än det är att kopiera och komma åt data som pekarna pekar på.

Pekare används också för att hålla adresserna till ingångspunkter för anropade subrutiner i procedurprogrammering och för körtidslänkning till dynamiska länkbibliotek (DLL) . I objektorienterad programmering används pekare till funktioner för bindningsmetoder , ofta med virtuella metodtabeller .

, mer konkret implementering av den mer abstrakta referensdatatypen . Flera språk, särskilt lågnivåspråk , stöder någon typ av pekare, även om vissa har fler begränsningar för deras användning än andra. Medan "pekare" har använts för att referera till referenser i allmänhet, är det mer korrekt tillämpligt på datastrukturer vars gränssnitt uttryckligen tillåter att pekaren manipuleras (aritmetiskt via pekarritmetik ) som en minnesadress, i motsats till en magisk cookie eller kapacitet som tillåter inte sådant. [ citat behövs ] Eftersom pekare tillåter både skyddad och oskyddad åtkomst till minnesadresser, finns det risker förknippade med att använda dem, särskilt i det senare fallet. Primitiva pekare lagras ofta i ett format som liknar ett heltal ; dock kan ett försök att därhänvisa eller "slå upp" en sådan pekare vars värde inte är en giltig minnesadress få ett program att krascha (eller innehålla ogiltiga data). För att lindra detta potentiella problem, som en fråga om typsäkerhet , betraktas pekare som en separat typ som parametriseras av typen av data de pekar på, även om den underliggande representationen är ett heltal. Andra åtgärder kan också vidtas (såsom validering & gränskontroll ) för att verifiera att pekarvariabeln innehåller ett värde som både är en giltig minnesadress och inom det numeriska intervallet som processorn kan adressera.

Historia

1955 uppfann den sovjetiska datavetaren Kateryna Jusjtjenko programmeringsspråket Adress som möjliggjorde indirekt adressering och adresser av högsta rang – analogt med pekare. Detta språk användes flitigt på Sovjetunionens datorer. Det var dock okänt utanför Sovjetunionen och vanligtvis krediteras Harold Lawson uppfinningen av pekaren 1964. År 2000 tilldelades Lawson Computer Pioneer Award av IEEE "[f]or att uppfinna pekarvariabeln och introducera detta koncept i PL/I, vilket för första gången ger möjligheten att flexibelt behandla länkade listor i ett allmänt syfte språk på hög nivå". Hans framstående artikel om begreppen dök upp i juninumret 1967 av CACM med titeln: PL/I List Processing. Enligt Oxford English Dictionary dök ordet pekare först upp i tryck som en stackpekare i ett tekniskt memorandum från System Development Corporation .

Formell beskrivning

Inom datavetenskap är en pekare en sorts referens .

En dataprimitiv (eller bara primitiv ) är vilken datum som helst som kan läsas från eller skrivas till datorns minne med en minnesåtkomst (till exempel är både en byte och ett ord primitiver).

Ett dataaggregat (eller bara aggregat ) är en grupp av primitiver som är logiskt sammanhängande i minnet och som ses kollektivt som ett datum (till exempel kan ett aggregat vara 3 logiskt sammanhängande byte, vars värden representerar de tre koordinaterna för en punkt i rymden). När ett aggregat är helt sammansatt av samma typ av primitiv, kan aggregatet kallas en array ; på ett sätt är ett primitivt ord med flera byte en array av bytes, och vissa program använder ord på detta sätt.

I sammanhanget av dessa definitioner är en byte den minsta primitiva; varje minnesadress anger olika byte. Minnesadressen för den initiala byten för ett datum anses vara minnesadressen (eller basminnesadressen ) för hela datumet.

En minnespekare (eller bara pekare ) är en primitiv, vars värde är avsett att användas som en minnesadress; det sägs att en pekare pekar på en minnesadress . Det sägs också att en pekare pekar på ett datum [i minnet] när pekarens värde är datumets minnesadress.

Mer allmänt är en pekare en sorts referens , och det sägs att en pekare refererar till ett datum lagrat någonstans i minnet ; att erhålla det datumet är att avreferera pekaren . Funktionen som skiljer pekare från andra typer av referenser är att en pekares värde är tänkt att tolkas som en minnesadress, vilket är ett ganska lågnivåbegrepp.

Referenser fungerar som en nivå av inriktning: En pekares värde bestämmer vilken minnesadress (det vill säga vilket datum) som ska användas i en beräkning. Eftersom inriktning är en grundläggande aspekt av algoritmer, uttrycks pekare ofta som en grundläggande datatyp i programmeringsspråk ; i statiskt (eller starkt ) typade programmeringsspråk, bestämmer typen av en pekare vilken typ av datum som pekaren pekar mot.

Arkitektoniska rötter

Pekare är en mycket tunn abstraktion utöver de adresseringsmöjligheter som de flesta moderna arkitekturer tillhandahåller . I det enklaste schemat tilldelas en adress eller ett numeriskt index till varje minnesenhet i systemet, där enheten vanligtvis är antingen en byte eller ett ord – beroende på om arkitekturen är byteadresserbar eller ordadresserbar – effektivt omvandla allt minne till en mycket stor array . Systemet skulle då också tillhandahålla en operation för att hämta värdet lagrat i minnesenheten vid en given adress (vanligtvis med användning av maskinens allmänna register) .

I det vanliga fallet är en pekare tillräckligt stor för att hålla fler adresser än det finns minnesenheter i systemet. Detta introducerar möjligheten att ett program kan försöka komma åt en adress som inte motsvarar någon minnesenhet, antingen för att inte tillräckligt med minne är installerat (dvs. utanför intervallet för tillgängligt minne) eller att arkitekturen inte stöder sådana adresser. Det första fallet kan i vissa plattformar som Intel x86 -arkitekturen kallas ett segmenteringsfel (segfault). Det andra fallet är möjligt i den nuvarande implementeringen av AMD64 , där pekare är 64 bitar långa och adresser bara sträcker sig till 48 bitar. Pekare måste överensstämma med vissa regler (kanoniska adresser), så om en icke-kanonisk pekare hänvisas bort, ställer processorn upp ett allmänt skyddsfel .

Å andra sidan har vissa system fler minnesenheter än det finns adresser. I detta fall används ett mer komplext schema såsom minnessegmentering eller personsökning för att använda olika delar av minnet vid olika tidpunkter. De sista inkarnationerna av x86-arkitekturen stöder upp till 36 bitar av fysiska minnesadresser, som mappades till det 32-bitars linjära adressutrymmet genom PAE- sökningsmekanismen . Således kan endast 1/16 av det möjliga totala minnet nås åt gången. Ett annat exempel i samma datorfamilj var det 16-bitars skyddade läget för 80286- processorn, som, även om den endast stöder 16 MB fysiskt minne, kunde komma åt upp till 1 GB virtuellt minne, men kombinationen av 16-bitars adress och segment register gjorde åtkomst till mer än 64 KB i en datastruktur besvärlig.

För att tillhandahålla ett konsekvent gränssnitt tillhandahåller vissa arkitekturer minnesmappad I/O, vilket gör att vissa adresser hänvisar till minnesenheter medan andra hänvisar till enhetsregister för andra enheter i datorn. Det finns analoga begrepp som filförskjutningar, arrayindex och fjärrobjektreferenser som tjänar några av samma syften som adresser för andra typer av objekt.

Används

Pekare stöds direkt utan begränsningar i språk som PL/I , C , C++ , Pascal , FreeBASIC och implicit i de flesta assemblerspråk . De används främst för att konstruera referenser , som i sin tur är grundläggande för att konstruera nästan alla datastrukturer , samt för att överföra data mellan olika delar av ett program.

I funktionella programmeringsspråk som är mycket beroende av listor, hanteras datareferenser abstrakt genom att använda primitiva konstruktioner som nackdelar och motsvarande element car och cdr , som kan ses som specialiserade pekare till de första och andra komponenterna i en cons-cell. Detta ger upphov till en del av den idiomatiska "smaken" av funktionell programmering. Genom att strukturera data i sådana cons-lists , underlättar dessa språk rekursiva metoder för att bygga och bearbeta data – till exempel genom att rekursivt komma åt huvud- och svanselementen i listor med listor; t.ex. "att ta bilen till cdr:n på cdr". Däremot underlättar minneshantering baserad på pekaravledning i någon approximation av en array av minnesadresser att behandla variabler som luckor i vilka data kan tilldelas imperativt .

När man har att göra med arrayer involverar den kritiska uppslagsoperationen typiskt ett steg som kallas adressberäkning som involverar att konstruera en pekare till det önskade dataelementet i arrayen. I andra datastrukturer, såsom länkade listor , används pekare som referenser för att explicit binda en del av strukturen till en annan.

Pekare används för att skicka parametrar genom referens. Detta är användbart om programmeraren vill att en funktions ändringar av en parameter ska vara synliga för funktionsanroparen. Detta är också användbart för att returnera flera värden från en funktion.

Pekare kan också användas för att allokera och avallokera dynamiska variabler och arrayer i minnet. Eftersom en variabel ofta blir överflödig efter att den har tjänat sitt syfte är det ett slöseri med minne att behålla den, och därför är det bra att avallokera den (med hjälp av den ursprungliga pekarreferensen) när den inte längre behövs. Underlåtenhet att göra det kan resultera i en minnesläcka (där tillgängligt ledigt minne gradvis, eller i allvarliga fall snabbt, minskar på grund av en ackumulering av många redundanta minnesblock).

C-pekare

Den grundläggande syntaxen för att definiera en pekare är:

  int  *  ptr  ; 

Detta deklarerar ptr som identifierare för ett objekt av följande typ:

  • pekare som pekar på ett objekt av typen int

Detta anges vanligtvis mer kortfattat eftersom " ptr är en pekare till int ."

Eftersom C-språket inte specificerar en implicit initiering för objekt med automatisk lagringslängd, bör man ofta se till att adressen till vilken ptr pekar är giltig; det är därför det ibland föreslås att en pekare explicit initieras till nollpekarvärdet , som traditionellt anges i C med det standardiserade makrot NULL :

    int  *  ptr  =  NULL  ; 

Att avreferensera en nollpekare i C producerar odefinierat beteende , vilket kan vara katastrofalt. Men de flesta implementeringar [ citat behövs ] stoppar helt enkelt körningen av programmet i fråga, vanligtvis med ett segmenteringsfel .

Initiering av pekare i onödan kan dock hindra programanalys och därigenom dölja buggar.

I vilket fall som helst, när en pekare har deklarerats, är nästa logiska steg att den pekar på något:

   
   

  a int  a  =  5  ;  int  *  ptr  =  NULL  ;  ptr  =  &a  ; 

Detta tilldelar värdet på adressen till a till ptr . Till exempel, om a lagras på minnesplatsen 0x8130 kommer värdet på ptr att vara 0x8130 efter tilldelningen. För att referera till pekaren används en asterisk igen:

   *  ptr  =  8  ; 

Det betyder att ta innehållet i ptr (som är 0x8130), "lokalisera" den adressen i minnet och ställ in dess värde till 8. Om a senare öppnas igen, kommer dess nya värde att vara 8.

Detta exempel kan bli tydligare om minnet undersöks direkt. Antag att a finns på adressen 0x8130 i minnet och ptr vid 0x8134; anta också att detta är en 32-bitars maskin så att en int är 32-bitars bred. Följande är vad som skulle finnas i minnet efter att följande kodavsnitt har körts:

   
    int  a  =  5  ;  int  *  ptr  =  NULL  ; 
Adress Innehåll
0x8130 0x00000005
0x8134 0x00000000

(NULL-pekaren som visas här är 0x00000000.) Genom att tilldela adressen för a till ptr :

   a ptr  =  &a  ; 

ger följande minnesvärden:

Adress Innehåll
0x8130 0x00000005
0x8134 0x00008130

Sedan genom att avleda ptr genom att koda:

    *  ptr  =  8  ; 

datorn kommer att ta innehållet i ptr (som är 0x8130), 'lokalisera' den adressen och tilldela 8 till den platsen vilket ger följande minne:

Adress Innehåll
0x8130 0x00000008
0x8134 0x00008130

Uppenbarligen kommer åtkomst till a att ge värdet 8 eftersom den föregående instruktionen modifierade innehållet i en med hjälp av pekaren ptr .

Användning i datastrukturer

När du ställer upp datastrukturer som listor , köer och träd, är det nödvändigt att ha pekare för att hantera hur strukturen implementeras och kontrolleras. Typiska exempel på pekare är startpekare, slutpekare och stackpekare . Dessa pekare kan antingen vara absoluta (den faktiska fysiska adressen eller en virtuell adress i virtuellt minne ) eller relativa (en förskjutning från en absolut startadress ("bas") som vanligtvis använder färre bitar än en fullständig adress, men som vanligtvis kräver ytterligare en aritmetisk operation för att lösa).

Relativa adresser är en form av manuell minnessegmentering och delar många av dess fördelar och nackdelar. En två-byte offset, som innehåller ett 16-bitars heltal utan tecken, kan användas för att tillhandahålla relativ adressering för upp till 64 KiB (2 16 bytes) av en datastruktur. Detta kan enkelt utökas till 128, 256 eller 512 KiB om adressen som pekas på tvingas justeras en halvords-, ord- eller dubbelordsgräns (men kräver en extra "vänstervänster" bitvis operation —med 1, 2 eller 3 bitar—för att justera förskjutningen med en faktor på 2, 4 eller 8, innan dess tillägg till basadressen). I allmänhet är dock sådana scheman mycket problem, och för att underlätta för programmeraren är absoluta adresser (och underliggande det, ett platt adressutrymme ) att föredra.

En enbyte-offset, såsom det hexadecimala ASCII- värdet för ett tecken (t.ex. X'29') kan användas för att peka på ett alternativt heltalsvärde (eller index) i en matris (t.ex. X'01'). På detta sätt kan tecken mycket effektivt översättas från " rådata " till ett användbart sekventiellt index och sedan till en absolut adress utan en uppslagstabell .

C-matriser

I C är arrayindexering formellt definierad i termer av pekarearitmetik; det vill säga språkspecifikationen kräver att array[i] är ekvivalent med *(array + i) . Sålunda i C kan arrayer ses som pekare till på varandra följande minnesområden (utan luckor), och syntaxen för åtkomst till arrayer är identisk för den som kan användas för att avreferera pekare. Till exempel kan en array array deklareras och användas på följande sätt:

       
     
0          
      
      
         int  array  [  5  ];  /* Deklarerar 5 sammanhängande heltal */  int  *  ptr  =  array  ;  /* Arrayer kan användas som pekare */  ptr  [  ]  =  1  ;  /* Pekare kan indexeras med arraysyntax */  *  (  array  +  1  )  =  2  ;  /* Matriser kan avreferenseras med pekarsyntax */  *  (  1  +  matris  )  =  2  ;  /* Pekaraddition är kommutativ */  2  [  array  ]  =  4  ;  /* Subscript operator är kommutativ */ 

Detta tilldelar ett block med fem heltal och namnger blockmatrisen , som fungerar som en pekare till blocket. En annan vanlig användning av pekare är att peka på dynamiskt allokerat minne från malloc som returnerar ett på varandra följande minnesblock av inte mindre än den begärda storleken som kan användas som en array.

skiljer sig resultatet av operatorns storlek . I det här exemplet kommer sizeof(array) att utvärderas till 5*sizeof(int) (storleken på matrisen), medan sizeof(ptr) kommer att utvärderas till sizeof(int*) , storleken på själva pekaren.

Standardvärden för en array kan deklareras som:

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

Om array finns i minnet med start vid adress 0x1000 på en 32-bitars liten endian- maskin kommer minnet att innehålla följande (värdena är i hexadecimal , som adresserna):

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

Representerade här är fem heltal: 2, 4, 3, 1 och 5. Dessa fem heltal upptar 32 bitar (4 byte) var och en med den minst signifikanta byten lagrad först (detta är en lite endian CPU-arkitektur) och lagras i följd börjar på adress 0x1000.

Syntaxen för C med pekare är:

  • array betyder 0x1000;
  • array + 1 betyder 0x1004: "+ 1" betyder att man lägger till storleken 1 int , vilket är 4 byte;
  • *array betyder att avreferera innehållet i array . Med tanke på innehållet som en minnesadress (0x1000), slå upp värdet på den platsen (0x0002);
  • array[i] betyder elementnummer i , 0-baserat, av array som översätts till *(array + i) .

Det sista exemplet är hur man kommer åt innehållet i arrayen . Bryter ner det:

  • array + i är minnesplatsen för det (i): e elementet i array , som börjar vid i=0;
  • *(array + i) tar den minnesadressen och avreferenser den för att komma åt värdet.

C länkad lista

Nedan är en exempeldefinition av en länkad lista i C.





  
              
        
 /* den tomma länkade listan representeras av NULL  * eller något annat sentinelvärde */  #define EMPTY_LIST NULL  struct  link  {  void  *  data  ;  /* data för denna länk */  struct  link  *  nästa  ;  /* nästa länk; EMPTY_LIST om det inte finns någon */   }; 

Denna pekare-rekursiva definition är i huvudsak densamma som den referens-rekursiva definitionen från Haskell-programmeringsspråket :

     
                  datalänk  a  =  Noll  |  _  Nackdelar  a  (  Länk  a  ) 

Noll är den tomma listan, och Cons a (Länk a) är en nackdel av typen a med en annan länk också av typen a .

Definitionen med referenser är dock typkontrollerad och använder inte potentiellt förvirrande signalvärden. Av denna anledning hanteras datastrukturer i C vanligtvis via omslagsfunktioner , som noggrant kontrolleras för korrekthet.

Pass-by-adress med hjälp av pekare

Pekare kan användas för att skicka variabler genom deras adress, vilket gör att deras värde kan ändras. Tänk till exempel på följande C- kod:


   
      



   
      


  
       

    
    
    

    
    
    

     0
 /* en kopia av int n kan ändras i funktionen utan att påverka anropskoden */  void  passByValue  (  int  n  )  {  n  =  12  ;  }  /* en pekare m skickas istället. Ingen kopia av värdet som pekas på av m skapas */   void  passByAddress  (  int  *  m  )  {  *  m  =  14  ;  }  int  main  (  void  )  {  int  x  =  3  ;  /* skicka en kopia av xs värde som argumentet */  passByValue  (  x  );  // värdet ändrades inuti funktionen, men x är fortfarande 3 från och med nu  /* skicka x:s adress som argumentet */  passByAddress  (  &  x  );  // x ändrades faktiskt av funktionen och är nu lika med 14 här  return  ;  } 

Dynamisk minnesallokering

I vissa program beror den nödvändiga mängden minne på vad användaren kan ange. I sådana fall behöver programmeraren allokera minne dynamiskt. Detta görs genom att allokera minne vid högen snarare än på stacken , där variabler vanligtvis lagras (variabler kan också lagras i CPU-registren, men det är en annan sak). Dynamisk minnesallokering kan endast göras genom pekare, och namn (som med vanliga variabler) kan inte ges.

Pekare används för att lagra och hantera adresserna till dynamiskt allokerade minnesblock. Sådana block används för att lagra dataobjekt eller arrayer av objekt. De flesta strukturerade och objektorienterade språk tillhandahåller ett minnesområde, kallat heap eller free store , från vilket objekt tilldelas dynamiskt.

Exempel C-koden nedan illustrerar hur strukturobjekt dynamiskt allokeras och refereras. Standard C-biblioteket tillhandahåller funktionen malloc() för att allokera minnesblock från högen. Det tar storleken på ett objekt att allokera som en parameter och returnerar en pekare till ett nyligen allokerat minnesblock som är lämpligt för att lagra objektet, eller så returnerar det en nollpekare om allokeringen misslyckades.


  
                  
              
              



      
       

    
       
       
         

    
     0  
        
      
      

    
        
        
        
         
    
     

    
     
 /* Parts inventory item */  struct  Item  {  int  id  ;  /* Artikelnummer */  char  *  namn  ;  /* Delnamn */  flytande  kostnad  ;  /* Kostnad */  };  /* Tilldela och initiera ett nytt objektobjekt */  struct  Item  *  make_item  (  const  char  *  name  )  {  struct  Item  *  item  ;  /* Tilldela ett minnesblock för ett nytt Item-objekt */  item  =  malloc  (  sizeof  (  struct  Item  ));  if  (  objekt  ==  NULL  )  returnera  NULL  ;  /* Initiera medlemmarna i det nya objektet */  memset  (  item  ,  ,  sizeof  (  struct  Item  ));  objekt  ->  id  =  -1  ;  objekt  ->  namn  =  NULL  ;  artikel  ->  kostnad  =  0,0  ;  /* Spara en kopia av namnet i det nya objektet */  objekt  ->  namn  =  malloc  (  strlen  (  namn  )  +  1  );  if  (  objekt  ->  namn  ==  NULL  )  {  gratis  (  objekt  );  returnera  NULL  ;  }  strcpy  (  objekt  ->  namn  ,  namn  );  /* Returnera det nyskapade objektet */  returnera  objektet  ;  } 

Koden nedan illustrerar hur minnesobjekt dynamiskt deallokeras, dvs. returneras till högen eller det fria lagret. Standard C-biblioteket tillhandahåller funktionen free() för att avallokera ett tidigare allokerat minnesblock och returnera det tillbaka till högen.


    
    
       
        

    
        
        
          
    

    
    
 /* Avallokera ett objektobjekt */  void  destroy_item  (  struct  Item  *  item  )  {  /* Kontrollera om det finns en nollobjektpekare */  if  (  item  ==  NULL  )  return  ;  /* Avallokera namnsträngen som sparats i objektet */  if  (  objekt  ->  namn  !=  NULL  )  {  gratis  (  objekt  ->  namn  );  objekt  ->  namn  =  NULL  ;  }  /* Avallokera själva objektet */  free  (  item  );  } 

Minneskartad hårdvara

På vissa datorarkitekturer kan pekare användas för att direkt manipulera minne eller minneskartade enheter.

Att tilldela adresser till pekare är ett ovärderligt verktyg vid programmering av mikrokontroller . Nedan är ett enkelt exempel som förklarar en pekare av typen int och initierar den till en hexadecimal adress i det här exemplet konstanten 0x7FFF:

     int  *  hårdvaruadress  =  (  int  *  )  0x7FFF  ; 

I mitten av 80-talet gick det långsamt att använda BIOS för att komma åt datorernas videofunktioner. Applikationer som var visningsintensiva användes vanligtvis för att komma åt CGA- videominne direkt genom att casta den hexadecimala konstanten 0xB8000 till en pekare till en matris med 80 osignerade 16-bitars int-värden. Varje värde bestod av en ASCII- kod i den låga byten och en färg i den höga byten. Således, för att sätta bokstaven 'A' på rad 5, kolumn 2 i ljust vitt på blått, skulle man skriva kod som följande:



  
        
 #define VID ((osignerad kort (*)[80])0xB8000)  void  foo  (  void  )  {  VID  [  4  ][  1  ]  =  0x1F00  |  'A'  ;  } 

Använd i kontrolltabeller

Kontrolltabeller som används för att styra programflödet använder vanligtvis pekare i stor utsträckning. Pekarna, vanligtvis inbäddade i en tabellpost, kan till exempel användas för att hålla ingångspunkterna till subrutiner som ska exekveras, baserat på vissa villkor definierade i samma tabellpost. Pekarna kan emellertid helt enkelt vara index till andra separata, men associerade, tabeller som innefattar en uppsättning av de faktiska adresserna eller adresserna själva (beroende på tillgängliga programmeringsspråkskonstruktioner). De kan också användas för att peka på tidigare tabellposter (som i loopbearbetning) eller vidarebefordra för att hoppa över några tabellposter (som i en switch eller "tidig" utgång från en loop). För detta senare ändamål kan "pekaren" helt enkelt vara själva tabellpostnumret och kan omvandlas till en faktisk adress genom enkel aritmetik.

Maskinskrivna pekpinnar och gjutning

På många språk har pekare den ytterligare begränsningen att objektet de pekar på har en specifik typ . Till exempel kan en pekare förklaras peka på ett heltal ; Språket kommer då att försöka hindra programmeraren från att peka det mot objekt som inte är heltal, såsom flyttal, vilket eliminerar vissa fel.

Till exempel i C

 
  int  *  pengar  ;  röding  *  påsar  ; 

pengar skulle vara en heltalspekare och påsar skulle vara en teckenpekare. Följande skulle ge en kompilatorvarning om "tilldelning från inkompatibel pekartyp" under GCC

   väskor  =  pengar  ; 

eftersom pengar och väskor deklarerades med olika typer. För att undertrycka kompilatorvarningen måste det göras tydligt att du verkligen vill göra tilldelningen genom att typcasta den

    påsar  =  (  char  *  )  pengar  ; 

som säger att man ska kasta heltalspekaren för pengar till en teckenpekare och tilldela till påsar .

Ett 2005 års utkast till C-standarden kräver att gjutning av en pekare härledd från en typ till en av en annan typ ska bibehålla inriktningens korrekthet för båda typerna (6.3.2.3 Pekare, par. 7):

   
 

     
                                          char  *  external_buffer  =  "abcdef"  ;  int  *  internal_data  ;  intern_data  =  (  int  *  )  extern_buffert  ;  // Odefinierat beteende om "den resulterande pekaren  // inte är korrekt justerad" 

På språk som tillåter pekararitmetik tar aritmetik på pekare hänsyn till typens storlek. Om till exempel att lägga till ett heltal till en pekare produceras en annan pekare som pekar på en adress som är högre med det talet gånger storleken på typen. Detta gör att vi enkelt kan beräkna adressen för element i en array av en given typ, som visades i exemplet med C-arrayer ovan. När en pekare av en typ gjuts till en annan typ av en annan storlek, bör programmeraren förvänta sig att pekarens aritmetik kommer att beräknas annorlunda. I C, till exempel, om pengarmatrisen börjar på 0x2000 och sizeof(int) är 4 byte medan sizeof(char) är 1 byte, så kommer pengar + 1 att peka på 0x2004, men bags + 1 skulle peka på 0x2001. Andra risker med casting inkluderar förlust av data när "bred" data skrivs till "smala" platser (t.ex. bags[0] = 65537; ), oväntade resultat vid bitförskjutning av värden och jämförelseproblem, särskilt med signerade vs osignerade värden.

Även om det i allmänhet är omöjligt att avgöra vid kompilering vilka casts som är säkra, lagrar vissa språk runtime- information som kan användas för att bekräfta att dessa farliga casts är giltiga under runtime. Andra språk accepterar bara en konservativ uppskattning av säkra avgjutningar, eller inga alls.

Värdet av pekare

I C och C++, även om två pekare jämförs som lika betyder det inte att de är likvärdiga. I dessa språk och LLVM tolkas regeln som att "bara för att två pekare pekar på samma adress betyder det inte att de är lika i den meningen att de kan användas omväxlande", skillnaden mellan pekarna som kallas deras härkomst . Casting till en heltalstyp som uintptr_t är implementeringsdefinierad och jämförelsen den ger ger inte någon mer insikt om huruvida de två pekarna är utbytbara. Dessutom kommer ytterligare konvertering till byte och aritmetik att kasta av sig optimerare som försöker hålla koll på användningen av pekare, ett problem som fortfarande belyses i akademisk forskning.

Gör pekare säkrare

Eftersom en pekare tillåter ett program att försöka komma åt ett objekt som kanske inte är definierat, kan pekare vara ursprunget till en mängd olika programmeringsfel . Dock är användbarheten av pekare så stor att det kan vara svårt att utföra programmeringsuppgifter utan dem. Följaktligen har många språk skapat konstruktioner som utformats för att tillhandahålla några av de användbara funktionerna i pekare utan några av deras fallgropar , även ibland kallade pekarfaror . I detta råpekare sammanhang kallas pekare som direkt adresserar minne (som används i den här artikeln) som , i motsats till smarta pekare eller andra varianter.

Ett stort problem med pekare är att så länge de direkt kan manipuleras som ett nummer, kan de fås att peka på oanvända adresser eller till data som används för andra ändamål. Många språk, inklusive de flesta funktionella programmeringsspråk och nya imperativa språk som Java , ersätter pekare med en mer ogenomskinlig typ av referens, vanligtvis hänvisad till som enbart en referens , som bara kan användas för att referera till objekt och inte manipuleras som siffror, vilket förhindrar detta typ av fel. Arrayindexering hanteras som ett specialfall.

En pekare som inte har någon adress tilldelad kallas en vild pekare . Alla försök att använda sådana oinitierade pekare kan orsaka oväntat beteende, antingen för att det initiala värdet inte är en giltig adress eller för att användningen av det kan skada andra delar av programmet. Resultatet är ofta ett segmenteringsfel , lagringsbrott eller vild gren (om den används som funktionspekare eller grenadress).

I system med explicit minnesallokering är det möjligt att skapa en hängande pekare genom att deallokera minnesregionen den pekar in i. Den här typen av pekare är farlig och subtil eftersom en avallokerad minnesregion kan innehålla samma data som den gjorde innan den avallokerades men kan sedan omfördelas och skrivas över av orelaterade kod, okänd för den tidigare koden. Språk med sophämtning förhindrar denna typ av fel eftersom avallokering utförs automatiskt när det inte finns fler referenser i omfattningen.

Vissa språk, som C++ , stöder smarta pekare , som använder en enkel form av referensräkning för att hjälpa till att spåra tilldelning av dynamiskt minne förutom att fungera som en referens. I avsaknad av referenscykler, där ett objekt hänvisar till sig själv indirekt genom en sekvens av smarta pekare, eliminerar dessa möjligheten för hängande pekare och minnesläckor. Delphi -strängar stöder referensräkning inbyggt.

Programmeringsspråket Rust introducerar en lånekontroll , pekares livstider och en optimering baserad på valfria typer för nollpekare för att eliminera pekarbuggar, utan att tillgripa sophämtning .

Speciella typer av pekpinnar

Typer definierade av värde

Nollpekare

En nollpekare har ett värde reserverat för att indikera att pekaren inte refererar till ett giltigt objekt. Nollpekare används rutinmässigt för att representera tillstånd som slutet på en lista med okänd längd eller misslyckandet med att utföra någon åtgärd; denna användning av nollpekare kan jämföras med nollbara typer och med värdet Ingenting i en optiontyp .

Dinglande pekare

En dinglande pekare är en pekare som inte pekar på ett giltigt objekt och som följaktligen kan få ett program att krascha eller bete sig konstigt. I programmeringsspråken Pascal eller C kan pekare som inte är specifikt initierade peka på oförutsägbara adresser i minnet.

Följande exempelkod visar en dinglande pekare:

  
        
            
            
            
 int  func  (  void  )  {  char  *  p1  =  malloc  (  sizeof  (  char  ));  /* (odefinierat) värde för någon plats på högen */  char  *  p2  ;  /* dinglande (oinitierad) pekare */  *  p1  =  'a'  ;  /* Detta är OK, förutsatt att malloc() inte har returnerat NULL. */   *  p2  =  'b'  ;  /* Detta anropar odefinierat beteende */  } 

Här kan p2 peka på var som helst i minnet, så utför tilldelningen *p2 = 'b'; kan förstöra ett okänt minnesområde eller utlösa ett segmenteringsfel .

Vild gren

Om en pekare används som adress för ingångspunkten till ett program eller start av en funktion som inte returnerar någonting och som också är antingen oinitierad eller skadad, om ett anrop eller hopp ändå görs till denna adress, visas en " vild gren" " sägs ha inträffat. Med andra ord är en vild gren en funktionspekare som är vild (dinglande).

Konsekvenserna är vanligtvis oförutsägbara och felet kan uppträda på flera olika sätt beroende på om pekaren är en "giltig" adress eller inte och om det (av en tillfällighet) finns en giltig instruktion (op-kod) på den adressen. Detekteringen av en vild gren kan utgöra en av de svåraste och mest frustrerande felsökningsövningarna eftersom mycket av bevisen redan kan ha förstörts i förväg eller genom att en eller flera olämpliga instruktioner har utförts på grenplatsen. Om det finns tillgängligt kan en simulator för instruktionsuppsättningar vanligtvis inte bara upptäcka en vild gren innan den träder i kraft, utan också ge ett helt eller delvis spår av dess historia.

Typer definierade av struktur

Autorelativ pekare

En autorelativ pekare är en pekare vars värde tolkas som en offset från adressen till själva pekaren; sålunda, om en datastruktur har en autorelativ pekare som pekar på någon del av själva datastrukturen, kan datastrukturen flyttas till minnet utan att behöva uppdatera värdet på den autorelativa pekaren.

Det citerade patentet använder också termen självrelativ pekare för att betyda samma sak. Men innebörden av den termen har använts på andra sätt:

  • att betyda en förskjutning från adressen till en struktur snarare än från adressen till själva pekaren; [ citat behövs ]
  • att betyda en pekare som innehåller sin egen adress, vilket kan vara användbart för att i vilken godtycklig minnesregion som helst rekonstruera en samling datastrukturer som pekar mot varandra.

Baserad pekare

En baserad pekare är en pekare vars värde är en offset från värdet på en annan pekare. Detta kan användas för att lagra och ladda datablock, tilldela adressen till början av blocket till baspekaren.

Typer definierade av användning eller datatyp

Flera inriktningar

På vissa språk kan en pekare referera till en annan pekare, vilket kräver flera dereferensoperationer för att komma till det ursprungliga värdet. Även om varje nivå av inriktning kan lägga till en prestationskostnad, är det ibland nödvändigt för att tillhandahålla korrekt beteende för komplexa datastrukturer . Till exempel, i C är det typiskt att definiera en länkad lista i termer av ett element som innehåller en pekare till nästa element i listan:

  
      
                


     struct  element  {  struct  element  *  nästa  ;  int  värde  ;  };  struct  element  *  head  =  NULL  ; 

Denna implementering använder en pekare till det första elementet i listan som ett surrogat för hela listan. Om ett nytt värde läggs till i början av listan, måste huvudet ändras för att peka på det nya elementet. Eftersom C-argument alltid skickas av värde, tillåter användning av dubbel inriktning att infogningen implementeras korrekt och har den önskvärda bieffekten att eliminera specialfallskod för att hantera infogningar längst fram i listan:



       
        
              
           
            
    
      
      



  // Med en sorterad lista vid *head, infoga elementet på den första  //-platsen där alla tidigare element har mindre eller lika värde.  void  insert  (  struct  element  **  head  ,  struct  element  *  item  )  {  struct  element  **  p  ;  // p pekar på en pekare till ett element  för  (  p  =  head  ;  *  p  !=  NULL  ;  p  =  &  (  *  p  )  ->  next  )  {  if  (  item  ->  value  <=  (  *  p  )  ->  value  )  bryta  ;  }  objekt  ->  nästa  =  *  p  ;  *  p  =  objekt  ;  }  // Uppringaren gör detta:  infoga  (  &  head  ,  item  ); 

objektets värde är mindre än head , uppdateras uppringarens huvud korrekt till adressen för det nya objektet.

Ett grundläggande exempel är i argv- argumentet till huvudfunktionen i C (och C++), som ges i prototypen som char **argv — detta beror på att variabeln argv i sig är en pekare till en array av strängar (en array av arrays), så *argv är en pekare till den 0:e strängen (enligt programmets namn), och **argv är det 0:e tecknet i den 0:e strängen.

Funktionspekare

På vissa språk kan en pekare referera till körbar kod, dvs den kan peka på en funktion, metod eller procedur. En funktionspekare kommer att lagra adressen till en funktion som ska anropas. Även om den här funktionen kan användas för att anropa funktioner dynamiskt, är den ofta en favoritteknik för författare av virus och andra skadliga program.

        
       


  
        
          
                    
               
                 
 int  sum  (  int  n1  ,  int  n2  )  {  // Funktion med två heltalsparametrar som returnerar ett heltalsvärde  returnerar  n1  +  n2  ;  }  int  main  (  void  )  {  int  a  ,  b  ,  x  ,  y  ;  int  (  *  fp  )(  int  ,  int  );  // Funktionspekare som kan peka på en funktion som sum  fp  =  &  sum  ;  // fp pekar nu på funktionssumma  x  =  (  *  fp  )(  a  ,  b  );  // Anropar funktionen summa med argumenten a och b  y  =  summa  (  a  ,  b  );  // Anropar funktionen summa med argumenten a och b  } 

Bakåtpekare

I dubbellänkade listor eller trädstrukturer pekar en bakåtpekare på ett element "bakåt" till objektet som hänvisar till det aktuella elementet. Dessa är användbara för navigering och manipulation, på bekostnad av större minnesanvändning.

Simulering med hjälp av ett arrayindex

Det är möjligt att simulera pekarens beteende med hjälp av ett index till en (normalt endimensionell) array.

Främst för språk som inte stöder pekare explicit men som stöder arrayer, kan arrayen tänkas och bearbetas som om den vore hela minnesområdet (inom ramen för den specifika arrayen) och vilket index som helst till det kan ses som likvärdigt till ett allmänt register i assemblerspråk (som pekar på de enskilda byten men vars faktiska värde är relativt början av arrayen, inte dess absoluta adress i minnet). Om vi ​​antar att arrayen till exempel är en sammanhängande 16 megabyte teckendatastruktur , kan individuella byte (eller en sträng av angränsande byte inom arrayen) direkt adresseras och manipuleras med namnet på arrayen med ett 31 bitars osignerat heltal som den simulerade pekaren (detta är ganska likt C-matrisexemplet som visas ovan). Pekaritmetik kan simuleras genom att addera eller subtrahera från indexet, med minimal extra omkostnad jämfört med äkta pekaritmetik.

Det är till och med teoretiskt möjligt, med hjälp av ovanstående teknik, tillsammans med en lämplig simulator för instruktionsuppsättningar för att simulera vilken maskinkod som helst eller mellanliggande ( bytekod ) för en processor/språk på ett annat språk som inte stöder pekare alls (till exempel Java / JavaScript ). För att uppnå detta kan den binära koden initialt laddas in i angränsande bytes i arrayen för simulatorn att "läsa", tolka och agera helt och hållet i minnet som finns i samma array. Om det behövs, för att helt undvika problem med buffertspill , kan gränskontroll vanligtvis utföras för kompilatorn (eller om inte, handkodas i simulatorn).

Support i olika programmeringsspråk

Ada

Ada är ett starkt maskinskrivet språk där alla pekare är skrivna och endast säkra typkonverteringar är tillåtna. Alla pekare initieras som standard till null , och varje försök att komma åt data via en nollpekare gör att ett undantag skapas. Pekare i Ada kallas åtkomsttyper . Ada 83 tillät inte aritmetik på åtkomsttyper (även om många kompilatorleverantörer tillhandahållit det som en icke-standardfunktion), men Ada 95 stöder "säker" aritmetik på åtkomsttyper via paketet System.Storage_Elements .

GRUNDLÄGGANDE

Flera gamla versioner av BASIC för Windows-plattformen hade stöd för STRPTR() för att returnera adressen till en sträng och för VARPTR() för att returnera adressen till en variabel. Visual Basic 5 hade också stöd för OBJPTR() för att returnera adressen till ett objektgränssnitt, och för en ADDRESSOF-operator för att returnera adressen till en funktion. Typerna av alla dessa är heltal, men deras värden är likvärdiga med de som innehas av pekartyper.

Nyare dialekter av BASIC , som FreeBASIC eller BlitzMax , har dock uttömmande pekarimplementationer. I FreeBASIC behandlas aritmetik på ALLA pekare (motsvarande C:s tomrum* ) som om ANY -pekaren var en bytebredd. NÅGON pekare kan inte avläsas, som i C. Att casta mellan NÅGON och någon annan typs pekare kommer inte heller att generera några varningar.

     
      
      
  
         dim  som  heltal  f  =  257  dim  som  valfri  ptr  g  =  @  f  dim  som  heltal  ptr  i  =  g  hävda  (  *  i  =  257  )  hävda  (  (  g  +  4  )  =  (  @  f  +  1  )  ) 

C och C++

I C och C++ är pekare variabler som lagrar adresser och kan vara null . Varje pekare har en typ den pekar på, men man kan fritt casta mellan pekartyper (men inte mellan en funktionspekare och en objektpekare). En speciell pekartyp som kallas "void pointer" tillåter att peka på vilket som helst (icke-funktions-) objekt, men begränsas av det faktum att det inte kan avläsas direkt (det ska gjutas). Adressen i sig kan ofta manipuleras direkt genom att kasta en pekare till och från en integraltyp av tillräcklig storlek, även om resultaten är implementeringsdefinierade och verkligen kan orsaka odefinierat beteende; medan tidigare C-standarder inte hade en integraltyp som garanterat var tillräckligt stor, specificerar C99 typedef namnet uintptr_t definierat i <stdint.h> , men en implementering behöver inte tillhandahålla det.

C++ stöder fullt ut C-pekare och C-typcasting. Det stöder också en ny grupp typcasting-operatörer för att hjälpa till att fånga några oavsiktliga farliga casts vid kompilering. Sedan C++11 tillhandahåller standardbiblioteket i C++ även smarta pekare ( unique_ptr , shared_ptr och weak_ptr ) som kan användas i vissa situationer som ett säkrare alternativ till primitiva C-pekare. C++ stöder också en annan form av referens, helt annorlunda än en pekare, som helt enkelt kallas en referens eller referenstyp .

Pekararitmetik , det vill säga möjligheten att modifiera en pekares måladress med aritmetiska operationer (såväl som magnitudjämförelser), begränsas av språkstandarden till att förbli inom gränserna för ett enstaka arrayobjekt (eller strax efter det) och kommer att annars åberopa odefinierat beteende . Att lägga till eller subtrahera från en pekare flyttar den med en multipel av storleken på dess datatyp . Om till exempel att lägga till 1 till en pekare till 4-byte heltalsvärden ökar pekarens pekade-till-byte-adress med 4. Detta har effekten att pekaren ökar så att den pekar på nästa element i en sammanhängande array av heltal – vilket är ofta det avsedda resultatet. Pekarritmetik kan inte utföras på void- pekare eftersom void-typen inte har någon storlek, och därför kan den spetsade adressen inte läggas till, även om gcc och andra kompilatorer kommer att utföra byte-aritmetik på void* som ett icke-standardiserat tillägg, och behandla det som om det var röding* .

Pekarritmetik ger programmeraren ett enda sätt att hantera olika typer: att lägga till och subtrahera antalet element som krävs istället för den faktiska offseten i byte. (Pekaritmetik med char * -pekare använder byteförskjutningar, eftersom sizeof(char) är 1 per definition.) I synnerhet deklarerar C-definitionen uttryckligen att syntaxen a[n] , som är det n -te elementet i arrayen a , är ekvivalent med *(a + n) , vilket är innehållet i elementet pekat med a + n . Detta innebär att n[a] är ekvivalent med a[n] , och man kan skriva t.ex. a[3] eller 3[a] lika bra för att komma åt det fjärde elementet i en array a .

Även om det är kraftfullt, kan pekarritmetik vara en källa till datorfel . Det tenderar att förvirra nybörjare programmerare och tvinga dem in i olika sammanhang: ett uttryck kan vara ett vanligt aritmetiskt eller ett pekarritmetiskt, och ibland är det lätt att missta det ena för det andra. Som svar på detta tillåter många moderna datorspråk på hög nivå (till exempel Java ) inte direkt åtkomst till minnet med hjälp av adresser. Dessutom tar den säkra C-dialekten Cyclone upp många av problemen med pekare. Se C programmeringsspråk för mer diskussion.

Void - pekaren , eller void* , stöds i ANSI C och C++ som en generisk pekartyp. En pekare till ogiltig kan lagra adressen till vilket objekt som helst (inte funktion), och, i C, konverteras den implicit till vilken annan objektpekare som helst vid tilldelning, men den måste explicit castas om den hänvisas till. K&R C använde char* för "typagnostisk pekare" (före ANSI C).

   
   
          
   
      int  x  =  4  ;  void  *  p1  =  &  x  ;  int  *  p2  =  pl  ;  // void* implicit konverterad till int*: giltigt C, men inte C++  int  a  =  *  p2  ;  int  b  =  *  (  int  *  )  pl  ;  // när man refererar inline, finns det ingen implicit konvertering 

C++ tillåter inte implicit omvandling av void* till andra pekartyper, inte ens i tilldelningar. Detta var ett designbeslut för att undvika slarviga och till och med oavsiktliga casts, även om de flesta kompilatorer bara matar ut varningar, inte fel, när de stöter på andra casts.

   
   
                        
                  
      int  x  =  4  ;  void  *  p1  =  &  x  ;  int  *  p2  =  pl  ;  // detta misslyckas i C++: det finns ingen implicit konvertering från void*  int  *  p3  =  (  int  *  )  p1  ;  // C-style cast  int  *  p4  =  reinterpret_cast  <  int  *>  (  p1  );  // C++ cast 

I C++ finns det ingen void& (hänvisning till void) för att komplettera void* (pekare till void), eftersom referenser beter sig som alias till variablerna de pekar på, och det kan aldrig finnas en variabel vars typ är void .

Pekare till medlem

I C++ kan pekare till icke-statiska medlemmar av en klass definieras. Om en klass C har en medlem T a är &C::a en pekare till medlemmen a av typen TC::* . Denna medlem kan vara ett objekt eller en funktion . De kan användas på höger sida av operatörerna . * och ->* för att komma åt motsvarande medlem.

  
 
    

 
   

     
     

  
     
  
      struct  S  {  int  a  ;  int  f  ()  const  {  return  a  ;}  };  S  s1  {};  S  *  ptrS  =  &  s1  ;  int  S  ::*  ptr  =  &  S  ::  a  ;  // pekare till S::a  int  (  S  ::*  fp  )()  const  =  &  S  ::  f  ;  // pekare till S::f  s1  .  *  ptr  =  1  ;  std  ::  cout  <<  (  s1  .  *  fp  )()  <<  "  \n  "  ;  // skriver ut 1  ptrS  ->*  ptr  =  2  ;  std  ::  cout  <<  (  ptrS  ->*  fp  )()  <<  "  \n  "  ;  // skriver ut 2 

Syntax för pekaredeklaration

Dessa pekardeklarationer täcker de flesta varianter av pekardeklarationer. Visst går det att ha trippelpekare, men huvudprinciperna bakom en trippelpekare finns redan i en dubbelpekare. Namnet som används här är vad uttrycket typeid(type).name() är lika med för var och en av dessa typer när man använder g++ eller clang .

     
         
             
       
          
        
  
   char  A5_A5_c  [  5  ][  5  ];  /* array av arrays av chars */  char  *  A5_Pc  [  5  ];  /* array av pekare till tecken */  char  **  PPc  ;  /* pekare till pekare till char ("dubbelpekare") */  char  (  *  PA5_c  )  [  5  ];  /* pekare till matris(er) av tecken */  char  *  FPcvE  ();  /* funktion som returnerar en pekare till char(s) */  char  (  *  PFcvE  )();  /* pekare till en funktion som returnerar ett char */  char  (  *  FPA5_cvE  ())[  5  ];  /* funktion som returnerar pekaren till en array av tecken */  char  (  *  A5_PFcvE  [  5  ])();  /* en uppsättning pekare till funktioner som returnerar ett tecken */ 

Följande deklarationer som involverar pekare-till-medlem är endast giltiga i C++:

 
 
                
          
              
              
           
           
         
         
   
          
    klass  C  ;  klass  D  ;  char  C  ::*  M1Cc  ;  /* pekare-till-medlem till char */  char  C  ::*  A5_M1Cc  [  5  ];  /* array av pekare-till-medlem till char */  char  *  C  ::*  M1CPc  ;  /* pekare-till-medlem till pekare till char(er) */  char  C  ::**  PM1Cc  ;  /* pekare till pekare-till-medlem till char */  char  (  *  M1CA5_c  )  [  5  ];  /* pekare-till-medlem till matris(er) av tecken */  char  C  ::*  FM1CcvE  ();  /* funktion som returnerar en pekare-till-medlem till char */  char  D  ::*  C  ::*  M1CM1Dc  ;  /* pekare-till-medlem till pekare-till-medlem till pekare till char(s) */  char  C  ::*  C  ::*  M1CMS_c  ;  /* pekare-till-medlem till pekare-till-medlem till pekare till char(er) */  char  (  C  ::*  FM1CA5_cvE  ())[  5  ];  /*-funktion som returnerar pekare-till-medlem till en array av tecken */ char  (  C  ::  *  M1CFcvE  )()  /* pekare-till-medlem-funktion som returnerar ett char */  char  (  C  ::*  A5_M1CFcvE  [  5  ])();  /* en array av pekare-till-medlemsfunktioner som returnerar ett tecken */ 

( ) och [] har högre prioritet än * .

C#

I programmeringsspråket C# stöds pekare endast under vissa förhållanden: alla kodblock inklusive pekare måste markeras med det osäkra nyckelordet. Sådana block kräver vanligtvis högre säkerhetsbehörigheter för att få köras. Syntaxen är i huvudsak densamma som i C++, och adressen som pekas kan vara antingen hanterat eller ohanterat minne. Dock måste pekare till hanterat minne (valfri pekare till ett hanterat objekt) deklareras med det fasta nyckelordet, vilket förhindrar sopsamlaren från att flytta det spetsiga objektet som en del av minneshanteringen medan pekaren är inom räckvidden, vilket håller pekarens adress giltig .

Ett undantag från detta är att använda IntPtr- strukturen, som är en säker hanterad motsvarande int* , och som inte kräver osäker kod. Denna typ returneras ofta när man använder metoder från System.Runtime.InteropServices , till exempel:


   




 // Få 16 byte minne från processens ohanterade minne  IntPtr-  pekare  =  System  .  Körtid  .  InteropServices  .  Marskalk  .  AllocHGlobal  (  16  );  // Gör något med det tilldelade minnet  // Frigör det tilldelade minnet  System  .  Körtid  .  InteropServices  .  Marskalk  .  FreeHGlobal  (  pekare  ); 

.NET- ramverket innehåller många klasser och metoder i namnområdena System och System.Runtime.InteropServices (som Marshal -klassen) som konverterar .NET-typer (till exempel System.String ) till och från många ohanterade typer och pekare (till exempel , LPWSTR eller void* ) för att tillåta kommunikation med ohanterad kod . De flesta sådana metoder har samma säkerhetstillståndskrav som ohanterad kod, eftersom de kan påverka godtyckliga platser i minnet.

COBOL

Programmeringsspråket COBOL stöder pekare till variabler. Primitiva eller gruppdataobjekt som deklareras inom LINKAGE SECTION av ett program är till sin natur pekarbaserade, där det enda minne som allokeras inom programmet är utrymme för adressen till dataobjektet (vanligtvis ett enda minnesord). I programkällkoden används dessa dataobjekt precis som alla andra WORKING-STORAGE- variabler, men deras innehåll nås implicit indirekt via deras LINKAGE -pekare.

Minnesutrymme för varje pekat dataobjekt allokeras vanligtvis dynamiskt med hjälp av externa CALL- satser eller via inbäddade utökade språkkonstruktioner som EXEC CICS eller EXEC SQL- satser.

Utökade versioner av COBOL tillhandahåller också pekarvariabler som deklareras med USAGE IS POINTER -satser. Värdena för sådana pekvariabler upprättas och modifieras med hjälp av SET- och SET ADDRESS -satser.

Vissa utökade versioner av COBOL tillhandahåller även PROCEDURE-POINTER -variabler, som kan lagra adresserna till körbar kod .

PL/I

PL /I -språket ger fullt stöd för pekare till alla datatyper (inklusive pekare till strukturer), rekursion , multitasking , stränghantering och omfattande inbyggda funktioner . PL/I var ett stort steg framåt jämfört med sin tids programmeringsspråk. [ citat behövs ] PL/I-pekare är otypade, och därför krävs ingen casting för pekaravledning eller tilldelning. Deklarationssyntaxen för en pekare är DECLARE xxx POINTER; , som deklarerar en pekare med namnet "xxx". Pekare används med BASERADE variabler. En baserad variabel kan deklareras med en standardlocator ( DECLARE xxx BASED(ppp); eller utan ( DECLARE xxx BASED; ), där xxx är en baserad variabel, som kan vara en elementvariabel, en struktur eller en array, och ppp är standardpekaren). En sådan variabel kan vara adress utan en explicit pekarreferens ( xxx=1; , eller kan adresseras med en explicit referens till standardlocatorn (ppp), eller till någon annan pekare ( qqq->xxx=1; ).

Pekaritmetik är inte en del av PL/I-standarden, men många kompilatorer tillåter uttryck av formen ptr = ptr±expression . IBM PL/I har även den inbyggda funktionen PTRADD för att utföra aritmetiken. Pekararitmetik utförs alltid i byte.

IBM Enterprise PL/I-kompilatorer har en ny form av maskinskriven pekare som kallas HANDLE .

D

Programmeringsspråket D är en derivata av C och C++ som fullt ut stöder C-pekare och C-typcasting.

Eiffel

Det objektorienterade Eiffelspråket använder värde- och referenssemantik utan pekaritmetik. Ändå tillhandahålls pekarklasser. De erbjuder pekaritmetik, typcasting, explicit minneshantering, gränssnitt med icke-Eiffel-programvara och andra funktioner.

Fortran

Fortran-90 introducerade en starkt typad pekarfunktion. Fortran-pekare innehåller mer än bara en enkel minnesadress. De kapslar också in de nedre och övre gränserna för arraydimensioner, steg (till exempel för att stödja godtyckliga arraysektioner) och annan metadata. En associationsoperator , => används för att associera en POINTER till en variabel som har ett TARGET -attribut. Fortran-90 ALLOCATE -satsen kan också användas för att associera en pekare till ett minnesblock. Till exempel kan följande kod användas för att definiera och skapa en länkad liststruktur:


    
         


    
    

  

  
     0 
 
    
 typ  real_list_t  real  ::  sample_data  (  100  )  typ  (  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  /=  )  exit  allocate  (  real_list_temp  %  next  )  real_list_temp  =>  real_list_temp  %  next  end do 

Fortran-2003 lägger till stöd för procedurpekare. Som en del av C Interoperability -funktionen stöder Fortran-2003 också inbyggda funktioner för att konvertera pekare i C-stil till Fortran-pekare och tillbaka.

Go har tips. Dess deklarationssyntax är likvärdig med C, men skriven tvärtom och slutar med typen. Till skillnad från C har Go sophämtning och tillåter inte pekarritmetik. Referenstyper, som i C++, finns inte. Vissa inbyggda typer, som kartor och kanaler, är förpackade (dvs internt är de pekare till föränderliga strukturer) och initieras med hjälp av make- funktionen . I ett tillvägagångssätt för enhetlig syntax mellan pekare och icke-pekare, har piloperatorn ( -> ) släppts: punktoperatorn på en pekare hänvisar till fältet eller metoden för det därhänvisade objektet. Detta fungerar dock bara med en nivå av inriktning.

Java

Det finns ingen explicit representation av pekare i Java . Istället implementeras mer komplexa datastrukturer som objekt och arrayer med hjälp av referenser . Språket tillhandahåller inga explicita pekarmanipuleringsoperatorer. Det är fortfarande möjligt för kod att försöka avreferera en nollreferens (nollpekare), vilket resulterar i att ett körtidsundantag kastas . Det utrymme som upptas av minnesobjekt utan referens återvinns automatiskt av skräpinsamling vid körning.

Modula-2

Pekare implementeras lika mycket som i Pascal, liksom VAR- parametrar i proceduranrop. Modula-2 är ännu starkare typad än Pascal, med färre sätt att undkomma typsystemet. Några av varianterna av Modula-2 (som Modula-3 ) inkluderar sophämtning.

Oberon

Precis som med Modula-2 finns pekare tillgängliga. Det finns fortfarande färre sätt att undvika typsystemet och så Oberon och dess varianter är fortfarande säkrare med avseende på pekare än Modula-2 eller dess varianter. Precis som med Modula-3 är sophämtning en del av språkspecifikationen.

Pascal

Till skillnad från många språk som har pekare tillåter standard ISO Pascal endast pekare att referera till dynamiskt skapade variabler som är anonyma och tillåter dem inte att referera till statiska eller lokala standardvariabler. Den har ingen pekare aritmetik. Pekare måste också ha en associerad typ och en pekare till en typ är inte kompatibel med en pekare till en annan typ (t.ex. en pekare till ett tecken är inte kompatibel med en pekare till ett heltal). Detta hjälper till att eliminera typsäkerhetsproblem som är inneboende med andra pekarimplementationer, särskilt de som används för PL/I eller C . Det tar också bort vissa risker orsakade av dinglande pekare , men möjligheten att dynamiskt släppa refererat utrymme genom att använda standardförfarandet för avyttring (som har samma effekt som den fria biblioteksfunktionen som finns i C ) gör att risken för att pekare inte har dinglat. eliminerats helt.

Men i vissa kommersiella och öppen källkodskompilatorimplementeringar av Pascal (eller derivator) — som Free Pascal , Turbo Pascal eller Object Pascal i Embarcadero Delphi — tillåts en pekare att referera till statiska eller lokala standardvariabler och kan castas från en pekartyp till annan. Dessutom är pekararitmetiken obegränsad: genom att lägga till eller subtrahera från en pekare flyttas den med det antalet byte i endera riktningen, men genom att använda Inc- eller Dec -standardprocedurerna med den flyttas pekaren med storleken på den datatyp som den deklareras peka på . En otypad pekare tillhandahålls också under namnet Pointer , som är kompatibel med andra pekaretyper.

Perl

Programmeringsspråket Perl stöder pekare, även om det sällan används , i form av packnings- och uppackningsfunktionerna. Dessa är endast avsedda för enkla interaktioner med kompilerade OS-bibliotek. I alla andra fall använder Perl referenser , som är skrivna och inte tillåter någon form av pekarritmetik. De används för att konstruera komplexa datastrukturer.

Se även

externa länkar