BREVE SINTESI
(che metterò anche in cima):
(0) Pensare ai puntatori come indirizzi è spesso un buon strumento di apprendimento ed è spesso l'implementazione effettiva di puntatori a tipi di dati ordinari.
(1) Ma su molti, forse la maggior parte, i compilatori puntatori a funzioni non sono indirizzi, ma sono più grandi di un indirizzo (in genere 2x, a volte più), o in realtà sono puntatori a una struttura in memoria che contiene gli indirizzi di funzioni e cose come un pool costante.
(2) I puntatori ai membri dei dati e i puntatori ai metodi sono spesso ancora più strani.
(3) Codice x86 legacy con problemi di puntatore FAR e NEAR
(4) Diversi esempi, in particolare IBM AS / 400, con "puntatori fat" sicuri.
Sono sicuro che puoi trovare di più.
DETTAGLIO:
UMMPPHHH !!!!! Molte delle risposte finora sono risposte "programmatore weenie" abbastanza tipiche - ma non weenie del compilatore o weenie hardware. Dal momento che fingo di essere un weenie hardware e spesso lavoro con weenies di compilatore, lasciami buttare i miei due centesimi:
Su molti compilatori C, probabilmente la maggior parte, un puntatore a dati di tipo T
è, in effetti, l'indirizzo di T
.
Belle.
Ma, anche su molti di questi compilatori, alcuni puntatori NON sono indirizzi. Puoi dirlo guardando sizeof(ThePointer)
.
Ad esempio, i puntatori alle funzioni sono talvolta molto più grandi degli indirizzi ordinari. Oppure, possono comportare un livello di riferimento indiretto. Questo articolofornisce una descrizione che coinvolge il processore Intel Itanium, ma ne ho visti altri. In genere, per chiamare una funzione è necessario conoscere non solo l'indirizzo del codice funzione, ma anche l'indirizzo del pool di costanti della funzione: un'area di memoria da cui vengono caricate le costanti con una singola istruzione di caricamento, piuttosto che il compilatore debba generare una costante a 64 bit tra più istruzioni Load Immediate e Shift e OR. Pertanto, anziché un singolo indirizzo a 64 bit, sono necessari 2 indirizzi a 64 bit. Alcune ABI (Application Binary Interfaces) lo spostano di 128 bit, mentre altri usano un livello di indiretta, con il puntatore a funzione che in realtà è l'indirizzo di un descrittore di funzione che contiene i 2 indirizzi effettivi appena menzionati. Che è migliore? Dipende dal tuo punto di vista: prestazioni, dimensioni del codice, e alcuni problemi di compatibilità - spesso il codice presuppone che un puntatore possa essere lanciato su un long o un long long, ma può anche presumere che il long long sia esattamente 64 bit. Tale codice potrebbe non essere conforme agli standard, ma tuttavia i clienti potrebbero desiderare che funzioni.
Molti di noi hanno dolorosi ricordi della vecchia architettura segmentata Intel x86, con NEAR POINTER e FAR POINTERS. Per fortuna ormai sono quasi estinti, quindi solo un breve riassunto: in modalità reale a 16 bit, l'indirizzo lineare reale era
LinearAddress = SegmentRegister[SegNum].base << 4 + Offset
Considerando che in modalità protetta, potrebbe essere
LinearAddress = SegmentRegister[SegNum].base + offset
con l'indirizzo risultante verificato contro un limite impostato nel segmento. Alcuni programmi utilizzavano dichiarazioni di puntatori FAR e NEAR C / C ++ non standard, ma molti hanno appena detto *T
--- ma c'erano opzioni di compilatore e linker quindi, ad esempio, i puntatori di codice potrebbero essere vicini a puntatori, solo un offset di 32 bit rispetto a qualsiasi cosa sia in il registro CS (Code Segment), mentre i puntatori di dati potrebbero essere puntatori FAR, specificando sia un numero di segmento a 16 bit che un offset a 32 bit per un valore di 48 bit. Ora, entrambe queste quantità sono sicuramente correlate all'indirizzo, ma poiché non hanno le stesse dimensioni, quale di esse è l'indirizzo? Inoltre, i segmenti avevano anche permessi - sola lettura, lettura-scrittura, eseguibile - oltre a cose relative all'indirizzo reale.
Un esempio più interessante, IMHO, è (o, forse, era) la famiglia IBM AS / 400. Questo computer è stato uno dei primi a implementare un sistema operativo in C ++. I puntatori su questo machime erano in genere 2 volte la dimensione effettiva dell'indirizzo, ad esempio come questa presentazionedice puntatori a 128 bit, ma gli indirizzi effettivi erano 48-64 bit e, di nuovo, alcune informazioni extra, quella che viene chiamata una capacità, che forniva autorizzazioni come lettura, scrittura e un limite per prevenire l'overflow del buffer. Sì: puoi farlo in modo compatibile con C / C ++ - e se questo fosse onnipresente, il PLA cinese e la mafia slava non andrebbero ad hackerare in così tanti sistemi informatici occidentali. Ma storicamente la maggior parte della programmazione C / C ++ ha trascurato la sicurezza per le prestazioni. Cosa più interessante, la famiglia AS400 ha permesso al sistema operativo di creare puntatori sicuri, che potevano essere dati a codice non privilegiato, ma che il codice non privilegiato non poteva forgiare o manomettere. Ancora una volta, la sicurezza, e sebbene conforme agli standard, il codice C / C ++ molto sciatto e non conforme agli standard non funzionerà in un sistema così sicuro. Ancora una volta, ci sono standard ufficiali,
Ora, uscirò dalla mia soapbox di sicurezza e menzionerò altri modi in cui i puntatori (di vari tipi) spesso non sono realmente indirizzi: puntatori ai membri dei dati, puntatori ai metodi delle funzioni dei membri e le loro versioni statiche sono più grandi di un indirizzo ordinario. Come dice questo post :
Esistono molti modi per risolverlo [problemi legati all'ereditarietà singola o multipla e all'eredità virtuale]. Ecco come il compilatore di Visual Studio decide di gestirlo: un puntatore a una funzione membro di una classe ereditata da moltiplicatori è in realtà una struttura. "E proseguono dicendo" Lanciare un puntatore a funzione può cambiare le sue dimensioni! ".
Come probabilmente puoi immaginare dal mio pontificare sulla (in) sicurezza, sono stato coinvolto in progetti hardware / software C / C ++ in cui un puntatore è stato trattato più come una capacità che come un indirizzo non elaborato.
Potrei continuare, ma spero che tu abbia avuto l'idea.
BREVE SINTESI
(che metterò anche in cima):
(0) pensare ai puntatori come indirizzi è spesso un buon strumento di apprendimento ed è spesso l'implementazione effettiva di puntatori a tipi di dati ordinari.
(1) Ma su molti, forse la maggior parte, i compilatori puntatori a funzioni non sono indirizzi, ma sono più grandi di un indirizzo (in genere 2X, a volte più), o in realtà sono puntatori a una struttura in memoria che contiene gli indirizzi di funzioni e cose come un pool costante.
(2) I puntatori ai membri dei dati e i puntatori ai metodi sono spesso ancora più strani.
(3) Codice x86 legacy con problemi di puntatore FAR e NEAR
(4) Diversi esempi, in particolare IBM AS / 400, con "puntatori fat" sicuri.
Sono sicuro che puoi trovare di più.