Newsgroup: it.comp.lang.c++
Titolo: beginner: Array e Puntatori

Per imparare il C++ sto tentando di scrivere qualche programmino
funzionante. In particolare sto tentando di usare i puntatori, ma non
so capire quando usare gli array e quando i puntatori e soprattutto
usando i puntatori ho problemi (credo di allocazione di memoria).
Qualcuno di buona volonta' puo' aiutarmi?
Vi riporto lo schemino del mio programma (uso c++builder ver1)
Ho funzione chiamata ripetutamente ad ogni riempimento del buffer di
dati di input:
void funzioneripetuta
{
  // Qui mi calcolo senza problemi la media e la deviazione 
  // standard dei miei dati: variabili mu e sigma
 
 // Adesso devo usare la funzione "funz1" che mi deve
 // restituire due vettori X e Y
 
 double *Y;
  double *X;
  Y = new double[N];
  X = new double[N-1];
 funz1(mu,sigma,N,Y,X);
  //non so se qui devo fare
  // delete X;
  // delete Y;
}
Con funz1 che chiama altre 3 subfunzioni:
int funz1(double mu, double sigma, int N, double *Y, double *X)
{
 
   double *xl;
   double *cdf;
   double *x2l;
   xl=new double[N-1];
   cdf=new double[N-1];
   x2l=new double[N-1];
// varie Operazioni su X e Y
   subfunz1(X,N-1,mu,sigma,xl);
   subfunz2(X,N-1,mu,sigma,cdf);
   subfunz3(X,N-1,mu,sigma,x2l);
 
// Altre varie su X e Y
   delete xl;
   delete cdf;
   delete x2l;
   return 0;
}

   delete x2l;
Anche qui occorrono le [] dopo il delete.
Alex

}
Per eliminare i 2 vettori allocati devi scrivere
delete [] X;
delete [] Y;
perche' il compilatore VUOLE sapere che si tratta di vettori ed in questo
modo puo' richiamare il distruttore di ogni elemento del vettore. Cio' vale
in generale perche' in questo caso trattandosi di "double" non abbiamo
nessun distruttore.
Lo stesso discorso vale per la funzione "funz1" che qui non riporto. Per il
resto non mi sembra ci siano problemi, anzi credo che tutto ti funzionasse
comunque indipendentemente dalle "delete" non proprio corrette.
Se come dici avevi problemi di memoria (magari blocchi, ecc.) controlla bene
di non superare mai i limiti degli array che allochi. Inoltre per essere
sicuro che tutto vada bene dovresti anche controllare che le "new" vadano a
buon fine e che non restituiscano NULL.
X = new double[N];
if (X == NULL)
{
    //   ... ERRORE...
}
else
{
    //  Resto del codice
    delete [] X;
}
Tornando alla tua domanda iniziale - array o puntatore - ecco alcune
considerazioni in pillole.
1) non c'e' grossa differenza tra un puntatore od un array nell'uso comune;
2) in generale un puntatore contiene un riferimento ad una zona di memoria
allocata nei modi piu' svariati e pemette di accedervi;
3) il valore di un puntatore puo' essere cambiato per "puntare" di volta in
volta a zone di memorie diverse; l'importante e' che il contenuto della
memoria rispecchi il tipo del puntatore
Alcuni esempi:
    int x;    // variabile normale
    int *p;    // puntatore ad un intero
    p = &x;    // punta ad "x"
    *p = 3;    // modifica "x"
    p = 3;    // ERRORE anche se alcuni compilatori potrebbero accettarlo
    p[0] = 3;    // modifica "x" facendo finta che siamo in presenza di
                      // un vettore, dove il primo elemento punta ad "x"
    p[1] = 3;    // ERRORE, usciamo dalla memoria di "x"
                      // La scrittura e' corretta ed il compilatore non puo'
                      // darti errore
    p = new int[5];    // Alloca 5 interi
    p[0] = 3;    // Cambia il primo elemento del vettore
    p[1] = 3;    // Cambia il secondo elemento...
    *p = 3;      // Cambia di nuovo il primo elemento
                     // Come scrivere p[0]  =3;
    *(p+2) = 3    // Esattamente come scriver p[2] = 3
    p++;        // Incrementa il puntatore di quanto necessario
                    // per puntare al prossimo intero, quindi ora
                    // p punta al secondo elemento del vettore
    p[0] = 3;    // Cambia il secondo elemento
4) un array "classico" viene allocato non dinamicamente ed in questo caso si
puo' utilizzare come un puntatore, tranne che il suo valore (quello del
puntatore) non puo' essere variato
    int v[10];    // Alloca 10 interi
    v[1] = 3;    // ovvio
    v+2 = 3;    // come scrivere v[2] = 3;
    v++;          // ERRORE, non ammesso
    int *p;        // puntatore ad interi
    p = v;        // stesso valore di v
    p[1] = 3;    // ovvio
    p++;          // stavolta e' ammesso
5) Quando un array viene passato ad una funzione, comunque nella funzione si
riceve un puntatore
    {
        int v[10];
        funzione(v);
    }
    // ...
    void funzione(int *v);
    void funzione(int v[]);    // Comodita' di scrittura ma identica alla
                                           // precedente
6) La cosa importante con i puntatori e' che il loro tipo rispecchi quello
che si trova nella memoria che indirizzano.
    double v[10];
    int *p;
    p = v;    // puo' dare errore
    p = (int *) v;    // generalmente ammesso
    p[1] = 3;        // ovviamente ERRATO
---
Spero di non aver generato ulteriore confusione, nel qual caso sono sempre
in linea. :)
Ciao,
Angelo.

if (X == NULL)
Buon consiglio per chi usa compilatori antichi, o in "modo di
compatibilita`" col passato, ma non per chi usa Standard C++;
nel C++ Standard, infatti, qualsiasi new, se non ha successo,
NON torna comunque mai 0, bensi` solleva un'eccezione (per
avere il comportamento all'antica, bisogna usare un esplicito
e specifico overload "new(nothrow)").
Alex

Alex Martelli ha scritto nel messaggio ...

Ti ringrazio per avermi fatto notare una cosa a cui non avevo mai fatto
attenzione.
Da prove che ho fatto con il VC++ 6.0, pero', devo dire che non sono
riuscito a "sollevare" alcuna eccezione, anzi il codice su scritto funziona
perfettamente.
Ho fatto una piccola indagine su MSDN ed in effetti vengono citate varie
modalita' di funzionamento, anche a seconda del file di header incluso
("new.h", "iostream.h", ecc.). Nel breve tempo a disposizione ne ho provate
qualcuna - anche disabilitando le estensioni del compilatore - ma niente,
nessuna eccezione.
Boh...
Riassumendo, in un modo od in un altro l'importante e' verificare che le
allocazioni di memoria siano andate a buon fine.
A presto,
Angelo.

nessuna eccezione.
Che io sappia, l'unico modo, in VC++, di ottenere il
comportamento standard di new, e` "throw bad_alloc()"
(e quest'ultima exception va dichiarata, non mi pare
che lo sia negli header file) nel gestore che si istalla
con _set_new_handler (NON con set_new_handler,
che e` presente ma, a differenza di quanto lo Standard
prevede, e` dummy!).  E` una delle 3 o 4 divergenze
che restano fra VC++ e lo Standard C++ (le altre piu`
importanti: manca la covarianza dei tipi di ritorno
delle funzioni virtuali, e non vengono gestiti ancora
del tutto correttamente i template specializzati); per
fortuna, e` la divergenza piu` facile da "compensare",
visto, appunto, che con un _set_new_handler e una
routine ausiliaria si fa tutto (un poco piu` di lavoro e`
gestire anche correttamente la set_new_handler come
previsto dallo Standard, ma nulla di terribile).
Gestire gli errori gravi e rarissimi tramite eccezioni,
come previsto dallo Standard C++, semplifica molto
il codice "normale" (quello che esegue il 99%+ delle
volte, cioe` in tutti i casi non-eccezionali), migliora
potenzialmente le prestazioni (non di molto, ma tutto
aiuta:-), ed evita qualsiasi possibilita` di dimenticanza
o trascuratezza (che potrebbe essere non agevole
trovare col testing, proprio per la rarita` delle gravi
condizioni in questione).  Per questo, secondo me, e
per stare il piu` vicini possibile allo Standard, merita
il piccolo sforzo di compensare in questo caso la
deviazione di VC++ rispetto alle specifiche dello
Standard stesso.
Alex