Newsgroup: it.comp.lang.c
Titolo: liberare memoria di una matrice

Ciao a tutti,
ho un piccolo problema, devo liberare la memoria, allocata attraverso
una malloc, di una matrice
dichiarazione matrice:
int **marked = (int **)malloc(height * sizeof(int *));
for(i = 0; i < height; i++)
   marked[i] = (int *)malloc(width * sizeof(int));
per liberarla faccio:
for(i = 0; i < height; i++)
   free(marked[i]);
free(marked);
solamente che il programma crasha? qualcuno sa aiutarmi?

gabriele ha scritto:

Concettualmente mi sembra corretto.
Nel codice vero e proprio, controlli che tutte le malloc abbiano avuto 
successo?
Remo.D

solamente che il programma crasha? qualcuno sa aiutarmi?
In che punto crasha?


Per caso height viene modificato tra la malloc e le free?

 solamente che il programma crasha? qualcuno sa aiutarmi?
gdb
Ciao Jack
-- 
Eroi non si nasce, ti incastrano
- Jim Belushi

gabriele wrote:

Ho provato il codice e sembra funzionare, secondo me
tra la malloc e la free sei andato a scrivere in questa matrice
sovrascrivendo qualche puntatore e al momento del free va in crash
-- 
Una verità detta con cattiva intenzione batte tutte le bugie
che potreste inventare.
                                             - W. Blake -

 gdb
Espando, per chi non e' familiare con il concetto.
Oltre che gli errori di compilazione, si possono facilmente
rintracciare gli errori di esecuzione. In Linux basta compilare con
l'opzione -g di gcc e poi lanciare il programma con
gdb nomeprogramma
run
--
enos76

height rimane sempre nella matrice scrivo facendo marked[i][j] = val,
dove val è 1 o 0 non credo che cio modifichi puntatori o altro..

   
Forse il problema sta' altrove: sei sicuro di NON sfondare
la tua matrice andando a scrivere OLTRE i tuoi elementi?
Le sritture a rampazzo possono causare questo e altri
problemi.
Se invece sei sicuro di non abusare delle memoria, e 
se col debugger *proprio* non riesci a risolvere, applica
un approccio difensivo del tipo:
if((0==height)||(0==width ))
{
    printf("\nOpps...\n ");
    exit(EXIT_FAILURE);
}
if(NULL == (marked = (int **)malloc(height * sizeof(int *)))
{
      // Non c'e' memoria!!!
      printf("\nChe sistema del tubo!\n ");
      exit(EXIT_FAILURE);
}
for(i = 0; i < height; i++)
{
   if(NULL == (marked[i] = (int *)malloc(width * sizeof(int))))
   {
      // Non c'e' memoria!!!
      printf("malloc: NULL ptr a %u!\n ", i);
      exit(EXIT_FAILURE);
   }
}
//---------------------------
for(i = 0; i < height; i++)
{
   if(NULL == marked[i])   
   {
      // Non c'e' memoria!!!
      printf("free: NULL ptr a %u!\n ", i);
      continue;
   }
   free(marked[i]);
   marked[i]=NULL;
}
free(marked);
Se il sistema va in crash ancora, tornerei alla prima
ipotesi.
B.

 }
 > [...]
Suggerisco di usare <assert.h> per questo controllo e i successivi:
assert(height>0 && width>0);
mi pare molto più chiaro, ed efficente.
E.

 mi pare molto più chiaro, ed efficente.
Che sia piu' comoda , magari per eliminarla col |NDEBUG| , puo' darsi.
che sia *molto* piu' chiara ed efficente non direi proprio ;-)
B.

 dove val è 1 o 0 non credo che cio modifichi puntatori o altro..
e scommetto che i e j vanno da 0 a height e da 0 a width
attento perchè quello che hai creato tu non è una matrice normale anzi
non è proprio una matrice, è un vettore di puntatori ad altri vettori
quando i e j valgono 0 entrambi sovrascrivi il primo puntatore contenuto in marked
devi caricare il puntatore che ti serve tramite i e usarlo per accedere al vettore tramite j
una matrice normale ha tutti gli elementi contigui, per crearla con 
malloc dovevi fare marked=malloc(height*width*sizeof(int))
poi potevi accedervi con marked[i][j]
spero di aver capito il problema, altrimenti scusami
ciao
-- 
Prudentemente Iddio nasconde tra le tenebre caliginose
gli eventi del tempo futuro.
                                             - Orazio -

che sia *molto* piu' chiara ed efficente non direi proprio ;-)
Sě, č molto piů chiara. Ed č inoltre la via standard per fare questo
tipo di controlli sulle pre/post-condizioni.

if(NULL == (marked = (int **)malloc(height * sizeof(int *)))
Meglio spezzare le istruzioni su più righe e meglio (come al solito)
non usare i cast sul valore di ritorno di malloc().

 poi potevi accedervi con marked[i][j]
Non e' esatto: la tua malloc sembra presupporre che marked
sia dichiarato come int *marked, ma poi per accedere non
puoi usare i due indici, ma devi usare marked[i*width+j]
ciao
Giacomo

   
assert e' una funzione standard, che *puo* essere utilizzata *se*
il programmatore decide di avvalersene: non e' la via standard per
fare controlli pre/post: non e' scritto da nessuna parte nello standard
*come* fare cio', perche' ci possono essere molti motivi per evitare
di terminare brutalmente, o per fare una diagnostica diversa da quella
predefinita da assert.
Se vuoi dire che assert e' una maniera per aggiungere delle trap di
debug, in modo standard, sono d'accordo, ma in questo caso non
c'e' bisogno di fare i religiosi ;-)
B.

 [...]
Come fa il compilatore a sapere di che dimensione sono i componenti
dell'array? Usa calloc.
--
enos76

 Giacomo
ciao
-- 
La maggior parte delle volte si ricerca la felicità come
si ricercano gli occhiali quando si hanno sul naso.
                                             - G. Droz -

 Non credo proprio! questione di opinioni comunque.
Di certo è questione di gusti. Però ci sono buone motivazioni per usare 
gli assert.
Guardando il codice che hai scritto, la prima motivazione è che con gli 
assert ti eviti di scrivere un sacco di righe. Più che risparmiarti le 
falangi il vantaggio è che il codice risulta molto più leggibile. 
Infatti quei controlli sono un fatto secondario, e devono distogliere il 
meno possibile dalle istruzioni che rappresentano veramente l'algoritmo 
che si sta implementando.
In seconda istanza è molto utile gestire questo tipo di controlli in 
modo coerente (eventualmente te la puoi definire tu, una macro al posto 
di assert). Questo ti permette di modificare in modo coerente il codice 
di tutti i controlli. Per esempio li potresti disabilitare, oppure 
mandare su stderr, oppure scriverli con un formato diverso, etc...
Come terzo direi che è utile usare una funzione standard, quando questa 
esiste e fa esattamente quello che ti serve. Questo rende il tuo codice 
più leggibile da terzi. Inoltre la funzione della libreria standard 
potrebbe sfruttare delle peculiarità del compilatore. Ad esempio il 
compilatore potrebbe sfruttare la tua asserzione per ottimizzare il 
codice compilato. Nel caso specifico (se ricordo bene!) la funzione 
assert scrive il numero di riga in cui si è verificato l'errore. Questa 
informazione è, ad esempio, più utile del tuo messaggio "Oops".
E.

eno...@gmail.com
 ha scritto:

sizeof(int) è la dimensione del componente. Io userei calloc solamente 
se mi serve che la memoria allocata sia azzerata.
E.

 se mi serve che la memoria allocata sia azzerata.
Intendevo: come fa poi il compilatore, quando si usa array[N], a
sapere di quanto fare i salti di N, se non si e' assegnato un tipo
all'array?
--
enos76

eno...@gmail.com
 ha scritto:

In base al tipo dichiarato per l'array. Considera che in C non esiste il 
concetto di matrice, ma solo quello di vettore. Dunque non si pone il 
problema di sapere quanto sono lunghe le righe.
E.

il programmatore decide di avvalersene
Come tutto ciò che riguarda la programmazione. Vedi di non fare tu il
religioso all'inverso. Come già detto assert() è la via standard per
fare controlli pre/post nel senso che è la strada normalmente
utilizzata/consigliata, tanto è vero che i sistemi di sviluppo sono
progettati per tenerne conto. Se vuoi fare una diagnostica diversa da
quella di assert() allora stai parlando di cose diverse. Se vuoi fare
una diagnostica come quella fattibile con assert(), allora usi
assert(). Punto.

Di certo č questione di gusti.
Non solo, č questione di metodologia di sviluppo.

dell'array? Usa calloc.
calloc() non è distinta da malloc(). E' solo una malloc() con un
azzeramento della memoria.

 
mi rispondo da solo per dire che quello che ho scritto č sbagliato,
ho provato il codice cosě come lo ha fatto "gabriele" e funziona, deve esserci un
errore da qualche altra parte.
scusate!
#include <stdio.h>
#include <stdlib.h>
main () {
int height=11, width=11, i, j;
int *p;
int **marked = (int **)malloc(height * sizeof(int *));
for(i = 0; i < height; i++)
   marked[i] = (int *)malloc(width * sizeof(int));
printf ("allocata\n");
for (i=0; i< height; i++)
  for (j=0; j< width; j++)
    marked[i][j]=i*j;
for (i=0; i< height; i++) {
  for (j=0; j< width; j++)
    printf ("%d ", marked[i][j]);
  printf ("\n");
}
printf ("scrittura diretta\n");
marked[2][1]=12;
marked[3][4]=25;
p = marked[2];
printf ("lettura indiretta %d\n", p[1]);
p = marked[3];
printf ("lettura indiretta %d\n", p[4]);
for(i = 0; i < height; i++)
   free(marked[i]);
free(marked);
printf ("free\n");
}
-- 
Nulla di piů raro al mondo che una persona abitualmente sopportabile.
                                             - G. Leopardi -

 problema di sapere quanto sono lunghe le righe.
Capito. Grazie molte della spiegazione.
Chiedo scusa per l'osservazione sbagliata.
--
enos76


Quoto tutto, ma mi sembra non hai colto, in questo caso, il punto della 
discussione ;-)
Il codice che ho scritto era solo per puntualizzare e visualizzare un 
approccio
difensivo, e' venuto cosi', inutile discutere su altro.
Se la nota fosse stata "si puo' utilizzare l' assert perche'...", ma 
affermare che
e' *molto* *meglio* e *molto* piu' chiaro usare assert non mi sembrava 
indicato
e non centrava il punto della situazione: in questo caso era solo un 
dettaglio
visto che il punto era focalizzato sui check difensivi *non* certo sull' 
algoritmo(!)
meglio evidenzire, no?
-----------
L' assert comunque non e' una funzione di libreria ma una *macro* definita
in <assert.h> e stampa semplicemente il nome file e riga del codice:
per quanto sia utile per *debug* e' ben poco ultile ad altri che al 
possessore
del codice ed infatti *non* e' quasi mai usata per altri motivi se non 
debug e
solo come trap, in quanto termina l'esecuzione brutalmente (c'e' chi ha 
buoni motivi
per evitarlo) poi e' quasi sempre rimossa via macro o semplicemente 
eliminata dal
codice.
personalmente preferisco evitare di lasciare nel codice trap di assert 
che causano
terminazioni brutali, ma anche qui, questioni di mertodo e gusti.
L' assert e' utile per il controllo di condizioni "statiche" che 
*presumibilmente*
nel codice di release non si verificheranno *mai*, non certo per controlli
a run time: questo per me sarebbe *cattivo* codice.
In generale, e' questione di metodologia (con buona pace di altri), e io
preferisco gestire le ecccezioni altrimenti.
L' "opps" puo' piu' utilmente essere sostituito da un bel messaggio 
molto piu'
esplicativo di numero riga e file, ma ancora, valeva solo per dimostrare
il punto.
B.

Andrea Laforgia ha scritto:

non hai capito il punto. punto.

 a run time: questo per me sarebbe *cattivo* codice.
Certo. Ed è quindi indicato per la ricerca dei bug. Questo è il caso in 
questione.
E.


 -g di gcc
meglio -ggdb 
       -ggdb
           Produce debugging information for use by GDB.  This means to
           use the most expressive format available (DWARF 2, stabs, or
           the native format if neither of those are supported),
           including GDB extensions if at all possible.
Ovvero, ti scrive le migliori informazioni di debug disponibili sulla
tua piattaforma, mentre -g usa solo il formato nativo del tuo OS. 
-- 
-riko

 non hai capito il punto. punto.
con manu (penso) siamo in tre a non avere capito il punto.
Tu vuoi convincerci che una soluzione ad hoc che pubblica meno
informazioni, è semanticamente più debole *e* è pure più inefficiente
sia migliore.
Devi fare *molto* meglio per convincermi del contrario.
-- 
-riko

 codice finale? Non è una "trap"? Non termina brutalmente?
Errore mio, l' intenzione era diversa.
B.