Allocazione dinamica array C++
Ciao a tutti!
(Premetto che le mie conoscenze si limitano alle basi del C++)
Ho letto un po' in giro, ma mi restano ancora parecchi dubbi su tale argomento.In particolare:
1)Un array(anche multidimensionale) dinamico dichiarato nel seguente modo:
pare funzionare nel C++, ma avendo letto al riguardo pareri contrastanti, mi chiedevo se costituisce solo un errore di "forma" o può causare malfunzionamenti?
2)Potreste spiegarmi come funziona la dichiarazione e la modifica di un array dinamico(anche multidimensionale).
Per esempio nel caso di un programma che modifica più volte la dimensione n di un array v[n].
(Premetto che le mie conoscenze si limitano alle basi del C++)
Ho letto un po' in giro, ma mi restano ancora parecchi dubbi su tale argomento.In particolare:
1)Un array(anche multidimensionale) dinamico dichiarato nel seguente modo:
int n; cin>>n; int v[n];
pare funzionare nel C++, ma avendo letto al riguardo pareri contrastanti, mi chiedevo se costituisce solo un errore di "forma" o può causare malfunzionamenti?
2)Potreste spiegarmi come funziona la dichiarazione e la modifica di un array dinamico(anche multidimensionale).
Per esempio nel caso di un programma che modifica più volte la dimensione n di un array v[n].
Risposte
Quello non è un array dinamico ma un Variable-length array http://en.wikipedia.org/wiki/Variable-length_array. Il C, dallo standard del 99, lo presenta nel linguaggio, il C++ NO. GCC, lo accetta. I big del C++, ovvero quelli che decidono le sorti del C++, lo odiano. Quindi evitalo a meno di conoscerlo bene e MAI per array grandi. Un VLA non può modificare la sua dimensione.
Con array dinamico, sia in C che in C++, si intende qualcosa che è stato definito nell'heap (mentre i VLA sono spesso memorizzati nello stack come le variabili automatiche). Pertanto cose che sono state allocate usando malloc oppure new. Se usi malloc puoi usare realloc per cambiarne la dimensione (anche se ci sono alcune ragioni per evitare di usarlo troppo spesso).
Con array dinamico, sia in C che in C++, si intende qualcosa che è stato definito nell'heap (mentre i VLA sono spesso memorizzati nello stack come le variabili automatiche). Pertanto cose che sono state allocate usando malloc oppure new. Se usi malloc puoi usare realloc per cambiarne la dimensione (anche se ci sono alcune ragioni per evitare di usarlo troppo spesso).
Quello non è un array dinamico ma un Variable-length array http://en.wikipedia.org/wiki/Variable-length_array. Il C, dallo standard del 99, lo presenta nel linguaggio, il C++ NO. GCC, lo accetta. I big del C++, ovvero quelli che decidono le sorti del C++, lo odiano. Quindi evitalo a meno di conoscerlo bene e MAI per array grandi. Un VLA non può modificare la sua dimensione.
Per capire, sarebbe più consigliabile un VLA cui associo poi una dimensione di 50 o un vettore statico di 300 o più elementi?
Mi scuso se la mia domanda possa sembrare stupida agli occhi degli informatici, ma ignoro completamente il modo in cui un programma interagisce con la memoria.
Con array dinamico, sia in C che in C++, si intende qualcosa che è stato definito nell'heap (mentre i VLA sono spesso memorizzati nello stack come le variabili automatiche). Pertanto cose che sono state allocate usando malloc oppure new. Se usi malloc puoi usare realloc per cambiarne la dimensione (anche se ci sono alcune ragioni per evitare di usarlo troppo spesso).
Ho letto varie cose in giro riguardo queste funzioni(malloc,realloc,new,free,delete), ma non ci ho capito molto, avendo anche trovato sintassi diverse.Necessitano di qualche libreria?Meglio new o malloc?
Magari come esempio potresti scrivere una parte di codice in cui viene dichiarato un vettore dinamico la cui dimensione passi non so ...da 2 a 5?
In C++ il metodo "standard" per definire memoria dinamica è usando new. A rigore, malloc è una eredità del C.
La differenza con la memoria statica è che la memoria dinamica va liberata e la sua durata non è limitata al blocco in cui è definita. Insomma non è esattamente una cosa che ti posso spiegare in un messaggio e richiede da parte tua una attenzione superiore di quella che puoi dedicare ad un messaggio su un forum. L'uso di un libro o qualche dispensa sarebbe l'ideale. Sono argomenti trattati in qualsiasi manuale.
Insomma usando malloc avresti qualcosa del tipo (prima di 250 int e poi di 300):
In genere però si crea una nuova allocazione e si distrugge quella precedente (eventualmente copiando il contenuto vecchio). Insomma con new/delete si procede così.
La differenza con la memoria statica è che la memoria dinamica va liberata e la sua durata non è limitata al blocco in cui è definita. Insomma non è esattamente una cosa che ti posso spiegare in un messaggio e richiede da parte tua una attenzione superiore di quella che puoi dedicare ad un messaggio su un forum. L'uso di un libro o qualche dispensa sarebbe l'ideale. Sono argomenti trattati in qualsiasi manuale.
Insomma usando malloc avresti qualcosa del tipo (prima di 250 int e poi di 300):
int* array = (int *) malloc(250 * sizeof(int) ); .... .... .... array = (int *) realloc(array, 300 * sizeof(int) ); .... .... free(array);
In genere però si crea una nuova allocazione e si distrugge quella precedente (eventualmente copiando il contenuto vecchio). Insomma con new/delete si procede così.
"Super Squirrel":
Per capire, sarebbe più consigliabile un VLA cui associo poi una dimensione di 50 o un vettore statico di 300 o più elementi?
Sarebbe più consigliabile usare uno std::vector. Non è quasi mai una buona idea gestire esplicitamente la memoria con new / malloc *. Se proprio è necessario è meglio localizzare queste operazioni dietro un wrapper, ad esempio dentro una classe. Per i vettori c'è già std::vector:
int n; std::cin >> n; std::vector<int> v(n); // Ora puoi accedere da v[0] fino a v[n-1] v.resize(2 * n); // Ora puoi accedere da v[0] fino a v[2n-1]
* Uno dei motivi è che in C++ esistono le eccezioni. Il codice di esempio postato da vict85 sembra funzionare, ma in realtà soffre di un grosso problema (che di sicuro conosce ma non ha trattato per semplificare l'esempio). Se viene lanciata un'eccezione tra una malloc() / new e la corrispondente free() / delete, quest'ultima non viene chiamata, e la memoria non viene liberata. Dato che invece anche in caso di eccezione vengono sempre chiamati i distruttori di tutti gli oggetti che escono dallo scope, incapsulare la gestione della memoria in una classe e chiamare free() / delete nel suo distruttore risolve questo problema (questo idioma è chiamato RAII).
@ vict85 Capisco la difficoltà di affrontare un argomento vasto e/o complesso in un post.
Cmq grazie, mi sono fatto un'idea di come funzionano malloc/free/realloc e new/delete.
@ claudio86 Molto interessante questo std::vector. Credo potrebbe fare al mio caso. Ho letto un po' in giro e da quanto ho capito,a differenza degli array gestiti con malloc, esso provvede ad una gestione automatica degli errori tramite le eccezioni.
Per quanto riguarda la sintassi farò qualche ricerca, ma vorrei chiedere se il suo utilizzo necessita di qualche accortenza e se risulta preferibile nel caso di array di gorsse dimensioni.
P.S.
Per evitare di aprire un nuovo topic per una piccola domanda(chiedo scusa nel caso in cui non sia possibile).
Vorrei sapere se esiste qualche funzione(o opzione del compilatore) che riporta a programmino eseguito il numero di calcoli effettuati o meglio un parametro indice della complessità del programma.
Cmq grazie, mi sono fatto un'idea di come funzionano malloc/free/realloc e new/delete.
@ claudio86 Molto interessante questo std::vector. Credo potrebbe fare al mio caso. Ho letto un po' in giro e da quanto ho capito,a differenza degli array gestiti con malloc, esso provvede ad una gestione automatica degli errori tramite le eccezioni.
Per quanto riguarda la sintassi farò qualche ricerca, ma vorrei chiedere se il suo utilizzo necessita di qualche accortenza e se risulta preferibile nel caso di array di gorsse dimensioni.
P.S.
Per evitare di aprire un nuovo topic per una piccola domanda(chiedo scusa nel caso in cui non sia possibile).
Vorrei sapere se esiste qualche funzione(o opzione del compilatore) che riporta a programmino eseguito il numero di calcoli effettuati o meglio un parametro indice della complessità del programma.
"Super Squirrel":
@ claudio86 Molto interessante questo std::vector. Credo potrebbe fare al mio caso. Ho letto un po' in giro e da quanto ho capito,a differenza degli array gestiti con malloc, esso provvede ad una gestione automatica degli errori tramite le eccezioni.
Garantisce che la memoria viene liberata correttamente anche in caso di eccezioni. Inoltre è molto più comodo da usare rispetto a malloc() e compagnia: ad esempio puoi aggiungere elementi in coda con la funzione push_back() senza preoccuparti di allocare manualmente più memoria, oppure puoi accedere agli elementi con la funzione at(), che in caso di sforamento lancia un'eccezione. Infine è l'idioma del C++ per i vettori: semplicemente si usa std::vector quando serve un vettore, non c'è ragione di usare altro[nota]Tolti casi particolari, come in alcune piattaforme embedded dove non si usa proprio la memoria allocata dinamicamente. O se a un professore non piace.[/nota][nota]E spesso anche per altre collezioni "lineari". Per motivi vari (allocazioni e cache) ha spesso prestazioni migliori di una std::list, anche se si aggiungono, tolgono molti elementi.[/nota].
"Super Squirrel":
Per quanto riguarda la sintassi farò qualche ricerca, ma vorrei chiedere se il suo utilizzo necessita di qualche accortenza e se risulta preferibile nel caso di array di gorsse dimensioni.
Tutto il C++ è parecchio complesso e pieno di casi particolari / regole complicate/ eccezioni, e servono molte accortezze. Un buon libro (o più di uno) è un buon punto di partenza. Magari leggiti le domande sul C++ più votate su stackoverflow per avere qualche spunto e scoprire qualche problematica.
std::vector è un semplice wrapper intorno a new/delete, sono praticamente uguali dal punto di vista del comportamento a runtime. L'unico caso in cui potrebbe non essere la scelta migliore è quando crei/distruggi moltissimi vettori molto piccoli. new/delete hanno un certo overhead, e in questo caso si potrebbe notare. In quel caso potrebbe essere meglio usare std::array.
Comunque vale sempre la regola che prima si scrive codice idiomatico, poi se le prestazioni non sono sufficienti si trova il collo di bottiglia, e lì si ottimizza.
"Super Squirrel":
Per evitare di aprire un nuovo topic per una piccola domanda(chiedo scusa nel caso in cui non sia possibile).
Vorrei sapere se esiste qualche funzione(o opzione del compilatore) che riporta a programmino eseguito il numero di calcoli effettuati o meglio un parametro indice della complessità del programma.
Quello che cerchi si chiama profiler. Non so consigliarti molto per il C++. Qui e qui dovresti trovare qualche informazione. Magari apri un'altra discussione specifica per questo argomento.
Grazie mille, chiarissimo!