Qual è il punto dei puntatori in C++ quando posso semplicemente dichiarare le variabili? Quando è opportuno usarli?Qual è il punto dei puntatori?
risposta
I puntatori sono meglio compresi da C & Le differenze di C++ nel passaggio delle variabili alle funzioni.
Sì, è possibile passare a un'intera variabile oa un puntatore (il gergo è per valore o riferimento, rispettivamente).
Ma cosa succede se la variabile è una matrice di 20 megabyte di byte, come hai deciso di leggere un intero file in una matrice? Passare per valore sarebbe sciocco: perché dovresti copiare 20 mega per questa operazione, e se finisci per modificarlo (vale a dire che è un parametro esterno) devi copiare quel 20 mega INDIETRO?
È meglio solo "puntare" su di esso. Tu dici "ecco un puntatore a un grande blob di memoria". E questa piccola osservazione indiretta fa risparmiare un sacco di tempo.
Una volta compreso questo, tutto il resto è praticamente lo stesso.Riorganizzare gli elementi in un elenco diventa solo lo scambio di puntatori piuttosto che copiare ogni elemento in giro, non è necessario sapere quanto è grande le cose sono quando si avvia fuori, ecc
Il tuo commento ha un senso (simile alla mia risposta), ma sarò pignolo sulla terminologia. "Ecco un riferimento a un grande blob di memoria", dovrebbe essere davvero "Ecco un puntatore a un grande blob di memoria". I riferimenti hanno un significato in C++ e sono diversi dai puntatori (sebbene siano molto simili e correlati, sono diversi). – Tom
A volte si dispone di una funzione che deve restituire una certa quantità di memoria che non è una quantità impostata, come la lettura da un file.
bool ReadDataFromFile(char** contents);
Si dichiarerà un contenuto di char * e si passerà l'indirizzo di tale puntatore alla funzione. Quella funzione assegnerebbe quindi la memoria e il puntatore punterebbe al contenuto al ritorno.
I puntatori sono molto utili quando si tratta di strutture di dati le cui dimensioni e forma non sono note in fase di compilazione (liste di pensieri, alberi, grafici, array, stringhe, ...).
Modifica
Queste risposte relative potrebbero anche aiutare (la risposta in alto nel secondo collegamento è sicuramente la pena dare un'occhiata):
In C++ I Cannot Grasp Pointers and Classes
What are the barriers to understanding pointers and what can be done to overcome them?
Sì, ma non riesco a vedere come l'indirizzo di memoria che passa può aiutare con l'allocazione della memoria! – Babiker
È necessario un modo per fare riferimento a un blocco di memoria che è stato assegnato in modo dinamico. Non è possibile creare dinamicamente un nome di variabile, i puntatori consentono di indirizzare la memoria indirettamente ("quel compagno seduto alla fine della barra" contro "Sam"). –
puntatori sono anche grandi per passare un argomento mutabile a una funzione in modo che il chiamante possa "vedere il cambiamento". Potresti chiedertelo, "ma perché non usare un riferimento?". Mi piace l'argomento di Google:
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments
Ero un fan di quell'idea ma ne sono uscito di recente. Penso di averlo preso da "Code Complete". Avendo usato entrambi, non penso che faccia alcuna differenza perché il chiamante può vedere un cambiamento è possibile dal fatto che il parametro è un riferimento non const. Potrebbe fare la differenza se tendi ad avere grandi funzioni così quando vedi * o -> sai qualcosa al di fuori della funzione sta cambiando. Penso che sia una di quelle cose che dovrebbero essere standard su un progetto, ma quale convenzione è usata non è tutto ciò che è importante per IMO. – markh44
ho avuto la stessa identica domanda quando stavo imparando puntatori, hanno semplicemente non sembrano importanti, ma come si procede, si scopre che sono a volte molto utili.
I puntatori vengono utilizzati spesso in situazioni di programmazione. Ad esempio, quando si fa riferimento una matrice in base al nome, come ad esempio array[i] = 3;
Il compilatore sta facendo un po 'di matematica di fantasia che finisce per trasformare il codice in
(indirizzo di serie) + (sizeof (elementi di un array) * i) = 3;
Inoltre, consentono di creare alberi, elenchi concatenati e altre strutture dati, che scoprirai man mano che ne impari di più.
Nifty. array [i] == i [array] –
Da Wikipedia:
C++ riferimenti differiscono da puntatori in diversi modi essenziali:
- non è possibile riferirsi direttamente a un oggetto di riferimento dopo è definito; qualsiasi occorrenza del suo nome si riferisce direttamente all'oggetto è riferimenti.
- Una volta creato un riferimento, non può essere successivamente fatto riferimento a un altro oggetto; diciamo che non può essere riposizionato. Questo è spesso fatto con i puntatori .
- I riferimenti non possono essere nulli, mentre i puntatori possono; ogni riferimento fa riferimento a a qualche oggetto, sebbene possa o non possa essere valido .
- I riferimenti non possono essere non inizializzati. Poiché è impossibile reinizializzare un riferimento, è necessario inizializzare non appena sono stati creati . In particolare, locale e variabili globali deve essere inizializzato in cui sono definiti, e riferimenti che sono membri di dati di classe casi deve essere inizializzato nella lista di inizializzazione del costruttore della classe .
Con le limitazioni di cui sopra, i puntatori sono tutto quello che hai come un contenitore leggero per fare riferimento all'oggetto, soprattutto se si sta facendo qualcosa con polimorfico o dinamica. L'array dinamico di puntatori di classe puramente astratti per rappresentare finestre può essere un esempio.
Dato che io sono troppo pigro per pensare a un esempio di polimorfismo tirerò uno da cplusplus.com:
// pointers to base class
#include <iostream>
using namespace std;
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
};
class CRectangle: public CPolygon {
public:
int area()
{ return (width * height); }
};
class CTriangle: public CPolygon {
public:
int area()
{ return (width * height/2); }
};
int main() {
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << rect.area() << endl;
cout << trgl.area() << endl;
return 0;
}
Nel codice precedente, puntatore CPolygon *
viene utilizzato per fare riferimento CRectangle
oggetto e CTriangle
oggetto.
I puntatori non sono necessari o addirittura preferiti in quell'esempio - i riferimenti sono. –
Al livello base, i puntatori consentono di associare blocchi disgiunti di memoria. Un esempio semplice (inventato, certo) di dove i puntatori potrebbero aiutarti a trovarti in un algoritmo che richiede un array di 1000000000000 numeri interi. una tale varietà sarebbe troppo grande per entrare all'interno della RAM della macchina su cui sto scrivendo in questo momento se ho provato una definizione come ad esempio:
int bigMatrix[1000000000000]; // I can't allocate this much contiguous memory
Tuttavia, se creo un singolo array di puntatori, posso mantenere i sub-array su un array di dischi di medie dimensioni.
int *bigMatrix[1000000]; // Each pointer refers to a sub-array
// of 1000000 elements on disk
Certo, dovrò scrivere codice alla pagina nei sottocampi se/quando l'utente lo richiede, compreso nascondendo la notazione di matrice dietro un metodo di accesso. Detto questo, i puntatori mi consentono di creare le associazioni ad hoc di cui ho bisogno quando ne ho bisogno.
Oltre all'efficienza e alla flessibilità.Il principale punto di puntatori in C/C++ è che è come funziona l'hardware, non si poteva wite un driver di periferica, gestore di memoria o della cache efficiente senza l'utilizzo di puntatori da qualche parte lungo la linea.
Uno degli obiettivi principali del progetto per il primo compilatore C era di essere un "linguaggio assembly portatile" e di essere in grado di fare in un linguaggio di livello superiore qualsiasi cosa si potesse fare con il codice assembly/macchina tradizionale. Questo significa essere in grado di manipolare gli indirizzi direttamente - che è il punto dei puntatori.
Tuttavia seguendo il principio KISS non uso puntatori a meno che realmente stanno facendo le cose più semplici.
Dal punto di vista architettonico, i puntatori sono un modo economico per modellare un 0 .. n relazione:
struct A {
vector<const B *> *pBees;
A() : pBees(nullptr) {}
void notice_bee(const B *pB) {
if (!pBees)
pBees = new vector<const B *>;
pBees.push_back(pB)
}
~A() {
delete pBees; // no need to test, delete nullptr is safe
}
size_t bees_noticed() { return pBees ? pBees->size : 0 }
};
Se la stragrande maggioranza degli oggetti A non avrà mai bisogno di prestare attenzione a qualsiasi oggetto B, non v'è nessun motivo per cui ogni oggetto A dovrebbe avere un vettore di lunghezza zero. Sotto Gnu C++ 4.0.1, sizeof (vector) è 12; sizeof (vettore *) è 4.
Supponiamo che si scrive un editor di testo. In questo caso, non si conosce la dimensione del documento in anticipo. Si potrebbe essere tentati di dichiarare qualcosa di simile
char Document[10000];
ma allora certamente un giorno qualcuno si desidera utilizzare il vostro editor su un documento molto più grande. Quindi questo tentativo è inutile; e quello che vi serve è un modo per chiedere nuova memoria in fase di esecuzione (invece di tempo di compilazione).
Il modo C++ di fare questo sta usando l'operatore new, che restituisce un puntatore alla memoria appena allocata:.
char* pDocument = new char[getSizeOfDocument()];
(Si noti che questo esempio è semplificato eccessivamente Nella vita reale, si sarebbe certamente non lo fanno è come questo, ma invece usa qualcosa come std :: string e std :: vector, che internamente esegue questa allocazione per te.)
Mi sembra che tu non abbia ancora imparato a conoscere l'allocazione dinamica della memoria. Esistono due modi per allocare memoria in C++: staticamente e dinamicamente. Hai già familiarità con l'allocazione statica (ad esempio dichiarando le variabili). Questo è fantastico se sai al momento di scrivere il tuo programma esattamente quante variabili (o meglio, memoria) hai bisogno.
Ma cosa succede se non lo fai? Ad esempio, diciamo che stai leggendo che contiene un sacco di numeri che devi tenere traccia di. Perché? Non lo so, ma non è questo il punto.
Beh si potrebbe iniziare con una matrice:
int array[100];
Ma cosa succede se ci sono più di 100 numeri nel file? Alla fine si vorrà una soluzione più flessibile:
int *array = new int[size];
// do stuff
delete [] array;
Questo ti dà molta più flessibilità e consente di creare strutture dati più dinamici.
Puntatori memorizzare un indirizzo di memoria, in modo da li potrebbe usare ogni volta che avete avuto bisogno l'indirizzo di memoria di qualcosa. Questo può essere utile per cose come la gestione della memoria, sapere dove sono memorizzati i tuoi dati. Il mio professore ci ha detto che questa è una bella cosa per i programmi avanzati in termini di garantire che i tuoi dati siano contigui nella memoria (come le stringhe in stile c sono).
Spero che questo sia una qualche forma di aiuto per voi!
-Zen, un novizio in C++
questo è soggettivo, polemico e molto probabilmente un duplicato. – lothar
Penso che questa sia una domanda valida da un programmatore C++ di inizio. Non capisco perché sia stato etichettato come soggettivo, e Babiker sta facendo del suo meglio per non essere polemico. Solo se qualcuno dovesse perdere il karma saltando dentro e dicendo qualcosa come "Ed è per questo che il C++ è un linguaggio scurrile rispetto alla" diventerebbe polemico, e ciò non sta accadendo. –
Sono d'accordo, questa è una buona domanda che probabilmente capita a molte persone che iniziano. Se si tratta di un duplicato, fornire un collegamento al thread originale in modo che il richiedente possa trovare le risposte. – Chuck