Peker (databehandling)

Jeg anser oppgaveutsagn og pekervariabler for å være blant de mest verdifulle skattene innen informatikk.— Donald Knuth , Strukturert programmering med gå til Statements [ 1 ]

I informatikk er en peker et programmeringsspråkobjekt , hvis verdi refererer til (eller " peker på") en annen verdi lagret et annet sted i dataminnet ved å bruke adressen . En peker refererer til en plassering i minnet, og å få verdien lagret på den plasseringen er kjent som å referere pekeren. Til analogi kan et sidetall i en boks indeks tenkes som en peker til den tilsvarende siden; Å referere en peker vil være som å gå til siden med det angitte sidetallet i indeksen.

Datapekere forbedrer ytelsen til repeterende operasjoner som skiftstrenger , oppslagstabeller , kontrolltabeller og trestrukturer betydelig . Spesielt er det ofte mye billigere i tid og rom å kopiere og avvise pekere enn det er å kopiere og få tilgang til dataene som pekeren peker til.

Pekere brukes også til å holde adressene til inngangspunkter for subrutiner for samtaler i prosedyreprogrammering og lenker for kobling til dynamiske koblingsbiblioteker (DLLer) under kjøretid . I objektorientert programmering brukes pekere til funksjoner for join -metoder , mange ganger ved å bruke det som kalles virtuelle metodetabeller .

En peker er en enklere, mer konkret implementering av den mer abstrakte referansedatatypen . Flere språk støtter en slags peker, selv om noen har flere restriksjoner på bruken enn andre. Mens en "peker" brukes til å referere til referanser generelt, gjelder den mer korrekt for datastrukturer hvis grensesnitt eksplisitt tillater at pekeren kan manipuleres (aritmetisk via pekeraritmetikk ) som en minneadresse, i motsetning til en minneadresse . alternativ der dette ikke er mulig. [ referanse nødvendig ] Fordi pekere både kan beskytte og gi tilgang til minneadresser, er det risiko forbundet med bruken, spesielt i sistnevnte tilfelle. Vanligvis lagres primitive pekere i et format som ligner på et heltall ; Imidlertid vil forsøk på å avvise eller "slå opp" til en peker hvis verdi aldri var en gyldig minneadresse, føre til et programkrasj. For å lindre dette potensielle problemet, som et spørsmål om typesikkerhet , betraktes pekere som en egen type parameterisert av en datatype de peker på, selv om den underliggende representasjonen er et heltall. Andre tiltak kan også tas (som validering og grensekontroll, for å verifisere innholdet i pekervariabelen inneholder en verdi som både er en gyldig minneadresse og innenfor det numeriske området som prosessoren er i stand til å adressere).

Historikk

Harold Lawson er kreditert med oppfinnelsen av pekeren i 1964. [ 2 ] I 2000 ble Lawson overrakt Computer Science Pioneer Award av IEEE "[for oppfinnelsen av pekervariabelen og introduksjonen av dette konseptet i PL/I , gir dermed for første gang muligheten til å håndtere lenkede lister fleksibelt på et overordnet språk for allmenn bruk". [ 3 ] I følge Oxford English Dictionary dukket ordet peker først opp på trykk som en stabelpeker i et teknisk notat fra System Development Corporation .

Formell beskrivelse

I informatikk er en peker en referansetype .

En dataprimitiv (eller ganske enkelt primitiv) er alle data som kan leses fra eller skrives til datamaskinens minne ved å bruke en minnetilgang (for eksempel er både en byte og et ord primitiver).


Et dataaggregat (eller ganske enkelt aggregert ) er en gruppe primitiver som er logisk sammenhengende i minnet og som samlet sett blir sett på som data (for eksempel kan et aggregat være 3 logisk sammenhengende byte, hvis verdier representerer de 3 koordinatene til et punkt i rommet). Når et aggregat er helt sammensatt av samme type primitiv, kan det aggregatet kalles en matrise; på en måte er et primitivt flerbyteord en rekke byte, og noen programmer bruker ord på denne måten.

En minnepeker (eller bare peker ) er en primitiv verdi som er ment å brukes som en minneadresse; en peker sies å peke til en minneadresse. En peker sies også å peke til data [i minnet] når verdien til pekeren er referanseminneadressen.

Mer generelt er en peker en referansetype , og en peker sies å referere til data som er lagret et sted i minnet ; for å få disse dataene, blir pekeren dereferert . Funksjonen som skiller pekere fra andre referansetyper er at en pekers verdi er ment å bli tolket som en minneadresse, som er et konsept på ganske lavt nivå.

Referanser tjener som et indirektionsnivå: Verdien til en peker bestemmer hvilken minneadresse (dvs. hvilke data) som brukes i en beregning. Fordi indirektion er et grunnleggende aspekt ved algoritmer, uttrykkes pekere ofte som en grunnleggende datatype i programmeringsspråk ; I form av statiske (eller sterkt ) typede programmeringsspråk, bestemmer typen peker hvilken type data pekeren peker til.

Bruk i datastrukturer

Når du konfigurerer datastrukturer som lister , køer og trær, er det nødvendig å ha flagg for å kontrollere hvordan strukturen implementeres og kontrolleres. Typiske eksempler på pekere er startpekere, sluttpekere og stabelpekere . Disse flaggene kan enten være absolutte ( reell fysisk adresse eller en virtuell adresse i virtuelt minne ) eller relative (en forskyvning fra en startende absolutt ("base") adresse som vanligvis bruker færre biter enn en full adresse, men som vanligvis krever en ekstra aritmetisk operasjon å løse).

Relative adresser er en form for manuell minnesegmentering , og de deler mange av sine fordeler og ulemper. En to-byte offset, som inneholder et 16-bits usignert heltall, kan brukes til å gi relativ adressering på opptil 64 kilobyte av en datastruktur. Dette kan enkelt utvides til 128K, 256K eller 512K hvis den spisse adressen tvinges til å justere - mellomord, ord eller dobbeltord (men, som krever en bitvis "venstreskift"-operasjon til 1, 2 eller 3 biter - for å juster forskyvningen med en faktor på 2, 4 eller 8, før du legger den til baseadressen). Imidlertid har slike leiligheter vanligvis mange problemer, og for programmererens bekvemmelighet er absolutte programmereradresser (og underliggende det, et flatt adresseområde ) å foretrekke.

En én-byte forskyvning, slik som hex ASCII- verdien til et tegn (f.eks. X'29') kan brukes til å peke på en alternativ heltallsverdi (eller indeks) i en matrise (f.eks. X'01'). På denne måten kan tegn oversettes veldig effektivt fra rådata , til en brukbar sekvensiell indeks og deretter til en absolutt adresse uten å bruke en oppslagstabell .

Bruk i kontrolltabeller

Kontrolltabeller , som brukes til å kontrollere programflyt , bruker vanligvis mye pekere. Pekere, vanligvis innebygd i en tabelloppføring, kan for eksempel brukes til å holde inngangspunkter til subrutiner som skal utføres, basert på visse betingelser definert i selve tabelloppføringen. Pekerne kan imidlertid være enkle indekser til andre separate, men relaterte tabeller som omfatter et sett av de faktiske adressene eller riktige adressene (avhengig av de tilgjengelige programmeringsspråkkonstruksjonene). De kan også brukes til å peke (tilbake) til tidligere tabelloppføringer (som i sløyfebehandling) eller fremover for å sende noen tabelloppføringer (som i en svitsj eller "tidlig" utgang fra en sløyfe). For sistnevnte formål kan "pekeren" ganske enkelt være inngangsnummeret til selve tabellen og kan gjøres om til en gjeldende adresse ved hjelp av enkel aritmetikk.

Opprinnelse i datamaskinarkitektur

Pekere er en veldig tynn abstraksjon på toppen av adresseringsmulighetene som tilbys av de fleste moderne arkitekturer . I det enkleste oppsettet tildeles en adresse eller en numerisk indeks til hver minneenhet i systemet, der enheten typisk er en byte eller et ord - avhengig av om arkitekturen er byteadresserbar eller ordadresserbar - transformerer effektivt alt minne i et veldig stort utvalg . Så hvis vi har en adresse, gir systemet en operasjon for å hente verdien som er lagret i minneenheten på den adressen (vanligvis ved å bruke maskinens generelle registre ).

I det vanlige tilfellet er en peker stor nok til å holde flere adresser enn ikke-minneenheter i systemet. Dette introduserer muligheten for at et program kan prøve å få tilgang til en adresse som tilsvarer ingen minneenhet, enten fordi det ikke er nok minne installert (dvs. utenfor det tilgjengelige minneområdet) eller fordi arkitekturen ikke støtter slike adresser. . I det første tilfellet, på visse plattformer som Intels x86- arkitektur , kalles det en segmenteringsfeil (segmentbrudd). Det andre tilfellet er mulig i den nåværende implementeringen av AMD64 , der pekere er 64 bit lange og adresser bare strekker seg til 48 biter. Der må pekere samsvare med visse kanoniske regler (adresser), så hvis en ikke-kanonisk peker avvises, oppstår en generell beskyttelsesfeil .

På den annen side har noen systemer flere minneenheter enn adresser. I dette tilfellet brukes et mer komplekst skjema, for eksempel minnesegmentering eller personsøking for å bruke forskjellige deler av minnet til forskjellige tider. De siste inkarnasjonene av x86-arkitekturen støtter opptil 36 bits fysiske minneadresser, som ble tilordnet det 32-bits lineære adresserommet ved hjelp av en PAE - søkemekanisme. Dermed kan bare 1/16 av det mulige totale minnet få tilgang til om gangen. Et annet eksempel fra samme familie av datamaskiner var 16-bits beskyttet modus 80286 -prosessoren , som imidlertid kun støttet 16 MiB fysisk minne, og kunne få tilgang til maksimalt 1 GiB virtuelt minne, men adressekombinasjonen på 16 biter og segmentregistre fikk tilgang til mer enn 64 KiB i en tungvint datastruktur. Noen begrensninger for ANSI-pekeraritmetikk kan ha vært på grunn av de segmenterte minnemodellene til denne familien av prosessorer. [ referanse nødvendig ]

For å gi et konsistent grensesnitt, gir noen arkitekturer minnetilordnet I/O , som lar noen adresser referere til minneenheter, mens andre refererer til enhetsregistre for andre enheter i datamaskinen. Det er analoge konsepter som filforskyvninger, array-indekser og eksterne objektreferanser som tjener noen av de samme formålene som adresser til andre typer objekter.

Bruker

Pekere støttes direkte uten begrensninger på språk som PL/1 , C , C++ , Pascal , og de fleste assembly-språk. De brukes hovedsakelig til konstruksjon av referanser, som igjen er essensielle for konstruksjon av nesten alle datastrukturer, samt for å sende data mellom de ulike delene av et program.

I funksjonelle programmeringsspråk som er avhengige av lister, håndteres pekere og referanser abstrakt av språket ved å bruke innebygde konstruksjoner som const .

Når man arbeider med matriser, involverer den kritiske oppslagsoperasjonen vanligvis et såkalt adresseberegningstrinn som innebærer å konstruere en peker til ønsket dataelement i matrisen. Hvis dataelementene i matrisen har lengder som er delbare med to potenser, er denne aritmetikken vanligvis litt mer effektiv. [ referanse nødvendig ] Polstring brukes vanligvis som en mekanisme for å sikre at dette er tilfelle, til tross for økte minnekrav. I andre datastrukturer, for eksempel koblede lister, brukes pekere som referanser for å eksplisitt koble en del av strukturen til en annen.

Pekere brukes til å sende parametere ved referanse. Dette er nyttig hvis programmereren vil at en funksjons modifikasjoner av en parameter skal være synlig for funksjonens kaller av funksjonen. Dette er også nyttig for å returnere flere verdier fra en funksjon.

Pekere kan også brukes til å tildele og deallokere dynamiske variabler og matriser i minnet. Siden en variabel mange ganger kan bli overflødig etter at den har tjent sin hensikt, noe som resulterer i tap av minne å holde på, er det derfor god praksis å deallokere den når den ikke lenger er nødvendig, ved å bruke den opprinnelige pekerreferansen. Unnlatelse av å gjøre det kan resultere i en minnelekkasje (der tilgjengelig ledig minne gradvis reduseres, eller i alvorlige tilfeller raskt, på grunn av en akkumulering av mange redundante minneblokker).

Pekere i C

Den grunnleggende syntaksen for å definere en peker er: [ 4 ]

int * ptr ;

Dette erklærer ptrsom identifikator for et objekt, som følger:

  • peker til et objekt av typenint

Dette vises vanligvis mer kortfattet som ' ptrer en pekepinn til int'.

Fordi C-språket ikke spesifiserer en implisitt initialisering for automatiske lagringslivstidsobjekter, [ 5 ] må man ofte passe på at adressen som ptrpekes til er gyldig; så det er noen ganger foreslått at en peker eksplisitt kan initialiseres til null-pekerverdien , som tradisjonelt er spesifisert i C med den standardiserte makroen NULL:[ 6 ]

int * ptr = NULL ;

Å frarefere en null-peker i C produserer udefinert oppførsel , [ 7 ] som kan være katastrofal. Imidlertid stopper de fleste implementeringer ganske enkelt kjøringen av det aktuelle programmet, vanligvis med en segmenteringsfeil.

Imidlertid kan initialiserte pekere unødvendig hindre programanalyse, og dermed skjule feil.

I alle fall, når en peker er erklært, er det neste logiske trinnet at den peker på noe:

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

Dette tilordner adresseverdien atil ptr. For eksempel, hvis den aer lagret i minneplassering 0x8130, vil verdien av ptrvære 0x8130 etter tildeling. For å henvise til pekeren, brukes stjernen igjen:

* ptr = 8 ;

Dette betyr å ta innholdet av ptr(som er 0x8130), "lokalisere" adressen i minnet og sette verdien til 8. Hvis den åpnes igjen senere, vil dens nye verdi være 8.

Dette eksemplet kan være klarere hvis minnet ikke undersøkes direkte. Anta at den aer plassert på adressen 0x8130 i minnet og ptrpå 0x8134; den forutsetter også at det er en 32-bits maskin, så en inthar en bredde på 32 biter. Følgende er hva som vil være i minnet etter at følgende kodebit er utført:

int a = 5 ; int * ptr = NULL ;
Adresse Innhold
0x8130 0x00000005
0x8134 0x00000000

(NULL-pekeren som vises her er 0x00000000.) Ved å tilordne adressen til aen ptr:

ptr = &a ;

produserer følgende minneverdier:

Adresse Innhold
0x8130 0x00000005
0x8134 0x00008130

Så derereferanse ptrvia koding:

* ptr = 8 ;

datamaskinen vil ta innholdet til ptr (som er 0x8130), 'lokalisere' den adressen og tilordne 8 til den plasseringen og produsere følgende minne:

Adresse Innhold
0x8130 0x00000008
0x8134 0x00008130

Det er klart at tilgangen til avil produsere verdien 8 fordi den forrige setningen endret innholdet til avia pekeren ptr.

C-matriser

I C er matriseindeksering formelt definert i form av aritmetiske pekere, det vil si at språkspesifikasjonen krever at den array[i]er ekvivalent med *(array + i). [ 8 ] Dermed kan arrays i C betraktes som pekere til påfølgende minneområder (uten tomme mellomrom), [8] og syntaksen for å få tilgang til arrays er identisk med den som brukes for dereferering av pekere. For eksempel kan en matrise arraydeklareres og brukes som følger:

int array [ 5 ]; /* Erklærer 5 sammenhengende heltall */ int * ptr = array ; /* Matriser kan brukes som pekere */ ptr [ 0 ] = 1 ; /* Pekere kan indekseres med array-syntaks */ * ( matrise + 1 ) = 2 ; /* Matriser kan derefereres med pekersyntaks */ * ( 1 + matrise ) = 3 ; /* Pekertilføyelse er kommutativ */ 2 [ matrise ] = 4 ; /* Subscript-operatoren er kommutativ */

Dette tildeler en blokk med fem heltall og blokkarraynavn , som fungerer som en peker til blokken. En annen vanlig bruk av pekere er å peke på dynamisk allokert minne fra malloc som returnerer en påfølgende minneblokk på ikke mindre enn den forespurte størrelsen som kan brukes som en matrise.

Selv om de fleste operatører på arrays og pekere er likeverdige, er det viktig å merke seg at operatøren sizeofvil være annerledes. I dette eksemplet vil sizeof (array) evaluere til 5*sizeof(int)(størrelsen på matrisen), mens sizeof(ptr)sizeof vil evaluere til (int*)størrelsen på selve pekeren.

Standardverdier for en matrise kan deklareres som:

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

Hvis det antas å arraybli funnet i minnet som starter ved adresse 0x1000 på en 32-bits liten endian -maskin , vil minnet inneholde følgende (verdier er i heksadesimal så vel som adresser):

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

Fem heltall er representert her: 2, 4, 3, 1 og 5. Disse fem heltall opptar 32 biter (4 byte) hver med den minst signifikante byten lagret først (dette er en liten endian CPU-arkitektur) og lagres fortløpende med start på adresse 0x1000.

C-syntaksen med pekere er:

  • arraybetyr 0x1000
  • array+1betyr 0x1004 (merk at det faktisk betyr at "1" legges til når størrelsen på en int(4 byte) ikke bokstavelig talt "pluss en")
  • *arraybetyr å henvise til innholdet i array. Vurdere innholdet som en minneadresse (0x1000), se etter verdien på det stedet (0x0002).
  • array[i]betyr elementnummer i, grunntall 0, av matrisen som oversettes til*(array + i)

Det siste eksemplet er hvordan du får tilgang til innholdet i array. Å bryte det ned:

  • array + ier minneplasseringen til det (i +1) elementet i matrisen
  • *(array + i)tar den minneadressen og fjerner referanser til tilgang til verdien.

For eksempel array[3]er det synonymt med *(array+ 3), det vil si *(0x1000 + 3*sizeof (int)), som sier "fjern referansen til verdien lagret i 0x100C", i dette tilfellet 0x0001.

Koblet liste i C

Her er et eksempel på å definere en koblet liste i C.

/* tom lenket liste er representert med NULL * eller en annen vaktverdi */ #define EMPTY_LIST NULL struct link { void * data ; /* data for denne lenken */ struct link * neste ; /* neste lenke; EMPTY_LIST hvis ingen */ };

Merk at denne peker-rekursive definisjonen i hovedsak er den samme som den referanse-rekursive definisjonen av Haskell -programmeringsspråket :

datalink a = Null | _ Ulemper til ( Link til )

Niler den tomme listen og Cons a (Link a)er en ulempecelle av en type amed en annen binding også av typen a.

Definisjonen av referanser er imidlertid typesjekket og bruker ikke de potensielt forvirrende tokenverdiene. Av denne grunn, i C, håndteres datastrukturer normalt gjennom wrapper-funksjoner , som kontrolleres nøye for korrekthet.

Passering etter adresse ved hjelp av pekere

Pekere kan brukes til å sende variabler etter adressen deres, slik at verdien kan endres. Som et eksempel kan du vurdere følgende C -kode :

#include <stdio.h> /* en kopi av int n kan endres inne i funksjonen uten å påvirke anropskoden */ void passbyvalue ( int n ) { n = 12 ; } /* Send i stedet en peker til m. Ingen kopi m av seg selv opprettes */ void passbyadresse ( int * m ) { * m = 14 ; } int main ( void ) { int x = 3 ; /* send en kopi av verdien av x som et argument */ passbyverdi ( x ); // verdien har endret seg inne i funksjonen, men x er fortsatt 3 herfra og ut /* pass adressen til x som argument */ passbyadresse ( & x ); //faktisk ble x endret av funksjonen og nå her er den lik 14 returner 0 ; }

Dynamisk minnetildeling

Pekere brukes til å lagre og administrere adressene til dynamisk tildelte minneblokker . Disse blokkene brukes til å lagre dataobjekter eller sett med objekter. Mer strukturerte, objektorienterte språk gir et minneområde, kalt haugen eller gratislageret, hvorfra objekter er dynamisk allokert.

Følgende C-eksempelkode illustrerer hvordan struktur- og referanseobjekter er dynamisk allokert. C-standardbiblioteket gir funksjonen malloc()til å tildele minneblokker fra haugen. Størrelsen på et objekt er nødvendig for å tilordne det som en parameter og returnere en peker til en nylig tildelt minneblokk som er egnet for lagring av objektet, eller en null-peker returneres hvis tilordningen mislykkes.

/* Delelagervare */ strukturelement { _ int id ; /* Delenummer */ char * navn ; /* Delnavn */ flytekostnad ; _ /* Kostnad */ }; /* Tildel og initialiser et nytt elementobjekt */ struct Item * make_item ( const char * name ) { struct Element * Element ; /* Tildel en minneblokk for et nytt elementobjekt */ Item = ( struct Item * ) malloc ( sizeof ( struct Item )); if ( Vare == NULL ) returner NULL ; /* Initialiser medlemmene til det nye elementet */ memset ( Item , 0 , sizeof ( struct Item )); Element -> id = -1 ; Element -> navn = NULL ; Vare -> kostnad = 0,0 ; /* Lagre en kopi av navnet i det nye elementet */ Vare -> navn = ( char * ) malloc ( strlen ( navn ) + 1 ); if ( Vare -> navn == NULL ) { gratis ( Vare ); returner NULL ; } strcpy ( Vare -> navn , navn ); /* Returner det nylig opprettede artikkelobjektet */ returnItem ; _ }

Følgende kode viser hvordan minneobjekter dynamisk deallokeres, dvs. returneres til haugen eller gratislageret. C-standardbiblioteket gir funksjonen free()til å deallokere en tidligere tildelt minneblokk og returnere den til haugen igjen.

/* Tildel et elementobjekt */ void destroy_item ( struct Item * item ) { /* Se etter en null-objektpeker */ if ( element == NULL ) returnere ; /* Tildel navnestrengen som er lagret i elementet */ if ( element -> navn != NULL ) { gratis ( vare -> navn ); element -> navn = NULL ; } /* Dealloker selve vareobjektet */ gratis ( vare ); }

Minnetilordnet maskinvare

I noen dataarkitekturer kan pekere brukes til å direkte manipulere minne eller minnetilordnede enheter.

Å tildele adresser til pekere er et uvurderlig verktøy i mikrokontrollerprogrammering . Nedenfor er et enkelt eksempel på å deklarere en peker av typen int og initialisere den til en heksadesimal adresse i dette eksemplet konstanten 0x7FFF:

int * hardware_address = ( int * ) 0x7FFF ;

På midten av 1980-tallet gikk det tregt å bruke BIOS for å få tilgang til PC-videofunksjoner. Applikasjoner som var skjermintensive ble vanligvis brukt for å få direkte tilgang til CGA -videominne ved å kaste de heksadesimale konstantene 0xb8000 til en peker til en matrise med 80 usignerte 16-bits int-verdier. Hver verdi besto av en ASCII -kode i den lave byten og en farge i den høye byten. Derfor, for å sette bokstaven 'A' i linje 5, kolonne 2 hvit på lyseblått, kan man skrive kode som følgende:

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

Innskrevne og kastet pekere

På mange språk har pekere den ekstra begrensningen at objektet de peker til har en bestemt type . For eksempel kan en peker erklæres å peke på et heltall; det vil være språket som prøver å hindre programmereren i å peke på objekter som ikke er heltall, for eksempel flytende tall, ved å eliminere noen feil.

For eksempel i C

int * penger ; røye * poser ;

moneyville være en heltallspeker og bagsville være en tegnpeker. Følgende vil gi en "tilordning fra en pekertype" kompilatoradvarsel under GCC

poser = penger ;

fordi moneyog bagsble deklarert med forskjellige typer. For å undertrykke kompilatoradvarselen, må du gjøre det eksplisitt at du virkelig vil typecaste.

poser = ( røye * ) penger ;

som sier cast heltallspekeren moneytil en tegnpeker og tilordne den til bags.

Et utkast til 2005 C-standarden krever at støping av en peker avledet fra en type til en av en annen type skal opprettholde justeringens korrekthet for begge typer (6.3.2.3 Pekere, par 7): [ 9 ]

char * external_buffer = "abcdef" ; int * interne_data ; intern_data = ( int * ) ekstern_buffer ; // UDEFINERET OPPFØRSEL hvis "resulterende peker // ikke er riktig justert"

På språk som tillater pekeraritmetikk, tar pekeraritmetikk hensyn til størrelsen på typen. For eksempel, å legge til et heltall til en peker produserer en annen peker som peker til en adresse som er ganger større enn størrelsen på typen. Dette lar oss enkelt beregne elementadressen til en matrise av en gitt type, som vist i C-matriseeksemplet ovenfor. Når en peker av en type konverteres til en annen type av en annen størrelse, bør programmereren forvente at den aritmetiske pekeren beregnes annerledes. I C for eksempel hvis matrisen moneystarter på 0x2000 og sizeof (int)er 4 byte mens den sizeof (char)er 1 byte, vil (money+1)den peke til 0x2004, men den (bags+1)vil peke til 0x2001. Andre risikoer ved casting inkluderer tap av data, når "brede" data skrives til "smale" steder (for eksempel bags[0] = 65537;), uventede resultater når det er bitoffset- verdier og sammenligningsproblemer på alt mellom signerte verdier vs usignerte verdier.

Selv om det vanligvis er umulig å avgjøre på kompileringstidspunktet hvilke casts som er trygge, lagrer noen språk hva slags informasjon under kjøringen som kan brukes til å bekrefte at disse farlige castene er gyldige under kjøring. Andre språk aksepterer ganske enkelt en konservativ tilnærming til sikre rollebesetninger, eller ingen i det hele tatt.

Gjøre pekere sikrere

Fordi en peker lar et program prøve å få tilgang til et objekt som kanskje ikke er definert, kan slike pekere være kilden til en rekke programmeringsfeil . Men nytten av pekere er så stor at det kan være vanskelig å programmere uten dem. Følgelig har mange språk laget konstruksjoner designet for å gi noen av de nyttige funksjonene til pekere uten noen av fallgruvene deres , også noen ganger kalt pekerfeller. I denne sammenheng er pekere som adresserer minne direkte (som brukt i denne artikkelen) kjent som råpekere , i motsetning til smarte pekere eller andre varianter .

Et av de største problemene med pekere er at siden de kan manipuleres direkte som et tall, kan de få dem til å peke på ubrukte adresser eller data som brukes til andre formål. Mange språk, inkludert funksjonelle programmeringsspråk og de siste imperative språkene som Java , erstatter pekere med en mer ugjennomsiktig type referanse, vanligvis referert til som en referanse , som bare kan brukes til å referere til objekter og ikke manipulerer tall som forhindrer denne typen feil. Indeksering av en matrise behandles som et spesialtilfelle.

En peker som ikke har noen adresse tilordnet kalles en jokerpeker . Ethvert forsøk på å bruke disse uinitialiserte pekerne kan forårsake uventet oppførsel, enten fordi startverdien ikke er en gyldig adresse, eller fordi bruken av dem kan skade andre deler av programmet. Resultatet er vanligvis en segmenteringsfeil , lagringsbrudd eller naturlig gren (hvis brukt som grenadresse eller funksjonspeker).

På systemer med eksplisitt minneallokering er det mulig å lage en hengende referansepeker for å deallokere minneadressen den peker inn i. Denne typen peker er farlig og subtil, siden en de-allokert minneregion kan inneholde de samme dataene som den gjorde før den ble deallokert, men kan deretter bli re-allokert og overskrevet av fremmed kode, ukjent for koden tidligere. Søppelsamlerspråk forhindrer denne typen feil fordi deallokering gjøres automatisk når det ikke er flere referanser i omfanget .

Noen språk, for eksempel C++ , støtter smarte pekere , som bruker en enkel form for referansetelling for å hjelpe til med tildelingen av en post fra dynamisk minne, i tillegg til å fungere som en referanse. I fravær av referansesykluser, hvor et objekt refererer til seg selv indirekte gjennom en sekvens av smarte pekere, eliminerer disse muligheten for hengende pekere og minnelekkasjer. Strenger i Delphi støtter naturlig referansetelling.

Nullpeker

En null-peker har en reservert verdi for å indikere at pekeren ikke refererer til et gyldig objekt. Null-pekere brukes ofte for å representere forhold som slutten av en liste med ukjent lengde eller unnlatelse av å utføre en type handling, så bruken av null-pekere kan sammenlignes med null - typer og verdien av ingenting i en opsjonstype .

Null-pekere blir ofte sett på som lik null-verdier i relasjonsdatabaser , men har noe forskjellig semantikk. I de fleste programmeringsspråk betyr en null-peker "ingen verdi", mens i en relasjonsdatabase betyr en null-verdi "ukjent verdi". Dette fører til viktige forskjeller i praksis: to null-pekere anses som like i de fleste programmeringsspråk, men to null-verdier er ikke i relasjonsdatabaser (du vet ikke om de er like, siden de representerer ukjente verdier). ).

I noen programmeringsspråkmiljøer (i det minste, for eksempel en proprietær Lisp-implementering [ referanse nødvendig ] ), kan verdien som brukes som null-pekeren (kalt nili Lisp) faktisk være en peker til en intern datablokk som er nyttig for applikasjonen (men ikke eksplisitt tilgjengelig fra brukerprogrammer), slik at det samme registeret kan brukes som en nyttig konstant og en rask måte å få tilgang til interne deler av applikasjonen. nilDette er kjent som en ('null') vektor .

I C er to null-pekere av hvilken som helst type garantert å sammenligne med samme datatype [ 10 ]​ Makroen NULLer en implementering definert av en NULL-pekerkonstant, [ 6 ]0​ som i C99 kan uttrykkes portabelt som en konvertert heltallsverdi implisitt eller eksplisitt til typen void*. [ 11 ]

Vanligvis betyr det å referere NULL-pekeren å prøve å lese eller skrive til minnet som ikke er allokert, dette utløser en segmenteringsfeil eller tilgangsbrudd. Dette kan i seg selv representere, for utvikleren, en feil i programmet, eller det blir et unntak som kan fanges opp. Det er imidlertid visse omstendigheter der dette ikke er tilfelle. For eksempel, i ekte x86-modus, er adressen 0000:0000 lesbar og vanligvis skrivbar, derfor er null-peker-dereferencing en perfekt gyldig, men generelt uønsket handling som kan føre til uønsket oppførsel.udefinert, men den krasjer ikke programmet. Vær også oppmerksom på at det er tider når NULL-dereferering er tilsiktet og veldefinert, for eksempel BIOS-kode, skrevet i C, for x86 16-bits enheter i reell modus, kan du skrive IDT til fysisk adresse 0 til maskinen, dereferensing pekeren til NULL for skriving. Det er også mulig for kompilatoren å eksternt optimalisere `NULL`-dereferansepekeren, unngå en segmenteringsfeil, men ikke forårsake annen uønsket oppførsel .

I C++, siden makroen NULLble arvet fra C, er heltallsliteralen tradisjonelt foretrukket fremfor null for å representere en nullpekerkonstant. [ 12 ] Imidlertid har C++11 introdusert en eksplisitt konstantnullptr som skal brukes i stedet.

En null-peker skal ikke forveksles med en ikke-initialisert peker: En null-peker er garantert å sammenligne ulik med enhver peker som peker til et gyldig objekt. Avhengig av språket og applikasjonen har imidlertid en uinitialisert peker enten en ubestemt verdi (tilfeldig eller meningsløs), eller en spesifikk verdi som ikke nødvendigvis er en slags null-pekerkonstant.

I 2009 uttalte CAR Hoare [ 13 ] [ 14 ]​ at han i 1965 oppfant nullreferanse som en del av Algol W -språket , selv om NIL siden 1959 hadde eksistert i Lisp. I den referansen fra 2009 beskriver Hoare oppfinnelsen sin som en "million dollar feil":

Jeg kaller feilen min en milliardfeil. Det var oppfinnelsen, i 1965, av nullreferansen. På den tiden utformet jeg det første omfattende typesystemet for referanser i et objektorientert språk (ALGOL W). Målet mitt var å sikre at all bruk av referanser må være helt trygg, med kontrollen som gjøres automatisk av kompilatoren. Men jeg kunne ikke motstå å sette inn en nullreferanse, rett og slett fordi det var så enkelt å implementere. Dette har ført til utallige feil, sårbarheter og systemkrasj, sannsynligvis forårsaket en milliard dollar med smerte og skade i løpet av de siste førti årene.

Fordi en null-peker ikke peker til et meningsfullt objekt, resulterer forsøk på å avreferere en null-peker vanligvis (men ikke alltid) i en kjøretidsfeil eller umiddelbar programkrasj.

  • I C er null-peker-dereference-atferd udefinert [ 15 ] Mange implementeringer fører til at denne typen kode resulterer i at programmet stopper med et tilgangsbrudd , fordi null-peker-representasjonen er valgt for å være en adresse som ikke er tilordnet av systemet for objekt Oppbevaring. Denne oppførselen er imidlertid ikke universell.
  • I Java utløser tilgang til en nullreferanse en NullPointerException (NPE), som kan fanges opp av feilhåndteringskode, men i praksis er det foretrukket å sikre at slike unntak aldri forekommer.
  • I. NET, utløser tilgang til nullreferansen et NullReferenceException. Selv om å fange disse generelt anses som dårlig praksis, kan denne typen unntak fanges opp og håndteres av programmet.
  • I Objective-C kan meldinger sendes til et objekt nil(som i hovedsak er en null-peker) uten å få programmet til å bryte; meldingen ignoreres ganske enkelt, og returverdien (hvis noen) er nilenten eller 0, avhengig av typen. [ 16 ]

I språk med en tagging-arkitektur kan en null-peker muligens erstattes med en markert union som tvinger eksplisitt håndtering av det unntakstilfelle, faktisk kan en null-peker muligens sees på som en tagget peker med en beregnet tag.

Auto relativ peker

Begrepet selvrelativ peker kan referere til en peker hvis verdi tolkes som en offset fra adressen til selve pekeren, så hvis en datastruktur, , har et selvrelativt pekerelement, , peker det til en del av seg selv , så kan den flyttes til minnet uten å måtte oppdatere verdien til . [ 17 ] Det siterte patentet bruker også begrepet relativ selvpeker for å bety det samme. Men betydningen av det begrepet har blitt brukt på andre måter:

  • Det brukes ofte til å bety en forskyvning fra adressen til en struktur og ikke fra adressen til selve pekeren. [ referanse nødvendig ]
  • Det har blitt brukt til å bety en peker som inneholder sin egen adresse, som kan være nyttig for å rekonstruere i et hvilket som helst vilkårlig minneområde en samling av datastrukturer som peker til hverandre. [ 18 ]

Grunnpeker

En grunnpeker er en peker hvis verdi er en offset fra verdien til en annen peker. Dette kan brukes til å lagre og laste datablokkene, ved å tilordne startadressen til blokken til basispekeren. [ 19 ]

Flere indirekte

På noen språk kan en peker referere til en annen peker, noe som krever flere derefererende operasjoner for å komme til den opprinnelige verdien. Selv om hvert nivå av indirekte kan legge til en ytelseskostnad, er det noen ganger nødvendig å gi korrekt oppførsel for komplekse datastrukturer . For eksempel, i C er det typisk å definere en koblet liste , i form av et element som inneholder en peker til neste element i listen:

strukturelement _ { struct element * neste ; int verdi ; }; struct element * head = NULL ;

Denne applikasjonen bruker en peker til det første elementet i listen som en erstatning for hele listen. Hvis en ny verdi legges til i begynnelsen av listen, må overskriften endres til å peke på det nye elementet. Siden C-argumenter alltid sendes av verdi, gjør bruk av dobbel indirektion det mulig å implementere innlegget på riktig måte, og har den ønskelige bieffekten av å eliminere spesialtilfellekode for å håndtere innsettinger foran på listen:

// Gitt en ordnet liste i *-overskriften, sett inn elementelementet på det første //-stedet der alle tidligere elementer har mindre enn eller lik verdi. void insert ( struct element ** head , struct element * item ) { strukturelement ** p ; _ // p peker på en peker til et element for ( p = hode ; * p != NULL ; p = & ( * p ) -> neste ) { if ( element -> verdi <= ( * p ) -> verdi ) bryte ; } element -> neste = * p ; * p = element ; } // Innringeren gjør dette: insert ( & head , item );

I dette tilfellet, hvis verdien av itemer mindre enn for head, vil den som ringer bli oppdatert head, korrekt til adressen til den nye varen.

Et grunnleggende eksempel er i argv -argumentet til hovedfunksjonen i C (og C++), som er gitt i prototypen som char **argv- dette er fordi variabelen argvi seg selv er en peker til en array av strenger (en array array), så den *argver en peker til streng 0 (etter konvensjon til programnavn), og **argvdet er tegn 0 i streng 0.

Peker til funksjon

På noen språk kan en peker referere til kjørbar kode, det vil si at den kan peke til en funksjon, metode eller prosedyre. En funksjonspeker vil lagre adressen til en funksjon som kalles. Selv om denne mekanismen kan brukes til å kalle funksjoner dynamisk, er det ofte en foretrukket teknikk for virus og andre skadevareforfattere.

int a , b , x , y ; int sum ( int n1 , int n2 ); // Funksjon med to heltallsparametere som returnerer en heltallsverdi int ( * fp )( int , int ); // Funksjonspeker som kan peke til en funksjon som sum fp = & sum ; // fp peker nå på funksjonen sum x = ( * fp )( a , b ); // Sumfunksjonen kaller med argumentene a og b y = sum ( a , b ); // Sum funksjon kaller med argumentene a og b

Wild pointer

En jokerpeker er en peker som ikke er initialisert (det vil si at en jokerpeker ikke har fått tildelt en adresse) og kan føre til at programmet krasjer eller oppfører seg merkelig. I programmeringsspråkene Pascal eller C kan pekere som ikke er spesifikt initialisert, peke til uforutsigbare adresser i minnet.

Følgende eksempelkode viser en jokerpeker:

int func ( ugyldig ) { char * p1 = malloc ( størrelse på ( char )); /* (verdi (udefinert) fra et sted i haugen */ char * p2 ; /* wild pointer (uinitialisert) */ * p1 = 'a' ; /* Dette er greit, forutsatt at malloc() ikke returnerte NULL. */ * p2 = 'b' ; /* Dette kaller en udefinert atferd */ }

Her kan p2den peke hvor som helst i minnet, så å utføre oppgaven * p2 = 'b'kan ødelegge et ukjent minneområde eller forårsake en segmenteringsfeil .

Simulering ved hjelp av en matriseindeks

Det er mulig å simulere oppførselen til pekeren ved å bruke en indeks til en matrise (vanligvis endimensjonal)

Hovedsakelig for språk som ikke eksplisitt støtter pekere, men som støtter arrays, kan arrayen betraktes og behandles som om den var hele minneområdet (innenfor rammen av den bestemte arrayen) og enhver indeks til den kan betraktes som ekvivalent til et assembly-språkregister for generell bruk (som peker på individuelle byte, men hvis faktiske verdi er i forhold til begynnelsen av matrisen, ikke dens absolutte adresse i minnet). Forutsatt at matrisen for eksempel er en 16 megabyte sammenhengende tegndatastruktur , kan de individuelle bytene (eller en streng med sammenhengende byte i matrisen) adresseres direkte og manipuleres ved å bruke matrisenavnet med et heltall 31-bit uten fortegn som simulert peker (dette er ganske likt C-array -eksemplet vist ovenfor). Pekeraritmetikk kan simuleres ved indeksaddisjon eller subtraksjon, med minimal ekstra overhead sammenlignet med ekte pekeraritmetikk.

Det er til og med teoretisk mulig, ved å bruke teknikken ovenfor, å koble sammen med en spillsimulator passende instruksjoner for å simulere hvilken som helst maskinkode eller den mellomliggende ( byteskode ) til en prosessor/språk på et annet språk som ikke støtter pekere i det hele tatt (for eksempel Java / javascript ). For å oppnå dette, kan binær kode innledningsvis lastes inn i sammenhengende byte i arrayet slik at simulatoren "leser", tolker og kjører helt i minnet i selve arrayen. Om nødvendig kan grensekontroll vanligvis aktiveres av kompilatoren, for å unngå bufferoverløpsproblemer helt (ellers kode for hånd i simulatoren).

Pekere i programmeringsspråk

I programmering er det mulig å lage prosedyrer, strukturer, variabler, konstanter blant annet, når hver av disse strukturene utføres har den en adresse i minnet, det er praktisk å kjenne til denne typen informasjon når du arbeider med språk som støtter pekere på en ikke-abstrakt måte.

Deretter skal vi se hvordan pekere fungerer i variabler, strukturer og fagforeninger. Pekere til disse lagringsstrukturene vil bli rettet til de første medlemmene av hver struktur, fagforening eller array.

Strukturer

Datastrukturer tildeler separate minnesegmenter for hvert medlem, noe som betyr at hvert medlem har sin egen minneadresse (sin egen peker).

Vi kan grafisk representere en datastruktur kalt "Struktur" som følger:

1 1 4 byte. +-----+-----+---------+ | Til | B | C | Medlemmer. +-----+-----+---------+ 0x1 0x2 0x3 adresse. ^ | Grunnpekeren for hele strukturen.

Som du kan se, er det 3 numeriske type metoder i minnet kalt "A","B" og "C", de to første metodene (A og B) er av typen Byte og har 1 byte reservert hver i forskjellige segmenter i minnet , så de har forskjellige adresser (0x01 og 0x02 i deres respektive rekkefølge).

Minneadressen til strukturen er generelt lik adressen til dets første medlem, adressen til dets andre medlem er lik adressen til det første medlemmet, pluss størrelsen i byte, og adressen til det tredje medlemmet er lik adressen til det første medlemmet, pluss størrelsen på de to foregående medlemmene, og så videre.

Fagforeninger

Hovedforskjellen mellom en fagforening og en struktur er minnet som brukes, i motsetning til strukturer, bruker fagforeninger bare mengden minne til sitt største medlem, og alle medlemmer deler en enkelt adresse i minnet.

For eksempel:

4 byte. +-----------+ | ABC | Medlemmer. +-----------+ 0x1 adresse.

Medlemmene "A" og "C" kan bare inneholde verdier mellom 0 og 255 (Byte Type), medlem "B" kan ha verdier mellom 0 og 4,294,967,295 (heltallstype), hvis medlem "A" endrer sin verdi til 13, det samme gjør medlemmene "B" og "C" (A,B og C er lik 13), hvis medlem "B" endrer verdien til 4000, forblir medlemmene "A" og "C" bare på 160 siden de er prøver å oversette verdien 4000 til en byte (A og C er lik 160, B = 4000), er den totale størrelsen på foreningen 4 byte fordi medlemmene deler det samme minnet (foreningen veier det samme som dens medlem med største minnereserve).

Matriser

Matriser er nesten det samme som strukturer, de reserverer minne for hver matrise, minneadressen til hvert medlem er fortløpende med summen av de forrige, pluss størrelsene deres, og adressen til den totale matrisen er lik adressen til det første medlemmet , den eneste forskjellen mellom en matrise og en struktur er at hver matrise har samme datatype (medlemmene i en struktur kan være av forskjellige typer).

Vi kan representere en matrise med rang 3 (heltallstype) i minnet som følger.

4 4 4 byte. +-----+-----+-----+ | 0 | 1 | 2 | identifikator. +-----+-----+-----+ 0x1 0x5 0x9 adresse.

Som man kan se, har hvert medlem en minneadresse med 4 byte fra hverandre. Grunnpekeren til hele matrisen er adressen til dens første matrise (Array@ = Array[0]@).

Støtte i programmeringsspråk

Ada

Ada er et sterkt skrevet språk der alle pekere er skrevet og kun sikre typekonverteringer er tillatt. Alle pekere er som standard initialisert til null, og ethvert forsøk på å få tilgang til informasjonen via en peker til nullforårsaker et unntak . I Ada kalles pekere tilgangstyper . Ada-83 tillot ikke aritmetikk på tilgangstyper (selv om forskjellige kompilatorer gir det som en funksjon utenfor mønsteret), men Ada-95 støtter aritmetiske typer på pakkesikre tilgangstyper System.Storage_Elements.

GRUNNLEGGENDE

Flere eldre versjoner av BASIC for Windows-plattformen hadde støtte for STRPTR() for å returnere adressen til en streng, og for VARPTR() for å returnere adressen til en variabel. Visual Basic 5 hadde også støtte for OBJPTR() for å returnere adressen til et grensesnittobjekt, og for en ADDRESSOF-operatør for å returnere adressen til en funksjon. Typene av alle disse er heltall, men verdiene deres tilsvarer disse verdiene etter pekertyper.

Nyere dialekter av BASIC , som FreeBASIC eller BlitzMax , har imidlertid uttømmende pekerimplementeringer. I FreeBASIC blir aritmetikk på pekere ANY(tilsvarer void*C) behandlet som om pekeren ANYvar en bredde på byte. I motsetning til C, ANYkan ikke pekere skilles fra. Konvertering mellom ANYog andre typer pekere vil heller ikke generere noen advarsler.

dimme som heltall f = 257 dimme som en hvilken som helst ptr g = @ f dim som heltall ptr i = g assert ( * i = 257 ) assert ( ( g + 4 ) = ( @ f + 1 ) )

C og C++

I C og C++ er pekere variabler som lagrer adresser og kan være null . Hver peker har en type den peker til, men programmereren kan fritt konvertere mellom pekertyper (men ikke mellom en funksjonspeker og ikke-funksjonspekertype). En spesiell pekertype kalt "tom peker" gjør det mulig å peke til enhver type variabel (ikke funksjon), men er begrenset av det faktum at den ikke kan avvises direkte. Selve adressen kan noen ganger manipuleres direkte ved å kaste en peker til og fra en heltallstype av tilstrekkelig størrelse, selv om resultatene er implementeringsdefinerte og kan faktisk forårsake udefinert oppførsel; mens pre-C-standarder ikke har en heltallstype som garantert er stor nok, spesifiserer C99 det definerte navnet uintptr_ʈ typedef i , men en applikasjon trenger ikke gi det. < stdint.h>

C++ støtter fullt ut C-pekere og C-typecasting. Den støtter også en ny gruppe typecasting-operatører for å hjelpe med å fange opp noen farlige uønskede casting på kompileringstidspunktet. Siden C++11 gir C++ Standard Library også smarte pekere ( unique_ptr, shared_ptrog weak_ptr) som kan brukes i noen situasjoner som et trygt alternativ til C++ primitive pekere. C++ støtter også en annen referansetype, ganske forskjellig fra en peker, ganske enkelt kalt en referanse eller referansetype .

Pekeraritmetikk , det vil si muligheten til å endre destinasjonsadressen til en peker med aritmetiske operasjoner (så vel som størrelsessammenlikninger), begrenses av standardspråket til å holde seg innenfor grensene til et enkelt matriseobjekt (eller like etter det) , fordi ellers ville det forårsake udefinert oppførsel . Legge til eller trekke fra en peker som ruller med et multiplum av størrelsen på datatypen den peker til. For eksempel, å legge til 1 til en peker til 4-byte heltallsverdier vil øke pekeren med 4. Dette har effekten av å øke pekeren til å peke til neste element i en sammenhengende rekke med heltall – som ofte returnerer et resultat gitt . Pekeraritmetikk kan ikke utføres på pekere voidfordi void-typen ikke har noen størrelse, og kan derfor ikke legge til adressen til pekere, selv om gcc og andre kompilatorer utfører bytearitmetikk void*som en ikke-standard utvidelse. For å jobbe "direkte" med bytes returnerer de vanligvis pekere til BYTE*, unsigned char*ellers BYTEer det ikke definert i standardbiblioteket som brukes.

Pekeraritmetikk gir programmereren en unik måte å håndtere forskjellige typer på: ved å legge til og trekke fra antall elementer som kreves i stedet for den faktiske offset i byte. (selv om pekeren er char, charer den definert til å alltid ha en størrelse på én byte, la elementforskyvningen av pekeraritmetikk i praksis være lik én byteforskyvning.) Spesielt sier C-definisjonen eksplisitt at syntaksen de a[n], som er det n-te elementet i matrisen til a , er ekvivalent med *(a+n), som er innholdet i elementet pekt på av a+n. Dette innebærer at n[a]det tilsvarer a[n], og kan skrives, for eksempel, a[3]eller 3[a]lik for å få tilgang til det fjerde elementet i en matrise a.

Selv om det er kraftig, kan pekeraritmetikk være en kilde til datafeil . Dette har en tendens til å forvirre nybegynnere programmerere , og tvinge dem inn i forskjellige sammenhenger: et uttrykk kan være vanlig aritmetisk en eller pekeraritmetisk en, og noen ganger er det lett å forveksle det ene med det andre. Som svar på dette tillater ikke mange moderne dataspråk på høyt nivå (f.eks . Java ) direkte minnetilgang ved bruk av adresser. Også den sikre dialekten til C, Cyclone , adresserer mange av problemene med pekere. Se C programmeringsspråk for å undersøke mer.

Pekeren void, eller void*, støttes i ANSI C og C++ som en generisk pekertype. En peker til voidkan lagre en adresse til en hvilken som helst ikke-funksjonsdatatype, og, i C, konverteres den implisitt til en hvilken som helst annen pekertype ved tildeling, men må eksplisitt konverteres hvis den refereres inline. K&R bruker C char*med det formål å "type agnostisk peker" (før ANSI C).

int x = 4 ; ugyldig * q = & x ; int * p = q ; /* void* konverterer implisitt til int*: gyldig i C, men ikke i C++ */ int i = * p ; int j = * ( int * ) q ; /* når det refereres inline, ingen implisitt konvertering */

C++ tillater ikke implisitt konvertering fra void*til andre pekertyper, selv ikke i oppgaver. Dette var en designbeslutning for å unngå slurvete og til og med uønskede flushes, selv om de fleste kompilatorer bare gir ut advarsler, ikke feil, når de møter andre kaster.

int x = 4 ; ugyldig * q = & x ; // int* p = q; Dette mislykkes i C++: det er ingen implisitt konvertering fra void* int * til = ( int * ) q ; // cast stil C int * b = static_cast < int *> ( q ); // C++ rollebesetning

I C++ er det ingen void&og (referanse til void) for å komplementere void*(peker til void), siden referanser oppfører seg som aliaser til variablene de peker på, og kan aldri være en variabel hvis type er void.

C#

I programmeringsspråket C# støttes pekere kun under visse forhold: enhver kodeblokk som inkluderer pekere må merkes med nøkkelordet unsafe. Vanligvis krever slike blokker høyere sikkerhetstillatelser enn pekerløs kode for å få lov til å kjøre. Syntaksen er i hovedsak den samme som i C++, og den spisse adressen kan administreres i både administrert og uadministrert minne . Pekere til administrert minne (enhver peker til et administrert objekt) må imidlertid deklareres ved hjelp av nøkkelordet , som hindrer søppelsamleren i å flytte det spisse objektet som en del av minnebehandlingen mens pekeren er ved sitt omfang, som beholder pekeradressen gyldig. fixed

Et unntak fra dette er å bruke strukturen til IntPtr, som er en sikker administrert tilsvarende int*, og som ikke krever usikret kode. Denne typen vises vanligvis når du bruker metoder for System.Runtime.InteropServices, for eksempel:

// Få 16 byte med minne fra prosessens uadministrerte minne IntPtr - peker = System . kjøretid . InteropServices . Marskalk . AllocHGlobal ( 16 ); // Gjør noe med det tildelte minnet // Gratis tildelt minne System . kjøretid . InteropServices . Marskalk . FreeHGlobal ( peker );

.NET Framework inkluderer mange klasser og metoder i systemet og navneområder System.Runtime.InteropServices(som klasse Marshal) som konverterer .NET-typer (for eksempel System.String) til og fra mange uadministrerte typer og pekere (for eksempel LPWSTReller void *) for å tillate kommunikasjon med uadministrert kode .

COBOL

COBOL - programmeringsspråket støtter pekere til variabler. Primitive objekter eller gruppedata (record) dataobjekter deklarert i LINKAGE SECTIONet program er iboende pekerbaserte, der det eneste minnet som er tildelt i programmet er adresserommet for dataelementet (vanligvis ett minneord). individuell), basert på pekere . I programmets kildekode brukes disse dataelementene som alle andre variabler WORKING-STORAGE, men innholdet deres er indirekte og implisitt tilgang til gjennom pekere LINKAGE.

Minneplassen for hvert pekt til dataobjekt tildeles vanligvis dynamisk ved hjelp av CALLeksterne setninger eller gjennom innebygde utvidede språkkonstruksjoner som EXEC CICSeller setninger .EXEC SQL

Utvidede versjoner av COBOL gir også pekervariabler deklarert med klausuler USAGE IS POINTER. Verdiene til disse pekervariablene settes og endres ved å bruke SETog deklarasjoner SET ADDRESS.

Noen utvidede versjoner av COBOL gir også trippelvariabler PROCEDURE-POINTER, som er i stand til å lagre adressene til kjørbar kode .

PL/I

PL/I - språket gir full støtte for pekere til alle datatyper (inkludert pekere til strukturer), rekursjon , multitasking , strenghåndtering og omfattende innebygde funksjoner . PL/I var et absolutt sprang fremover sammenlignet med sin tids programmeringsspråk. [ referanse nødvendig ]

D

Programmeringsspråket D er et derivat av C og C++, som er fullt kompatibelt med C-pekere og C-typecasting.

Eiffel

Det objektorienterte Eiffel-språket støtter pekere i form av referanser , som er skrevet og ikke tillater noen pekereritmetikk. ECMA - standarden for Eiffel inkluderer en "kapslingstype"-mekanisme som er ment å sikre et trygt tomrom .

Fortran

Fortran-90 introduserte sterkt skrevet pekerfunksjon. Pekere i Fortran inneholder mer enn bare en minneadresse. De innkapsler også nedre og øvre grenser for arraydimensjoner, trinn (f.eks. for å støtte vilkårlige arrayseksjoner) og andre metadata. En assosiasjonsoperator , => brukes til å knytte en peker til en variabel som har et attributt TARGET. Fortran-90-setningen kan også brukes ALLOCATEtil å knytte en peker til en minneblokk. For eksempel kan følgende kode brukes til å definere og lage en koblet listestruktur:

type real_list_t real :: sample_data ( 100 ) type ( real_list_t ), peker :: neste => null () end type type ( real_list_t ), mål :: min_real_list type ( real_list_t ), peker :: 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 legger til støtte for prosedyrepekere. Som en del av funksjonen C Interoperability , støtter Fortran-2003 også indre egenskaper for å konvertere pekere i C-stil til Fortran-pekere og baksider.

Go har tips. Deklarasjonssyntaksen tilsvarer syntaksen til C, men skrevet i revers og slutter med type. I motsetning til C, har Go søppelinnsamling, og tillater ikke pekereritmetikk. Som i C++ er det ingen referansetyper. Noen innebygde typer, for eksempel kart og kanaler, er innrammet (det vil si at de er interne pekere til foranderlige strukturer), og initialiseres ved hjelp av make. Som en annen tilnærming (enn referansetyper) til den enhetlige syntaksen mellom pekere og ikke-pekere, har piloperatoren ( ->) blitt droppet – det er mulig å bruke punktoperatoren direkte på en peker til en datatype for å få tilgang til et omfang eller metoden for den derefererte verdien, som om punktoperatoren brukes på den underliggende datatypen. Dette fungerer imidlertid bare med 1 nivå av indirekte.

Java

I motsetning til C , C++ eller Pascal , er det ingen eksplisitt representasjon av pekere i Java . I stedet implementeres mer komplekse datastrukturer som objekter og matriser ved hjelp av referanser. Språket tilbyr ikke eksplisitte pekermanipulasjonsoperatører. Det er fortsatt mulig for kode å forsøke å avreferere en nullreferanse (nullpeker), noe som resulterer i at et kjøretidsunntak blir kastet. Plass som er okkupert av ikke-refererte minneobjekter, blir automatisk gjenvunnet av søppelsamling under kjøring. [ 20 ]

Modula-2

Pekere er sterkt implementert som i Pascal, det samme er parametere VARi prosedyrekall. Modula-2 er enda mer lite fleksibel enn Pascal, med i det minste måter rundt typesystemet. Noen av Modula-2-variantene (som Modula-3 ) inkluderer søppelinnsamling.

Oberon

Som Modula-2 er pekere tilgjengelige. Det er fortsatt færre måter å omgå typesystemet på, og derfor er Oberon og dens varianter enda sikrere enn Modula-2- pekere eller deres varianter. Som med Modula-3 er søppelinnsamling en del av språkspesifikasjonen.

Pascal

I motsetning til mange språk som er avhengige av pekere, tillater ISO Pascal -standarden bare pekere å referere til dynamisk opprettede variabler som er anonyme og tillater dem ikke å referere til statiske standarder eller lokale variabler. [ 21 ] Den har ingen peker-aritmetikk. Pekere må også ha en tilknyttet type, og en peker til en type er ikke kompatibel med en peker til en annen type (for eksempel er en peker til en tegn ikke kompatibel med en peker til et heltall). Dette bidrar til å eliminere de iboende sikkerhetsproblemene med andre pekerimplementeringer, spesielt de som brukes for PL/I eller C . Den eliminerer også noen risikoer forårsaket av dinglende pekere , men muligheten til dynamisk å slippe plass referert ved hjelp av standardprosedyren dispose(som har samme effekt som bibliotekfunksjonen som freefinnes i C ) betyr at risikoen for hengende pekere ikke er fullstendig fjernet. [ 22 ]

Men i noen kommersielle og åpen kildekode-implementeringer av Pascal (eller avledede) kompilatorer – slik som Free Pascal , [ 23 ] ​Turbo Pascal eller Object Pascal i Embarcadero Delphi – er en peker tillatt å referere til statiske eller lokale variabler standarder og kan kastes fra en pekertype til en annen. Pekeraritmetikk, på den annen side, er begrenset: å legge til eller trekke fra en peker flyttes med det antallet byte på en hvilken som helst adresse, men ved å bruke standardprosedyrer Inceller Decflytte pekeren etter størrelsen på datatypen den er erklært å være.punkt. En utype peker er også gitt under navnet Pointer, som er kompatibel med andre pekertyper.

Pauscal

Pauscal - programmeringsspråket har sterk pekerstøtte, slik at du kan peke på variabler, strukturer, prosedyrer, prototyper, fagforeninger og til og med klasser og deres metoder. Pekere kan brukes til å lagre minneadressen til et objekt eller lage en referanse til det. Pauscal bruker pekere for å konvertere datatyper uten behov for noe eksternt applikasjonsprogrammeringsgrensesnitt (API), noe som øker hastigheten på programkjøringen og lar programmer være "native" til språket.

Perl

Perl -programmeringsspråket støtter pekere, selv om de sjelden brukes, i form av pakke- og utpakkingsfunksjoner. Disse er kun ment for enkel interaksjon med kompilerte OS-biblioteker. I alle andre tilfeller bruker Perl referanser , som er skrevet og ikke tillater noen pekereritmetikk. Disse brukes til å bygge komplekse datastrukturer. [ 24 ]

Se også

Referanser

  1. Donald Knuth (1974). "Structured Programming with go to Statements ("Structured Programming with goto statements")" (pdf) . Computing Surveys 6 ( 5): 261-301. doi : 10.1145/356635.356640 . Arkivert fra originalen 19. mai 2013. 
  2. Milepæler innen informatikk og informasjonsteknologi
  3. ^ "IEEE Computer Society-prisliste" . Arkivert fra originalen 22. mars 2011 . Hentet 16. april 2015 . 
  4. ISO/IEC 9899 , ​​klausul 6.7.5.1, avsnitt 1.
  5. ISO/IEC 9899 , ​​klausul 6.7.8, avsnitt 10.
  6. a b ISO/IEC 9899 , ​​klausul 7.17, avsnitt 3: NULL... som utvides til en implementering definert som null konstant peker...
  7. ISO/IEC 9899 , ​​klausul 6.5.3.2, paragraf 4, fotnote 87: Hvis en ugyldig verdi har blitt tildelt pekeren, er oppførselen til den unære operatøren * udefinert... Skriv inn de gyldige verdiene for å avreferere en peker av operatøren unary * er en null-peker...
  8. Plauger, PJ ; Brody, Jim (1992). ANSI og ISO Standard C programmeringsreferanse . Redmond, WA: Microsoft Press. s. 108, 51 . ISBN  1-55615-359-7 . "En matrisetype inneholder ingen ekstra hull fordi alle andre pakketyper er forseglet når de er satt sammen i matriser [på side 51]  ." 
  9. WG14 N1124 , C – Godkjente standarder: ISO/IEC 9899 – Programmeringsspråk – C , 6. mai 2005.
  10. ISO/IEC 9899 , ​​klausul 6.3.2.3, avsnitt 4.
  11. ISO/IEC 9899 , klausul 6.3.2.3, avsnitt 3.
  12. Stroustrup, Bjarne (mars 2001). "Kapittel 5: Pekere, matriser og strukturer: 5.1.1: Null". C++-programmeringsspråket (14. opplag av 3. utgave). USA og Canada: Addison–Wesley. s. 88 . ISBN  0-201-88954-4 . I C har det vært populært å definere en makro NULLfor å representere nullpekeren. På grunn av strengere typekontroll i C++, NULLfører bruk av vanlig 0, i stedet for noen foreslått makro, til færre problemer. Hvis du trenger å definere NULL. bruk : const int NULL = 0; Kvalifikatoren const(§ 5.4) forhindrer utilsiktet redefinering av NULLog sikrer at NULLden kan brukes når det kreves en konstant. » 
  13. Tony Hoare (2009). "Nullreferanser: Billion Dollar Mistake" . QCon London. Arkivert fra originalen 19. januar 2009 . Hentet 15. mars 2014 . 
  14. Tony Hoare (25. august 2009). "Nullreferanser: Billion Dollar Mistake" . InfoQ.com. 
  15. ISO/IEC 9899 , ​​klausul 6.5.3.2, avsnitt 4.
  16. Objective-C 2.0-programmeringsspråket , avsnittet "Sende meldinger til null" .
  17. Mal:Patenthenvisning
  18. Mal:Patenthenvisning
  19. Baserte pekere
  20. Nick Parlante, [1] , Stanford Computer Science Education Library , s. 9–10 (2000). (på engelsk)
  21. ISO 7185 Pascal Standard (uoffisiell kopi), avsnitt 6.4.4 Peker-typer et seq.
  22. J. Welsh, WJ Sneeringer og CAR Hoare, "Ambiguities and Insecurities in Pascal," Software Practice and Experience 7 , s. 685–696 (1977)
  23. Free Pascal Language Reference guide, avsnitt 3.4 Pekere
  24. // Lage referanser (Perl-referanser og nestede datastrukturer)

Eksterne lenker