10/08/19 22:04
AldoBaldo
Galvanizzato dall'entusiasmo travolgente che ha generato il mio ultimo thread (quello sull'ipotesi di un insieme di funzioni per gestire matrici generiche in C), in questi giorni sto buttando giù una "estensione" del concetto, con funzioni per gestire array multidimensionali generici di dimensioni qualsiasi e con una quantità arbitraria di dimensioni.
Una cosa che ho voluto evitare, è l'allocazione di una miriade di minuscoli blocchi di memoria (se non ho capito male, la frammentazione della memoria che potrebbe derivarne non è una buonca cosa). Con le funzioni che seguono, la matrice multidimensionale viene sempre creata in UN UNICO blocco di memoria, allocato con UN'UNICA chiamata a calloc(). I puntatori sono "inglobati" in testa al blocco, e puntano alle posizioni più opportune, tutte nell'ambito dello stesso blocco. Sarà anche un meccanismo banale, magari ho solo reinventato la ruota convinto di avere avuto un'idea originale, però risolvere questo "rebus" mi è costato parecchio in termini di tempo e di sforzo. Spero di non avere commesso errori (dai test non ne ho rilevati).
Mentre la volta scorsa vi ho proposto un codice finito, questa volta è un "lavoro in corso", per cui ci sono ancora rifiniture da mettere a punto, come la definizione dei codici e dei messaggi di errore, il controllo dei parametri è così via. Il codice è comunque funzionante.
Graditi commenti e/o suggerimenti.
File amd.h
File amd.c
Una cosa che ho voluto evitare, è l'allocazione di una miriade di minuscoli blocchi di memoria (se non ho capito male, la frammentazione della memoria che potrebbe derivarne non è una buonca cosa). Con le funzioni che seguono, la matrice multidimensionale viene sempre creata in UN UNICO blocco di memoria, allocato con UN'UNICA chiamata a calloc(). I puntatori sono "inglobati" in testa al blocco, e puntano alle posizioni più opportune, tutte nell'ambito dello stesso blocco. Sarà anche un meccanismo banale, magari ho solo reinventato la ruota convinto di avere avuto un'idea originale, però risolvere questo "rebus" mi è costato parecchio in termini di tempo e di sforzo. Spero di non avere commesso errori (dai test non ne ho rilevati).
Mentre la volta scorsa vi ho proposto un codice finito, questa volta è un "lavoro in corso", per cui ci sono ancora rifiniture da mettere a punto, come la definizione dei codici e dei messaggi di errore, il controllo dei parametri è così via. Il codice è comunque funzionante.
Graditi commenti e/o suggerimenti.
File amd.h
#ifndef MATRICE_H_INCLUDED #define MATRICE_H_INCLUDED #include <stdio.h> #include <stdlib.h> #include <string.h> static const size_t kSizeofPtr = sizeof(void*); typedef struct { void *d; // puntatore allo spazio occupato in memoria da dati+puntatori 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; // AMD: Array Multi Dimensionale int AMD_Crea( AMD *m, size_t *qElXDim, size_t totDim, size_t dEl ); int AMD_Distruggi( AMD *m ); size_t AMD_QuantitaElementi( AMD *m ); size_t AMD_QuantitaPuntatori( AMD *m ); size_t AMD_DimensioniMemoriaAllocata( AMD *m ); size_t AMD_DimensioniSpazioPuntatori( AMD *m ); size_t AMD_DimensioniSpazioDati( AMD *m ); void *AMD_InizioSpazioDati( AMD *m ); #endif // MATRICE_H_INCLUDED
File amd.c
#include "amd.h" /*============================================================================== Viene chiamata solo se l'array ha piu' di una dimensione (con totDim>1). PARAMETRI pPtrs "pPtrs": puntatore ai puntatori puntatore al blocco di memoria allocato da AMD_Crea(); 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 *dst, *srg, *pDati; // dst: destinazione; srg: sorgente size_t dimBloccoDati, qBlocchiDati; // vedi sotto size_t i, j; // contatori // lo spazio dei dati comincia subito // dopo lo spazio riservato ai puntatori pDati = pPtrs + qPtrs*kSizeofPtr; // ogni gruppo dei dati contenuti nell'ultima // dimensione dell'array occupa dimBloccoDati byte dimBloccoDati = qElXDim[totDim-1]*dimEl; // esistono qBlocchiDati blocchi di dati, // ciascuno grande dimBloccoDati byte qBlocchiDati = qDati/qElXDim[totDim-1]; // dst punti appena oltre lo spazio destinato ai puntatori dst = pPtrs + qPtrs*kSizeofPtr; // srg punti appena oltre lo spazio destinato ai dati srg = pDati + qBlocchiDati*dimBloccoDati; // arretra dst di kSizeofPtr byte alla volta lungo lo spazio riservato ai // puntatori, arretrando parallelamente srg di dimBloccoDati byte alla volta // e memorizzando in dst l'indirizzo di ciascun gruppo di qElXDim[totDim-1] // elementi dell'array (qElXDim[totDim-1] e' l'ultima dimensione dell'array) for( j=0; j<qBlocchiDati; ++j ) { dst -= kSizeofPtr; srg -= dimBloccoDati; memcpy( dst, &srg, kSizeofPtr ); } // arretra dst di kSizeofPtr byte alla volta lungo lo spazio riservato ai // puntatori, arretrando parallelamente srg quanto serve per "seguire" la // sequenza dei puntatori alle varie dimensioni dell'array e memorizzando in // dst l'indirizzo di ciascun puntatore rilevato (elaborare questa soluzione // m'ha fatto girare ben bene la testa!) for( i=totDim-2; i>0; --i ) { for( j=0; j<qElXDim[i-1]; ++j ) { dst -= kSizeofPtr; srg -= qElXDim[i]*kSizeofPtr; memcpy( dst, &srg, kSizeofPtr ); } } } /*============================================================================== 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 []. Non essendo consentito l'accesso "esterno" a questa funzione, non viene effettuata alcuna verifica della validita' dei 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; } size_t AMD_QuantitaElementi( AMD *m ) { return AMD_calcola_quantita_elementi( m->qElXDim, m->totDim ); } size_t AMD_QuantitaPuntatori( AMD *m ) { return AMD_calcola_quantita_puntatori( m->qElXDim, m->totDim, 1, 0 ); } size_t AMD_DimensioniMemoriaAllocata( AMD *m ) { return AMD_DimensioniSpazioPuntatori(m) + AMD_DimensioniSpazioDati(m); } size_t AMD_DimensioniSpazioPuntatori( AMD *m ) { size_t qPtrs = AMD_calcola_quantita_puntatori( m->qElXDim, m->totDim, 1,0 ); return qPtrs*kSizeofPtr; } size_t AMD_DimensioniSpazioDati( AMD *m ) { return AMD_QuantitaElementi(m) * m->dimEl; } void *AMD_InizioSpazioDati( AMD *m ) { return m->d + AMD_DimensioniSpazioPuntatori(m); } /*============================================================================== ==============================================================================*/ int AMD_Crea( AMD *m, size_t *qElXDim, size_t totDim, size_t dEl ) { 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( !m || !qElXDim ) return 1; // parametri NULL if( !totDim || !dEl ) return 2; // dimensioni non valide if( m->d || m->qElXDim ) return 3; // la struttura m contiene gia' dati? qDati = AMD_calcola_quantita_elementi( qElXDim, totDim ); if( !qDati ) return 4; // una delle dimensioni ha 0 elementi memDati = qDati*dEl; qPtrs = AMD_calcola_quantita_puntatori( qElXDim, totDim, 1, 0 ); memPtrs = qPtrs*kSizeofPtr; 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, dEl, qElXDim, totDim ); memcpy( qElXDimTmp, qElXDim, totDim*sizeof(*qElXDim) ); m->d = dTmp; m->qElXDim = qElXDimTmp; m->totDim = totDim; m->dimEl = dEl; return 0; } free( dTmp ); } return 5; // allocazione fallita } int AMD_Distruggi( AMD *m ) { if( m ) { if( m->d ) free( m->d ); memset( m, 0, sizeof(*m) ); return 0; } return 1; // parametro NULL }
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.