Oppure

Loading
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:

/*==============================================================================
         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.