Newsgroup: it.comp.lang.c++
Titolo: Domanda sull'allocazione di memoria

Salve a tutti.
Premetto che sono alle prime armi con il c++.
Sto sviluppando una dll che interfacci un'applicazione con un server
openldap.
Sto scrivendo una funzione che deve accettare alcuni parametri, di cui due
di tipo char**, e questi li utilizzo per dare due array di stringhe di
ritorno dalla funzione.
Nella funzione io alloco la memoria con il comando NEW e tutto funziona una
meraviglia, ho monitorato con il debug le strutture di memoria ed
effettivamente vengono allocate correttamente e vengono anche riempite con i
dati giusti.
Il problema nasce quando la funzione esce e ritorna a quella chiamente, la
memoria allocata sembra non esserci piů, i puntatori alle strutture non
puntano piů alla locazione di memoria giusta ma a 0xcccccccc.
Io sto usando microsoft visual c++.
Spero di aver dato tutte le informazioni giuste.
Grazie anticipatamente a chi puň aiutarmi.
Ciao
Riccardo


Ok hai spiegato che all'interno della funzione hai allocato la memoria con
new, ma a quale puntatore l'hai assegnata la memoria allocata?
Cioe' il puntatore lo hai dichiarato locale alla funzione o e' statico o lo
hai passato a qualche parametro della funzione stessa?
Cosi' come dici, a naso, parrebbe che il puntatore e' locale alla funzione,
per cui, alla fine della funzione, lo scope delle variabili locali, e dei
parametri, cosi' come dell'eventuale puntatore locale, e' perso e non potrai
nemmeno piu' applicarvi la delete con conseguente memory leak.
Prova intanto a dichiararlo static o altrimenti posta la parte di codice che
interessa, ovvero la funzione dove avviene il fattaccio.
Maurus

 puntano piů alla locazione di memoria giusta ma a 0xcccccccc.
Osservando meglio il tuo messaggio, mi sembra di capire che nella tua
funzione tu allochi memoria per poter riempire 2 array di stringhe, i cui
puntatori vengono passati alla funzione.
Ora, una stringa e' un char* ma affinche' possa essere riempita/modificata
all'interno di una funzione, occorre che il parametro sia char** ok?
Ma tu stai parlando di un array di stringhe, per cui non basta che il
parametro della funzione sia del tipo char**, altrimenti all'uscita della
funzione avviene proprio quello che descrivi tu.
Fai la prova passandogli sempre char** ma riempiendola con una sola stringa,
invece che trattarla come array di stringhe per vedere se e' cosi'.
Maurus

 Maurus
Innanzitutto ti ringrazio della risposta.
Sicuramente sarà una banalità, ma ti ripeto sono alle prime armi con il c++.
Ti allego il codice di prova che sto usando:
#include <stdio.h>
#include <string.h>
#include <stdio.h>
int prova(char** buf);
int prova(char** buf)
{
    buf = new char * [2];
    buf[0] = new char [100];
    strcpy(buf[0],"Ric");
 return 0;
}
int main(int argc, char *argv[])
{
 char** b;
 prova(b);
 printf("Stringa: %s\n",b[0]);
 delete b;
  return 0;
}
Grazie per la pazienza.
Riccardo


Prova con questo codice, ma ti consiglio di usare le string al posto degli
array di char che come vedi possono creare confusione.
L'arcano era proprio nel fatto che tu all'interno della funzione caricavi
nel parametro char** la tua stringa. Ma affinche' la stringa venga caricata
all'indirizzo della variabile buf del main, occorre che il parametro di
prova sia anche reference.
Per lo stesso motivo per il quale tu dichiari un parametro di funzione
puntatore (o anche reference) al posto di passarlo per valore.
Per quello che nel debugger, all'interno della funzione vedevi la stringa
ok, ma poi al momento della copia del parametro il valore veniva perduto.
#include <stdio.h>
#include <string.h>
#include <stdio.h>
// qui ho messo un & (reference) in piu'
// per permettere alla funzione di cambiare la stringa
int prova(char** &buf)
{
   buf = new char * [3];
   buf[0] = new char [100];
   buf[1] = new char [100];
   buf[2] = new char [100];
   strcpy(buf[0],"Tizio");
   strcpy(buf[1],"Caio");
   strcpy(buf[2],"Sempronio");
   return 0;
}
int main(int argc, char *argv[])
{
   char** b;
   prova(b);
   printf("Stringa: %s\n",b[0]);
   printf("Stringa: %s\n",b[1]);
   printf("Stringa: %s\n",b[2]);
   delete []b[0];
   delete []b[1];
   delete []b[2];
   delete []b;
   return 0;
}
Le string, i reference ed i vector sono stati creati in C++ proprio per
evitare incasinamenti simili.
Maurus

 Nella funzione io alloco la memoria con il comando NEW
Non dovresti mai distribuire la responsabilita per la gerenza di memoria tra
una funzione ed il suo cliente. Sia che tutto si fa in un posto o nel altro.
Nel tuo caso, puoi, invece di dare un dopio puntatore a la tua funzione, sia
dare una riferenza a un std::string o ritornare un std:string, così:
void f1(std::string &s)
{
    s = "abc";
}
oppure
std::string f2()
{
    return "abc";
}
Il primo esempio è più efficace.
Se la tua funzione deve assolutamente fare allocazioni per qualsiasi raggione,
fagli accetare un allocatore:
template <typename T>
class Allocatore {
public:
    virtual T *allocate() = 0;
    virtual void deallocate(T *) = 0;
};
template <typename T>
class AllocatoreNew : public Allocatore<T> {
public:
    virtual T *allocate()
        {
            return new T;
        }
    virtual void deallocate(T *p)
        {
            delete p;
        }
};
void f(MiaClasse **pMiaClasse, Allocatore<MiaClasse> &allocatore)
{
    // bla, bla, bla
    *pMiaClasse = allocatore.allocate();
    // bla, bla, bla
}
E la chiami così:
AllocatoreNew<MiaClasse> allocatore;
// ...
MiaClasse *pMiaClasse;
f(&pMiaClasse, allocatore);
Poi, quando hai finito con l'oggetto, lo distruggi con:
allocatore.deallocate(pMiaClasse);
Perchè tutto questo traffico, mi chiedi? Perchè se tu chiamassi 'new' in un
contesto (per esempio in un DLL) e 'delete' in un altro, potresti in infatti
chiamare due librerie diverse ed incompatibili. Se tutto si fa nello stesso
contesto, sei assicurato della consistenza della memoria. In più, se un giorno
devi adoperare strategie più complesse per la gerenza di memoria, come il
pooling, sia la funzione che il cliente ne sono isolati.
Non sembrerà utile in un programmino, ma quando lavori in progietti di grande
scala, è di prima importanza di ben definire le responsabilità dei clienti e
delle librerie, e di mantenere la coesione delle interfacie.
Claudio Puviani

 }
In questo caso il resto del codice rimane uguale.
Ciao,
Nicola

 tangente...
Per la stessa ragione che se qualcuno mi chiedessi come far volare una
macchina, gli direi di scambiare la sua macchina per un aereo. Dirli che basta
farla saltare dal fianco di una montagna non sarebbe renderli servizio.
Non si distribuisce mai la responsabilità per la gerenza di memoria. È un
principio talmente elementare che ne è infantile ed ignorarlo non è altro che
far prova d'incompetenza. Allora, a che serve insegnare pratiche deplorate ed
inutilmente pericolose ad un debuttante? Tu, sicuramente sai valutarne i
pericoli, ma lui non ci vedrà che fuoco.
Claudio Puviani

 pericoli, ma lui non ci vedrà che fuoco.
No, non e' un principio elementare, anche se e' certamente di buon
senso. In base al tipo di domanda penso che menzionare queste cose
serva solo a creare confusione. Bisogna anche saper dare tempo al
tempo.
Ciao,
Nicola