30/07/17 10:30
AldoBaldo
E rieccomi con uno dei miei "esperimenti" per reinventare la ruota...
L'idea, questa volta, è avere a disposizione delle funzioni "stand alone" per allocare e deallocare in memoria dinamica vettori e matrici di qualsiasi tipo con il minor sforzo possibile (una riga di codice, una verifica). Dopo un po' di lavorio mentale (ho i miei limiti) sono arrivato a "sintetizzare" quattro funzioncine, due per creare/distruggere vettori e due per creare/distruggere matrici. Mentre le funzioni che riguardano i vettori non hanno bisogno nessun supporto esterno, quelle che riguardano le matrici s'appoggiano alle funzioni che riguardano i vettori.
Ve le propongo perché mi piacerebbe sapere se ci vedete qualche problema, soprattutto per quel che riguarda eventuali puntatori impazziti o memoria non deallocata a dovere. Nel frattempo continuo con i test, che finora ho completato solo in minima parte.
Edit: noto ora che per qualche ragione la formattazione del codice viene "disturbata" dalla presenza degli apostrofi all'interno dei commenti; per aggirare la questione metto i commenti DOPO il codice, sperando che funzioni.
NOTA su stdint.h e uintptr_t
Le funzioni fanno uso del tipo uintptr_t, definito nel file di include stdint.h. La descrizione di uintptr_t che viene data in quel file è: "Integer type capable of holding object pointers".
Commento su distruggi_vettore()
Dato un vettore di puntatori generici che puntano a memoria allocata dinamicamente con malloc, calloc o realloc, la funzione libera la memoria puntata dai singoli puntatori elementi del vettore e quella allocata dall'array di puntatori stesso.
Ne consegue che, in uscita, TUTTA la memoria dinamica risulterà liberata e il puntatore passato come parametro in "vettore" sarà un puntatore non valido.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il puntatore passato nel parametro "vettore" sia indicato come void* per evitare la necessità di un cast esplicito, ci si attende che il parametro sia un puntatore a doppia indirezione (ad esempio, char** o int**).
Commento su crea_vettore()
Crea un vettore di puntatori generici e per ciascun elemento di quel vettore alloca con calloc uno spazio di memoria dinamica pari a dimEl byte.
Ne consegue che, in uscita, sarà allocata una memoria complessiva pari a qEl moltiplicato per la dimensione di un puntatore (nei sistemi a 32 bit, 4 byte) PIU' dimEl*qEl byte.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il valore di ritorno sia indicato come void* per evitare la necessità di un cast esplicito, si tenga presente che la sua reale natura è quella di un puntatore a doppia indirezione, per cui deve essere assegnato a una variabile (ad esempio) di tipo char** o int**.
Commento su distruggi_matrice()
Data una matrice bidimensionale di puntatori generici che puntano a memoria allocata dinamicamente con malloc, calloc o realloc, la funzione libera la memoria puntata dai singoli puntatori elementi della matrice e quella allocata dalla matrice di puntatori stessa.
Ne consegue che, in uscita, TUTTA la memoria dinamica risulterà liberata e il puntatore passato come parametro in "matrice" sarà un puntatore non valido.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il puntatore passato nel parametro "matrice" sia indicato come void* per evitare la necessità di un cast esplicito, ci si attende che il parametro sia un puntatore a tripla indirezione (ad esempio, char*** o int***).
Commento su crea_matrice()
Crea una matrice di puntatori generici e per ciascun elemento di quella matrice alloca con calloc uno spazio di memoria dinamica pari a dimEl byte.
Ne consegue che, in uscita, sarà allocata una memoria complessiva pari a qEl moltiplicato per la dimensione di un puntatore (nei sistemi a 32 bit, 4 byte)
PIU' dimEl*qRighe*qColonne byte.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il valore di ritorno sia indicato come void* per evitare la necessità di un cast esplicito, si tenga presente che la sua reale natura è quella di un puntatore a tripla indirezione, per cui deve essere assegnato a una variabile (ad esempio) di tipo char*** o int***.
L'idea, questa volta, è avere a disposizione delle funzioni "stand alone" per allocare e deallocare in memoria dinamica vettori e matrici di qualsiasi tipo con il minor sforzo possibile (una riga di codice, una verifica). Dopo un po' di lavorio mentale (ho i miei limiti) sono arrivato a "sintetizzare" quattro funzioncine, due per creare/distruggere vettori e due per creare/distruggere matrici. Mentre le funzioni che riguardano i vettori non hanno bisogno nessun supporto esterno, quelle che riguardano le matrici s'appoggiano alle funzioni che riguardano i vettori.
Ve le propongo perché mi piacerebbe sapere se ci vedete qualche problema, soprattutto per quel che riguarda eventuali puntatori impazziti o memoria non deallocata a dovere. Nel frattempo continuo con i test, che finora ho completato solo in minima parte.
Edit: noto ora che per qualche ragione la formattazione del codice viene "disturbata" dalla presenza degli apostrofi all'interno dei commenti; per aggirare la questione metto i commenti DOPO il codice, sperando che funzioni.
#include <stdio.h> #include <stdlib.h> #include <stdint.h> /* vedi nota */ /*===> distruggi_vettore() <==================================================*/ int distruggi_vettore( void *vettore, size_t qEl ) { if( vettore && qEl ) { uintptr_t *v = vettore; /* puntatore ausiliario */ size_t e; /* contatore per gli elementi del vettore */ for( e=0; e<qEl; ++e ) free( (void*)v[e] ); free( vettore ); return 1; /* 1 = tutto bene */ } return 0; /* 0 = errore */ } /*===> crea_vettore() <=======================================================*/ void *crea_vettore( size_t qEl, size_t dimEl ) { uintptr_t *v = NULL; /* v = vettore */ if( qEl && dimEl ) { v = calloc( qEl, sizeof(uintptr_t) ); if( v ) { size_t e; /* contatore per gli elementi del vettore*/ for( e=0; e<qEl; ++e ) { v[e] = (uintptr_t) calloc( 1, dimEl ); if( !v[e] ) break; } if( e < qEl ) { /* allocazione non completata */ distruggi_vettore( v, e ); v = NULL; } } } return v; } /*===> distruggi_matrice() <==================================================*/ int distruggi_matrice( void *matrice, size_t qRighe, size_t qColonne ) { if( matrice && qRighe && qColonne ) { uintptr_t **m = matrice; /* puntatore ausiliario */ size_t r; /* contatore per le righe della matrice */ for( r=0; r<qRighe; ++r ) distruggi_vettore( m[r], qColonne ); free( m ); return 1; /* 1 = tutto bene */ } return 0; /* 0 = errore */ } /*===> crea_matrice() <=======================================================*/ void *crea_matrice( size_t qRighe, size_t qColonne, size_t dimEl ) { uintptr_t **m = NULL; /* m = matrice */ if( qRighe && qColonne && dimEl ) { m = calloc( qRighe, sizeof(uintptr_t) ); if( m ) { size_t r; /* contatore delle righe della matrice */ for( r=0; r<qRighe; ++r ) { m[r] = crea_vettore( qColonne, dimEl ); if( !m[r] ) break; } if( r < qRighe ) { /* allocazione non completata */ for( --r; r<((size_t)-1); --r ) distruggi_vettore( m[r], qColonne ); free( m ); m = NULL; } } } return m; } /*============================================================================== ESEMPI DI USO ==============================================================================*/ void esempio_vettore( void ) { const int qe = 10; int **vettore = NULL; // doppia indirezione! (qualsiasi tipo) int e; vettore = crea_vettore( qe, sizeof(**vettore) ); if( vettore ) { /* "popola" il vettore con valori crescenti */ for( e=0; e<qe; ++e ) *vettore[e] = e+1; /* mostra in console il contenuto del vettore */ printf( "\nVETTORE\n" ); for( e=0; e<qe; ++e ) printf( "%d%c", *vettore[e], e<qe-1?' ':'\n' ); /* libera la memoria allocata */ if( !distruggi_vettore(vettore,qe) ) printf( "\nVettore non distrutto.\n" ); } else { printf( "\nVettore non creato.\n" ); } } void esempio_matrice( void ) { const int qr = 5, qc = 5; /* qr = quantita righe; qc = quantita colonne */ int ***matrice = NULL; // tripla indirezione! (qualsiasi tipo) int r, c; /* contatori per le righe e per le colonne */ matrice = crea_matrice( qr, qc, sizeof(***matrice) ); if( matrice ) { /* "popola" la matrice con valori dei quali la prima cifra rappresenta la riga e la seconda cifra la colonna */ for( r=0; r<qr; ++r ) for( c=0; c<qc; ++c ) *matrice[r][c] = (r+1)*10 + c+1; /* mostra in console il contenuto della matrice */ printf( "\nMATRICE\n" ); for( r=0; r<qr; ++r ) for( c=0; c<qc; ++c ) printf( "%d%c", *matrice[r][c], c<qc-1?' ':'\n' ); /* libera la memoria allocata */ if( !distruggi_matrice(matrice,qr,qc) ) printf( "\nMatrice non distrutta." ); } else { printf( "\nMatrice non creata.\n" ); } } int main() { esempio_vettore(); esempio_matrice(); printf( "\nPremi \"invio\" per lasciare il programma...\n\n" ); while( getchar() != '\n' ); return 0; }
NOTA su stdint.h e uintptr_t
Le funzioni fanno uso del tipo uintptr_t, definito nel file di include stdint.h. La descrizione di uintptr_t che viene data in quel file è: "Integer type capable of holding object pointers".
Commento su distruggi_vettore()
Dato un vettore di puntatori generici che puntano a memoria allocata dinamicamente con malloc, calloc o realloc, la funzione libera la memoria puntata dai singoli puntatori elementi del vettore e quella allocata dall'array di puntatori stesso.
Ne consegue che, in uscita, TUTTA la memoria dinamica risulterà liberata e il puntatore passato come parametro in "vettore" sarà un puntatore non valido.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il puntatore passato nel parametro "vettore" sia indicato come void* per evitare la necessità di un cast esplicito, ci si attende che il parametro sia un puntatore a doppia indirezione (ad esempio, char** o int**).
Commento su crea_vettore()
Crea un vettore di puntatori generici e per ciascun elemento di quel vettore alloca con calloc uno spazio di memoria dinamica pari a dimEl byte.
Ne consegue che, in uscita, sarà allocata una memoria complessiva pari a qEl moltiplicato per la dimensione di un puntatore (nei sistemi a 32 bit, 4 byte) PIU' dimEl*qEl byte.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il valore di ritorno sia indicato come void* per evitare la necessità di un cast esplicito, si tenga presente che la sua reale natura è quella di un puntatore a doppia indirezione, per cui deve essere assegnato a una variabile (ad esempio) di tipo char** o int**.
Commento su distruggi_matrice()
Data una matrice bidimensionale di puntatori generici che puntano a memoria allocata dinamicamente con malloc, calloc o realloc, la funzione libera la memoria puntata dai singoli puntatori elementi della matrice e quella allocata dalla matrice di puntatori stessa.
Ne consegue che, in uscita, TUTTA la memoria dinamica risulterà liberata e il puntatore passato come parametro in "matrice" sarà un puntatore non valido.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il puntatore passato nel parametro "matrice" sia indicato come void* per evitare la necessità di un cast esplicito, ci si attende che il parametro sia un puntatore a tripla indirezione (ad esempio, char*** o int***).
Commento su crea_matrice()
Crea una matrice di puntatori generici e per ciascun elemento di quella matrice alloca con calloc uno spazio di memoria dinamica pari a dimEl byte.
Ne consegue che, in uscita, sarà allocata una memoria complessiva pari a qEl moltiplicato per la dimensione di un puntatore (nei sistemi a 32 bit, 4 byte)
PIU' dimEl*qRighe*qColonne byte.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il valore di ritorno sia indicato come void* per evitare la necessità di un cast esplicito, si tenga presente che la sua reale natura è quella di un puntatore a tripla indirezione, per cui deve essere assegnato a una variabile (ad esempio) di tipo char*** o int***.
Ultima modifica effettuata da AldoBaldo 30/07/17 11:31
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.