17/08/19 21:43
AldoBaldo
Ah! Se non sto prendendo l'ennesimo abbaglio clamoroso... CE L'HO FATTA!!!
Ora la mia libreria dovrebbe funzionare con array con qualsiasi quantità di dimensioni: 1D (vettori), 2D (matrici), 3D, 4D... Le prove le ho fatte fino a 4D, e sembra che ogni cosa sia al suo posto. Visti i miei limiti, mi sono spaccato il cervello in quattro per giorni, ma ho l'impressione che finalmente dia davvero array generici e (spero) funzionanti senza sorprese.
Ecco il codice. Se qualche anima buona volesse dare un'occhiata, in particolare alla funzione AMD_imposta_ptr(), sarebbe per me rassicurante (o "devastante", dipenderebbe da cosa mi dovesse dire).
File amd.h:
File amd.c:
I test che ho fatto sono questi:
file main.c:
file test.h:
file test.c:
Ora la mia libreria dovrebbe funzionare con array con qualsiasi quantità di dimensioni: 1D (vettori), 2D (matrici), 3D, 4D... Le prove le ho fatte fino a 4D, e sembra che ogni cosa sia al suo posto. Visti i miei limiti, mi sono spaccato il cervello in quattro per giorni, ma ho l'impressione che finalmente dia davvero array generici e (spero) funzionanti senza sorprese.
Ecco il codice. Se qualche anima buona volesse dare un'occhiata, in particolare alla funzione AMD_imposta_ptr(), sarebbe per me rassicurante (o "devastante", dipenderebbe da cosa mi dovesse dire).
File amd.h:
/*============================================================================== A R R A Y M U L T I D I M E N S I O N A L E G E N E R I C O di Aldo Carpanelli - v1.0.1, 17 agosto 2019 Una libreria in C che, nelle intenzioni, dovrebbe rendere piu' agevole e spedito l'uso di matrici multidimensionali allocate dinamicamente, indipendentemente dal tipo dei dati trattati. ==============================================================================*/ #ifndef AMD_H_INCLUDED #define AMD_H_INCLUDED #ifdef __cplusplus extern "C" { #endif #include <stdlib.h> typedef struct { void *d; // puntatore allo spazio occupato in memoria da // dati+puntatori agli elementi dell'array size_t *qElXDim; // quantita' di elementi per ogni dimensione dell'array size_t totDim; // quantita' totale delle dimensioni dell'array size_t dimEl; // le dimensioni (in byte) di ogni elemento dell'array } AMD_struct; // AMD: [A]rray [M]ulti[D]imensionale typedef AMD_struct *AMD_ptr; enum { AMDErr_NoErr, AMDErr_PtrNULL, AMDErr_NoMem, AMDErr_DatiGiaPresenti, AMDErr_ZeroDimensioni, AMDErr_DimensioneZero, AMDErr_DimElZeroByte, AMDErr_StrutturaNonValida, AMDErr_StruttureNonCompatibili, AMD_MaxErr }; int AMD_Crea( AMD_ptr amd, size_t dimEl, size_t totDim, ... ); int AMD_Duplica( AMD_ptr duplicato, const AMD_ptr originale ); int AMD_Distruggi( AMD_ptr amd ); int AMD_Copia( AMD_ptr copia, const AMD_ptr originale ); size_t AMD_QuantitaElementi( const AMD_ptr amd ); size_t AMD_QuantitaPuntatori( const AMD_ptr amd ); size_t AMD_DimensioniMemoriaAllocata( const AMD_ptr amd ); size_t AMD_DimensioniSpazioPuntatori( const AMD_ptr amd ); size_t AMD_DimensioniSpazioDati( const AMD_ptr amd ); void *AMD_InizioSpazioDati( const AMD_ptr amd ); const char *AMD_DescrizioneErrore( int codice ); /*============================================================================== CAMPI DELLA STRUTTURA AMD_struct d d: [d]ati dell'array Punta allo spazio in memoria occupato dall'array; In realta', non si tratta di un semplice puntatore, bensi' di un puntatore a indirezione multipla, il livello di indirezione del quale dipende dalla quantita' delle dimensioni dell'array multidimensionale. In C, questo puntatore si puo' tranquillamente assegnare a un int*[*...], a un char*[*...], a un float*[*...] o a qualsiasi altro puntatore a doppia, tripla, quadrupla... indirezione a qualsiasi tipo. In C++ si puo' fare la stessa cosa, ma occorre procedere con un cast esplicito. Nota: oltre allo spazio per "ospitare" i dati, la memoria puntata da d ha dimensioni sufficienti a contenere "in testa" anche i puntatori che servono per rendere utilizzabile l'operatore [] con l'array multidimensionale. qElXDim qElXDim: [q]uantita' degli [El]ementi [X] per ogni [Dim]ensione dell'array multidimensionale. E' un puntatore a una serie di valori di tipo size_t, ciascuno dei quali definisce la "profondita'" della dimensione corrispondente alla sua posizione nella serie. Ad esempio, se qElXDim punta alla serie di valori 3,2,4, allora l'array e' tridimensionale, con profondita' della dimensione x pari a 4, profondita' della dimensione y pari a 2 e profondita' della dimensione z pari a 3, per un totale di 4*3*2=24 elementi nell'array. totDim totDim: [tot]ale delle [Dim]ensioni dell'array Indica la quantita' delle dimensioni dell'array, e deve corrispondere alla quantita' dei valori presenti nella serie puntata da qElXDim. Ad esempio, nel caso dell'array tridimensionale appena descritto, totDim deve essere 3. dimEl dimEl: [dim]ensioni degli [El]ementi dell'array Le dimensioni, in byte, di ciascuno degli elementi dell'array multidimensionale. Ad esempio, se nel caso dell'array tridimensionale appena descritto dimEl fosse 8 (8 byte per elemento), la dimensione complessiva dei dati dell'intero array di 4*3*2=24 elementi sarebbe di 24*8=192 byte. ==============================================================================*/ #ifdef __cplusplus } #endif #endif // AMD_H_INCLUDED
File amd.c:
/*============================================================================== A R R A Y M U L T I D I M E N S I O N A L E G E N E R I C O di Aldo Carpanelli - v1.0.1, 17 agosto 2019 Una libreria in C che, nelle intenzioni, dovrebbe rendere piu' agevole e spedito l'uso di matrici multidimensionali allocate dinamicamente, indipendentemente dal tipo dei dati trattati. ==============================================================================*/ #include <string.h> #include <stdarg.h> #include "amd.h" static const size_t AMD_SizeofPtr = sizeof(void*); static const unsigned int AMD_UIntMenoUno = (unsigned int)-1; // costanti usate in AMD_StatoAzzeramentoCampi() static const unsigned int AMD_Bit_d = 1U; // I bit static const unsigned int AMD_Bit_qElXDim = 2U; // II bit static const unsigned int AMD_Bit_totDim = 4U; // III bit static const unsigned int AMD_Bit_dimEl = 8U; // IV bit static const unsigned int AMD_NoCampiZero = 15U; // nessun campo azzerato static const unsigned int AMD_TuttiCampiZero = 0U; // tutti campi azzerati /// ===> FUNZIONI NON DISPONIBILI PER L'UTENTE <================================ /*============================================================================== Valuta la quantita' degli elementi contenuti nell'array multidimensionale, moltiplicando tra loro le dimensioni elencate nell'array dim. Non essendo consentito l'accesso "esterno" a questa funzione, non viene effettuata alcuna verifica della validita' dei parametri. ==============================================================================*/ static size_t AMD_calcola_quantita_elementi( size_t *dim, size_t totDim ) { size_t i, qEl; for( qEl=1, i=0; i<totDim; ++i ) qEl *= dim[i]; return qEl; } /*============================================================================== Valuta la quantita' dei puntatori che occorrera' impostare per permettere all'array multidimensionale di "funzionare" correttamente con gli operatori []. PARAMETRI dim punta a un array di valori di tipo size_t, ciascuno dei quali rappresenta la quantita' degli elementi presenti in una delle dimensioni di un array multidimensionale totDim la quantita' dei valori contenuti nell'array dim dimIn indice in dim della dimensione dalla quale iniziare il conteggio (NON e' zero based! l'indice alla prima dimensione e' 1, non 0!) exQp la quantita' dei puntatori rilevata valutando la dimensione precedente (la funzione e' ricorsiva) Non essendo consentito l'accesso "esterno" a questa funzione, non viene effettuata alcuna verifica della validita' dei parametri. VALORE DI RITORNO Restituisce la quantita' dei puntatori occorrenti per implementare l'array multidimensionale corrispondente ai parametri. ==============================================================================*/ static size_t AMD_calcola_quantita_puntatori( size_t *dim, size_t totDim, size_t dimIn, size_t exQp ) { if( 1==totDim ) return 0; // con una sola dimensione, non servono // calcoli, ne' puntatori "ausiliari" if( dimIn<totDim ) { size_t i, qp; for( qp=1, i=0; i<dimIn; ++i ) qp *= dim[i]; return AMD_calcola_quantita_puntatori( dim, totDim, dimIn+1, exQp+qp ); } return exQp; } /*============================================================================== Viene chiamata da AMD_imposta_ptr() e colloca, nelle ultime posizioni dello spazio riservato ai puntatori, della serie dei puntatori ai gruppi di dati che costituiscono l'ultima dimensione dell'array. La collocazione dei puntatori avviene arretrando nello spazio loro riservato, a partire dalla posizione immediatamente precedente al punto nel quale inizia lo spazio riservato ai dati. PARAMETRI pDati "pDati": puntatore ai dati puntatore alla porzione del blocco di memoria allocato dalla funzione statica AMD_crea_da_array_dimensioni() destinato a contenere i dati dell'array qBlocchi "qBlocchi": quantita' dei blocchi di dati la quantita' degli elementi dell'array contenuti in ciascuno dei gruppi di dati della sua ultima dimensione dimBlocco "dimBlocco": dimensione d'ognuno dei blocchi di dati in byte, indica la quantita' di memoria occupata dagli elementi dell'array contenuti in ciascuno dei qBlocchi blocchi di dati ==============================================================================*/ static void AMD_crea_indirezioni_blocchi_dati( void *pDati, size_t qBlocchi, size_t dimBlocco ) { void *srg = pDati + qBlocchi*dimBlocco; void *dst = pDati; size_t i; for( i=0; i<qBlocchi; ++i ) { dst -= AMD_SizeofPtr; srg -= dimBlocco; memcpy( dst, &srg, AMD_SizeofPtr ); } } /*============================================================================== Viene chiamata da AMD_imposta_ptr() e colloca, nello spazio riservato ai puntatori non ancora sfruttato da AMD_crea_indirezioni_blocchi_dati(), della serie dei puntatori a indirezione multipla occorrenti per permettere l'uso dell'array con l'operatore[]. PARAMETRI pPtrs "pPtrs": puntatore ai puntatori puntatore alla posizione dalla quale cominciare l'operazione di individuazione delle indirezioni qElXDim "qElXDim": quantita' degli elementi per dimensione la quantita' degli elementi per ciascuna dimensione dell'array, a partire da quella utile per l'operazione di indirezione in corso offset "offset": scostamento rispetto al primo puntatore indica quanti puntatori sono gia' stati collocati con la corretta indirezione PRIMA di quelli che verranno ulteriormente aggiunti iter "iter": abbreviato per "iterazioni" indica quante iterazioni devono ancora essere affrontate prima che sia completato il processo di individuazione delle indirezioni ==============================================================================*/ static void AMD_completa_indirezioni( void *pPtrs, size_t *qElXDim, size_t offset, size_t iter ) { if( iter ) { size_t o = offset*qElXDim[0]; size_t d1 = qElXDim[1]; size_t i; void *src = pPtrs + o*AMD_SizeofPtr; for( i=0; i<o; ++i ) { memcpy( pPtrs, &src, AMD_SizeofPtr ); pPtrs += AMD_SizeofPtr; src += d1*AMD_SizeofPtr; } AMD_completa_indirezioni( pPtrs, qElXDim+1, o, iter-1 ); } } /*============================================================================== Viene chiamata solo se l'array ha piu' di una dimensione (con totDim>1). Compila lo spazio riservato ai puntatori nell'ambito della memoria allocata per un array multidimensionale, cosi' da rendere successivamente possibile con esso l'impiego dell'operatore []. PARAMETRI pPtrs "pPtrs": puntatore ai puntatori puntatore al blocco di memoria allocato dalla funzione statica AMD_crea_da_array_dimensioni(); in uscita, in testa al blocco si trovera' la schiera dei puntatori necessari a rendere utilizzabile la matrice multidimensionale con gli operatori [] qPtrs "qPtrs": quantita' dei puntatori la quantita' dei puntatori che devono essere collocati in testa al blocco di memoria puntato da pPtrs qDati "qDati": quantita' dei dati la quantita' complessiva degli elementi presenti nell'intera matrice multidimensionale; equivale al prodotto di tutte le dimensioni della matrice stessa, come elencate in qElXDim dimEl "dimEl": dimensioni dell'elemento le dimensioni d'ognuno dei qDati elementi presenti nell'intera matrice multidimensionale qElXDim "qElXDim": quantita' di elementi per ogni dimensione puntatore a un array di valori di tipo size_t, ciascuno dei quali rappresenta la quantita' di elementi presenti in una determinata dimensione dell'array multidimensionale; le dimensioni sono elencate nell'array in ordine da quella piu' esterna (in posizione [0]) a quella piu' interna (in posizione [totDim-1]) totDim "totDim": quantita' totale delle dimensioni la quantita' totale delle dimensioni della matrice multidimensionale l'array di size_t puntato da qElXDim ha totDim elementi ==============================================================================*/ static void AMD_imposta_ptr( void *pPtrs, size_t qPtrs, size_t qDati, size_t dimEl, size_t *qElXDim, size_t totDim ) { void *pDati; // pDati: il punto nel quale iniziano i dati size_t dimBlocco, qBlocchi; // vedi sotto // lo spazio dei dati comincia subito // dopo lo spazio riservato ai puntatori pDati = pPtrs + qPtrs*AMD_SizeofPtr; // ogni gruppo dei dati contenuti nell'ultima // dimensione dell'array occupa dimBlocco byte dimBlocco = qElXDim[totDim-1]*dimEl; // esistono qBlocchi blocchi di dati, // ciascuno grande dimBlocco byte qBlocchi = qDati/qElXDim[totDim-1]; // per cominciare, posizioniamo i puntatori ai blocchi dei dati // nella parte terminale dello spazio riservato ai puntatori AMD_crea_indirezioni_blocchi_dati( pDati, qBlocchi, dimBlocco ); // posizioniamo le indirezioni riferite alle dimensioni rimanenti AMD_completa_indirezioni( pPtrs, qElXDim, 1, totDim-2 ); } /*============================================================================== Crea un array multidimensionale con le caratteristiche richieste tramite i parametri qElXdim, totDim e dimEl, allocando la memoria dinamica necessaria e compilando opportunamente i campi della struttura puntata dal parametro amd. La struttura puntata da amd, in ingresso deve avere almeno i campi d e qElXDim azzerati (valore NULL). Il parametro qElXDim non puo' essere NULL, ne' puo' essere 0 alcuno dei valori contenuti nell'array da esso puntato. Il parametro totDim, che non puo' essere 0, deve esplicitare la quantita' dei valori contenuti nell'array puntato da qElXDim. Il parametro totDim, che non puo' essere 0, deve esplicitare la dimensione in byte d'ogni singolo elemento dell'array da creare. In caso di successo, la funzione restituisce AMDErr_NoErr e i campi della struttura puntata dal parametro amd sono correttamente compilati. L'array definito da detta struttura dev'essere distrutto dal chiamante, tramite la funzione AMD_Distruggi(). In caso di errore, il valore di ritorno e' un codice che identifica il problema incontrato, la struttura puntata dal parametro amd non viene modificata e nessuna memoria viene allocata. ==============================================================================*/ static int AMD_crea_da_array_dimensioni( AMD_ptr amd, size_t dimEl, size_t *qElXDim, size_t totDim ) { void *dTmp; size_t *qElXDimTmp; size_t qDati; // quantita' di elementi nell'array size_t qPtrs; // quantita' di puntatori size_t memDati; // quantita' di memoria occupata dai dati (in bytes) size_t memPtrs; // quantita' di memoria occupata dai puntatori (in bytes) if( !amd ) return AMDErr_PtrNULL; // parametri NULL if( !dimEl ) return AMDErr_DimElZeroByte; // array di elementi da 0 byte? if( !totDim ) return AMDErr_ZeroDimensioni; // array "zero dimensionale"? if( amd->d || amd->qElXDim ) return AMDErr_DatiGiaPresenti; // la struttura m contiene gia' dati? if( !(qDati=AMD_calcola_quantita_elementi(qElXDim,totDim)) ) return AMDErr_DimensioneZero; // una delle dimensioni ha 0 elementi memDati = qDati*dimEl; qPtrs = AMD_calcola_quantita_puntatori( qElXDim, totDim, 1, 0 ); memPtrs = qPtrs*AMD_SizeofPtr; dTmp = malloc( memPtrs+memDati ); if( dTmp ) { qElXDimTmp = calloc( totDim, sizeof(*qElXDimTmp) ); if( qElXDimTmp ) { memset( dTmp, 0, memPtrs+memDati ); if( 1<totDim ) // solo se l'array e' multidimensionale AMD_imposta_ptr( dTmp, qPtrs, qDati, dimEl, qElXDim, totDim ); memcpy( qElXDimTmp, qElXDim, totDim*sizeof(*qElXDimTmp) ); amd->d = dTmp; amd->qElXDim = qElXDimTmp; amd->totDim = totDim; amd->dimEl = dimEl; return AMDErr_NoErr; } free( dTmp ); } return AMDErr_NoMem; // allocazione fallita } /*============================================================================== Verifica quali campi della struttura puntata da amd sono o non sono azzerati. Nel valore di ritorno, i bit (a partire dal meno significativo) sono impostati se il campo di amd corrispondente non e' azzerato, non impostati se il campo di amd corrispondente e' azzerato. Nota: se tutti i campi sono azzerati, st e' AMD_TuttiCampiZero (cioe' 0) se nessun campo e' azzerato, st e' AMD_NoCampiZero (cioe' 15) Se il puntatore amd e' NULL, il valore di ritorno e' AMD_UIntMenoUno. ==============================================================================*/ static unsigned int AMD_stato_azzeramento_campi( const AMD_ptr amd ) { if( NULL != amd ) { unsigned int stato = AMD_TuttiCampiZero; // il I bit corrisponde al campo d stato |= AMD_Bit_d * (NULL!=amd->d); // il II bit corrisponde al campo qElXDim stato |= AMD_Bit_qElXDim * (NULL!=amd->qElXDim); // il III bit corrisponde al campo totDim stato |= AMD_Bit_totDim * (0!=amd->totDim); // il IV bit corrisponde al campo dimEl stato |= AMD_Bit_dimEl * (0!=amd->dimEl); return stato; } return AMD_UIntMenoUno; // solo se amd e' NULL } /*============================================================================== Confronta le strutture puntate da s1 e s2 per verificare se definiscono due array con le stesse dimensioni. In caso le dimensioni coincidano, il valore di ritorno e' 1 (per "true", le due strutture sono compatibili); in caso contrario il valore di ritorno e' 0 (per "false", ler due strutture sono incompatibili). Il valore di ritorno e' 0 anche nel caso in cui il confronto sia impossibile (parametri NULL) o qualche campo sia azzerato. ==============================================================================*/ static int AMD_strutture_compatibili( const AMD_ptr s1, const AMD_ptr s2 ) { unsigned int st1 = AMD_stato_azzeramento_campi( s1 ); unsigned int st2 = AMD_stato_azzeramento_campi( s2 ); if( AMD_UIntMenoUno!=st1 && AMD_UIntMenoUno!=st2 ) { if( AMD_NoCampiZero==st1 && AMD_NoCampiZero==st2 ) { if( s1->totDim==s2->totDim && s1->dimEl==s2->dimEl ) { size_t i; for( i=0; i<s1->totDim; ++i ) if( s1->qElXDim[i] != s2->qElXDim[i] ) return 0; return 1; } } } return 0; } /// ===> FUNZIONI DISPONIBILI PER L'UTENTE <==================================== /*============================================================================== Sostituisce i dati dell'array definito dalla struttura puntata dal parametro "copia" con quelli dell'array definito dalla struttura puntata dal parametro "originale". In caso la copia avvenga con successo, il valore di ritorno e' AMDErr_NoErr. In caso d'errore, il valore di ritorno e' un codice che definisce il problema nel quale e' incappata la funzione. ==============================================================================*/ int AMD_Copia( AMD_ptr copia, const AMD_ptr originale ) { if( copia && originale ) { if( AMD_strutture_compatibili(copia,originale) ) { memcpy( AMD_InizioSpazioDati( copia ), AMD_InizioSpazioDati( originale ), AMD_DimensioniSpazioDati( originale ) ); return AMDErr_NoErr; } return AMDErr_StruttureNonCompatibili; } return AMDErr_PtrNULL; } /*============================================================================== Restituisce la quantita' complessiva degli elementi contenuti nell'array. Un valore di ritorno 0 indica chiaramente una condizione inaccettabile per un array valido. ==============================================================================*/ size_t AMD_QuantitaElementi( const AMD_ptr amd ) { if( AMD_NoCampiZero == AMD_stato_azzeramento_campi(amd) ) return AMD_calcola_quantita_elementi( amd->qElXDim, amd->totDim ); return 0; } /*============================================================================== Restituisce la quantita' dei puntatori usati per rendere possibile l'impiego dell'array con l'operatore []. Un valore di ritorno 0 indica chiaramente una condizione inaccettabile per un array valido. ==============================================================================*/ size_t AMD_QuantitaPuntatori( const AMD_ptr amd ) { if( AMD_NoCampiZero == AMD_stato_azzeramento_campi(amd) ) return AMD_calcola_quantita_puntatori( amd->qElXDim, amd->totDim, 1,0 ); return 0; } /*============================================================================== Restituisce la quantita' di memoria complessivamente allocata, quella il puntatore alla quale e' memorizzato nel campo amd->d. Un valore di ritorno 0 indica chiaramente una condizione inaccettabile per un array valido. ==============================================================================*/ size_t AMD_DimensioniMemoriaAllocata( const AMD_ptr amd ) { if( AMD_NoCampiZero == AMD_stato_azzeramento_campi(amd) ) return AMD_DimensioniSpazioPuntatori(amd)+AMD_DimensioniSpazioDati(amd); return 0; } /*============================================================================== Restituisce la quantita' di memoria che, nell'ambito di quella complessivamente allocata in amd->d, e' destinata all'immagazzinamento dei puntatori usati per rendere possibile l'impiego dell'array con l'operatore []. Un valore di ritorno 0 indica chiaramente una condizione inaccettabile per un array valido. ==============================================================================*/ size_t AMD_DimensioniSpazioPuntatori( const AMD_ptr amd ) { if( AMD_NoCampiZero == AMD_stato_azzeramento_campi(amd) ) { size_t qp = AMD_calcola_quantita_puntatori(amd->qElXDim,amd->totDim,1,0); return qp*AMD_SizeofPtr; } return 0; } /*============================================================================== Restituisce la quantita' di memoria che, nell'ambito di quella complessivamente allocata in amd->d, e' destinata all'immagazzinamento dei dati dell'array. Un valore di ritorno 0 indica chiaramente una condizione inaccettabile per un array valido. ==============================================================================*/ size_t AMD_DimensioniSpazioDati( const AMD_ptr amd ) { if( AMD_NoCampiZero == AMD_stato_azzeramento_campi(amd) ) return AMD_QuantitaElementi(amd) * amd->dimEl; return 0; } /*============================================================================== Restituisce un puntatore al punto ove, nell'ambito della memoria complessivamente allocata in amd->d, ha inizio lo spazio destinato all'immagazzinamento dei dati dell'array. Un valore di ritorno NULL indica chiaramente una condizione inaccettabile per un array valido. ==============================================================================*/ void *AMD_InizioSpazioDati( const AMD_ptr amd ) { if( AMD_NoCampiZero == AMD_stato_azzeramento_campi(amd) ) return amd->d + AMD_DimensioniSpazioPuntatori(amd); return NULL; } /*============================================================================== Crea un array multidimensionale con le caratteristiche richieste tramite i parametri dimEl, totDim e i parametri in quantita' variabile successivi, il valore di ciascuno dei quali indica la profondita' di una dimensione, a partire da quella piu' "esterna". Alloca la memoria dinamica necessaria e compila opportunamente i campi della struttura puntata dal parametro amd. La struttura puntata da amd, in ingresso deve avere almeno i campi d e qElXDim azzerati (valore NULL). Il parametro dimEl, che non puo' essere 0, deve esplicitare la dimensione in byte d'ogni singolo elemento dell'array da creare. Il parametro totDim, che non puo' essere 0, deve esplicitare la quantita' dei parametri successivi. Il valore di essuno dei parametri successivi a totDim puo' essere 0. In caso di successo, la funzione restituisce AMDErr_NoErr e i campi della struttura puntata dal parametro amd sono correttamente compilati. L'array definito da detta struttura dev'essere distrutto dal chiamante, tramite la funzione AMD_Distruggi(). In caso di errore, il valore di ritorno e' un codice che identifica il problema incontrato, la struttura puntata dal parametro amd non viene modificata e nessuna memoria viene allocata. ==============================================================================*/ int AMD_Crea( AMD_ptr amd, size_t dimEl, size_t totDim, ... ) { int errore = AMDErr_NoErr; size_t *qElXDimTmp = calloc( totDim, sizeof(*qElXDimTmp) ); if( qElXDimTmp ) { size_t i; va_list arg_ptr; va_start( arg_ptr, totDim ); for( i=0; i<totDim; ++i ) qElXDimTmp[i] = va_arg( arg_ptr, size_t ); va_end( arg_ptr ); errore = AMD_crea_da_array_dimensioni( amd, dimEl, qElXDimTmp, totDim ); free( qElXDimTmp ); qElXDimTmp = NULL; } return errore; // allocazione fallita } /*============================================================================== Crea un duplicato dell'array multidimensionale definito dalla struttura puntata dal parametro "originale". La creazione del duplicato avviene con una chiamata a AMD_crea_da_array_dimensioni(). I dati dell'array originale vengono copiati nel nuovo array con una chiamata a AMD_Copia(). In caso di successo, la funzione restituisce AMDErr_NoErr e la struttura puntata dal parametro "duplicato" definisce l'array duplicato appena creato. L'array definito da detta struttura dev'essere distrutto dal chiamante, tramite la funzione AMD_Distruggi(). In caso di errore, il valore di ritorno e' un codice che identifica il problema incontrato, la struttura puntata dal parametro "duplicato" non viene modificata e nessuna memoria viene allocata. ==============================================================================*/ int AMD_Duplica( AMD_ptr duplicato, const AMD_ptr originale ) { if( NULL == duplicato || NULL == originale ) return AMDErr_PtrNULL; if( AMD_NoCampiZero==AMD_stato_azzeramento_campi(originale) ) { int errore = AMD_crea_da_array_dimensioni( duplicato, originale->dimEl, originale->qElXDim, originale->totDim ); if( AMDErr_NoErr==errore ) return AMD_Copia( duplicato, originale ); return errore; } return AMDErr_StrutturaNonValida; } /*============================================================================== Distrugge l'array multidimensionale definito dalla struttura puntata dal parametro amd. La struttura puntata dal parametro amd deve derivare da una precedente chiamata a una delle due funzioni AMD_Crea() e AMD_Duplica(). In caso di successo, la funzione restituisce AMDErr_NoErr, tutti i campi della struttura puntata dal parametro amd sono azzerati e tutta la memoria dinamica connessa all'array e' deallocata. In caso di errore, il valore di ritorno e' un codice che identifica il problema incontrato, la struttura puntata dal parametro amd non viene modificata, e nessuna memoria viene deallocata. ==============================================================================*/ int AMD_Distruggi( AMD_ptr amd ) { unsigned int stato = AMD_stato_azzeramento_campi( amd ); if( AMD_UIntMenoUno != stato ) { if( AMD_NoCampiZero == stato ) { if( amd->d ) free( amd->d ); if( amd->qElXDim ) free( amd->qElXDim ); memset( amd, 0, sizeof(*amd) ); return AMDErr_NoErr; } return AMDErr_StrutturaNonValida; } return AMDErr_PtrNULL; // parametro NULL } const char *AMD_DescrizioneErrore( int codice ) { const char *kStrErr[AMD_MaxErr] = { "nessun errore", "puntatore NULL", "allocazione di memoria fallita", "la struttura pare contenere già dei dati", "impossibile creare un array a zero dimensioni", "nessuna delle dimensioni di un array può essere zero", "le dimensioni degli elementi dell'array non possono essere zero", "la struttura non è valida", "le due strutture non sono tra loro compatibili" }; if( codice >= 0 && codice < AMD_MaxErr ) return kStrErr[codice]; else return "errore imprevisto"; }
I test che ho fatto sono questi:
file main.c:
#include "test.h" #include "c_menu.h" int main() { do { // NOTA: c_menu_va() e' una funzione di una libreria che non sto a // mettere qui (l'include e' quel "c_menu.h"); comunque sia, non // fa altro che presentare un menu e assicurarsi che la scelta // effettuata sia una scelta valida. int scelta = c_menu_va("SCELTA TEST",5,"Esci","1D","2D","3D","4D"); switch( scelta ) { case 1: case 2: case 3: case 4: test( scelta ); break; default: return 0; } } while( 1 ); }
file test.h:
#ifndef TEST_H_INCLUDED #define TEST_H_INCLUDED #include <stdio.h> #include "amd.h" typedef int TipoTest; void test( size_t qDim ); #endif // TEST_H_INCLUDED
file test.c:
#include "test.h" #define QMAX_DIM 4 // in caso si cambiasse TipoTest, occorrerebbe modificare le stringhe // di formato usare dalle varie chiamate a printf(); e' comodo quindi // raggruppare qui tutte le stringhe di formato necessarie const char *kStrFormato4D = {" [%u][%u][%u][%u] = %04d\n"}; const char *kStrFormato3D = {" [%u][%u][%u] = %03d\n"}; const char *kStrFormato2D = {" [%u][%u] = %02d\n"}; const char *kStrFormato1D = {" [%u] = %01d\n"}; void chiedi_dimensioni( size_t *dim, size_t qDim ) { const char *dimStr[QMAX_DIM] = { "prima", "seconda", "terza", "quarta" }; const char id[QMAX_DIM] = { 'x', 'y', 'z', 't' }; int tmp[QMAX_DIM] = {0}; size_t i; for( i=0; i<qDim; ++i ) { int dati_letti, nl; printf( "Profondita' della %s dimensione (%c, 1-10): ", dimStr[i], id[i] ); dati_letti = scanf( "%d", tmp+i ); nl = '\n'==getchar(); if( (1!=dati_letti) || !nl || (tmp[i]<1||tmp[i]>10) ) { if( !nl ) while( '\n'!=getchar() ); --i; continue; } } for( i=0; i<QMAX_DIM; ++i ) dim[i] = tmp[i]; puts(""); } void test( size_t qDim ) { size_t dim[QMAX_DIM] = {0}; chiedi_dimensioni( dim, qDim ); switch( qDim ) { case 1: test_1d(dim); break; case 2: test_2d(dim); break; case 3: test_3d(dim); break; case 4: test_4d(dim); break; default: ; } } void mostra_a4d( const AMD_ptr amd ) { // si sarebbe potuto passare direttamente l'alias a4d dal test_4d, ma così // si hanno tutte le dimensioni "inglobate" in un'unica AMD_struct TipoTest ****a4d = amd->d; size_t c[4]; for( c[0]=0; c[0]<amd->qElXDim[0]; ++c[0] ) for( c[1]=0; c[1]<amd->qElXDim[1]; ++c[1] ) for( c[2]=0; c[2]<amd->qElXDim[2]; ++c[2] ) for( c[3]=0; c[3]<amd->qElXDim[3]; ++c[3] ) printf( kStrFormato4D, c[0],c[1],c[2],c[3], a4d[c[0]][c[1]][c[2]][c[3]] ); printf( "\n In totale, %u elementi.\n\n", AMD_QuantitaElementi(amd) ); } void test_4d( const size_t *dim ) { AMD_struct amd = {0}; // e' consigliabile azzerare sempre la struttura int errore = AMD_Crea( &amd, sizeof(TipoTest), 4, dim[3], dim[2], dim[1], dim[0] ); if( AMDErr_NoErr == errore ) { TipoTest ****a4D = amd.d; // in C++ richiede un cast size_t t, p, r, c; // scrive dei dati for( t=0; t<dim[3]; ++t ) for( p=0; p<dim[2]; ++p ) for( r=0; r<dim[1]; ++r ) for( c=0; c<dim[0]; ++c ) a4D[t][p][r][c] = t*1000+p*100+r*10+c; mostra_a4d( &amd ); errore = AMD_Distruggi( &amd ); } if( AMDErr_NoErr != errore ) printf( "Errore test 4d: %s\n\n", AMD_DescrizioneErrore(errore) ); } void mostra_a3d( const AMD_ptr amd ) { // si sarebbe potuto passare direttamente l'alias a3d dal test_3d, ma così // si hanno tutte le dimensioni "inglobate" in un'unica AMD_struct TipoTest ***a3d = amd->d; size_t c[3]; for( c[0]=0; c[0]<amd->qElXDim[0]; ++c[0] ) for( c[1]=0; c[1]<amd->qElXDim[1]; ++c[1] ) for( c[2]=0; c[2]<amd->qElXDim[2]; ++c[2] ) printf( kStrFormato3D, c[0],c[1],c[2], a3d[c[0]][c[1]][c[2]] ); printf( "\n In totale, %u elementi.\n\n", AMD_QuantitaElementi(amd) ); } void test_3d( const size_t *dim ) { AMD_struct amd = {0}; // e' consigliabile azzerare sempre la struttura int errore = AMD_Crea( &amd, sizeof(TipoTest), 3, dim[2], dim[1], dim[0] ); if( AMDErr_NoErr == errore ) { TipoTest ***a3D = amd.d; // in C++ richiede un cast size_t p, r, c; // scrive dei dati for( p=0; p<dim[2]; ++p ) for( r=0; r<dim[1]; ++r ) for( c=0; c<dim[0]; ++c ) a3D[p][r][c] = p*100+r*10+c; mostra_a3d( &amd ); errore = AMD_Distruggi( &amd ); } if( AMDErr_NoErr != errore ) printf( "Errore test 3d: %s\n\n", AMD_DescrizioneErrore(errore) ); } void mostra_a2d( const AMD_ptr amd ) { // si sarebbe potuto passare direttamente l'alias a2d dal test_2d, ma così // si hanno tutte le dimensioni "inglobate" in un'unica AMD_struct TipoTest **a2d = amd->d; size_t c[2]; for( c[0]=0; c[0]<amd->qElXDim[0]; ++c[0] ) for( c[1]=0; c[1]<amd->qElXDim[1]; ++c[1] ) printf( kStrFormato2D, c[0],c[1], a2d[c[0]][c[1]] ); printf( "\n In totale, %u elementi.\n\n", AMD_QuantitaElementi(amd) ); } void test_2d( const size_t *dim ) { AMD_struct amd = {0}; // e' consigliabile azzerare sempre la struttura int errore = AMD_Crea( &amd, sizeof(TipoTest), 2, dim[1], dim[0] ); if( AMDErr_NoErr == errore ) { TipoTest **a2D = amd.d; // in C++ richiede un cast size_t r, c; // scrive dei dati for( r=0; r<dim[1]; ++r ) for( c=0; c<dim[0]; ++c ) a2D[r][c] = r*10+c; mostra_a2d( &amd ); errore = AMD_Distruggi( &amd ); } if( AMDErr_NoErr != errore ) printf( "Errore test 2d: %s\n\n", AMD_DescrizioneErrore(errore) ); } void mostra_a1d( const AMD_ptr amd ) { // si sarebbe potuto passare direttamente l'alias a1d dal test_1d, ma così // si hanno tutte le dimensioni "inglobate" in un'unica AMD_struct TipoTest *a1d = amd->d; size_t c[1]; for( c[0]=0; c[0]<amd->qElXDim[0]; ++c[0] ) printf( kStrFormato1D, c[0], a1d[c[0]] ); printf( "\n In totale, %u elementi.\n\n", AMD_QuantitaElementi(amd) ); } void test_1d( const size_t *dim ) { AMD_struct amd = {0}; // e' consigliabile azzerare sempre la struttura int errore = AMD_Crea( &amd, sizeof(TipoTest), 1, dim[0] ); if( AMDErr_NoErr == errore ) { TipoTest *a1D = amd.d; // in C++ richiede un cast size_t x; // scrive dei dati for( x=0; x<dim[0]; ++x ) a1D[x] = x; mostra_a1d( &amd ); errore = AMD_Distruggi( &amd ); } if( AMDErr_NoErr != errore ) printf( "Errore test 1d: %s\n\n", AMD_DescrizioneErrore(errore) ); }
Ultima modifica effettuata da AldoBaldo 17/08/19 21:47
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.