Oppure

Loading
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

#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.
11/08/19 22:52
AldoBaldo
Cambiate un po' di cose, aggiunte un po' di altre...

File amd.h:

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

#include "amd.h"

#include <string.h>
#include <stdarg.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 <================================

/*==============================================================================
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 *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*AMD_SizeofPtr;

    // 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*AMD_SizeofPtr;
    // srg punti appena oltre lo spazio destinato ai dati
    srg = pDati + qBlocchiDati*dimBloccoDati;

    // arretra dst di AMD_SizeofPtr byte per 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 -= AMD_SizeofPtr;
        srg -= dimBloccoDati;
        memcpy( dst, &srg, AMD_SizeofPtr );
    }

    // arretra dst di AMD_SizeofPtr byte per 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 -= AMD_SizeofPtr;
            srg -= qElXDim[i]*AMD_SizeofPtr;
            memcpy( dst, &srg, AMD_SizeofPtr );
        }
    }
}

/*==============================================================================
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;
}

/*==============================================================================
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";
}
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.
11/08/19 23:44
TheDarkJuster
Non capisco il senso di questo codice. Cioè a parte il fatto che è divertente scriverlo che vantaggio ha rispetto a fare int*a= malloc(4*3*5*7) *(a+20) ?
aaa
12/08/19 17:46
AldoBaldo
Prima di risponderti dovrei capire cosa significa la formula che mi proponi, perché mi sfugge. Dal momento che mi sfugge, ho provato a inserirla in un programmino di prova per poi analizzare quel che ne veniva fuori, ma il compilatore mi ha mandato a stendere con un errore che dice: "invalid operands to binary * (have 'void *' and 'int *')". A parte l'errore, che magari hai sbagliato a battere la formula, cosa mi sto perdendo? Mi spieghi da dove nasce, cosa c'è dietro che mi sfugge?

E comunque, il mio codice sembra funzionare "a rate". Devo revisionarlo.
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.
12/08/19 21:48
Ultimo
Postato originariamente da AldoBaldo:

Prima di risponderti dovrei capire cosa significa la formula che mi proponi, perché mi sfugge. Dal momento che mi sfugge, ho provato a inserirla in un programmino di prova per poi analizzare quel che ne veniva fuori, ma il compilatore mi ha mandato a stendere con un errore che dice: "invalid operands to binary * (have 'void *' and 'int *')". A parte l'errore, che magari hai sbagliato a battere la formula, cosa mi sto perdendo? Mi spieghi da dove nasce, cosa c'è dietro che mi sfugge?

E comunque, il mio codice sembra funzionare "a rate". Devo revisionarlo.


:k:

If ok Then GOTO Avanza else GOTO Inizia

12/08/19 22:08
TheDarkJuster
Postato originariamente da AldoBaldo:
Prima di risponderti dovrei capire cosa significa la formula che mi proponi, perché mi sfugge. Dal momento che mi sfugge, ho provato a inserirla in un programmino di prova per poi analizzare quel che ne veniva fuori, ma il compilatore mi ha mandato a stendere con un errore che dice: &quot;invalid operands to binary * (have 'void *' and 'int *')&quot;. A parte l'errore, che magari hai sbagliato a battere la formula, cosa mi sto perdendo? Mi spieghi da dove nasce, cosa c'è dietro che mi sfugge?

E comunque, il mio codice sembra funzionare &quot;a rate&quot;. Devo revisionarlo.

int *a= malloc(3*4*5*2*sizeof(int)); vedilo come un array [3][4][5][2]
int valCella=*(a+2*4*5*2+3*5*2+1) ; Equivale ad a[2][3][.... Scusa ma non ho voglia di fare il conto. Chiedo perdono
aaa
13/08/19 11:09
AldoBaldo
A proposito del "funzionamento a rate", ho trovato l'inghippo e ho capito in cosa consiste l'errore: sta in AMD_imposta_ptr(). Ora devo trovare il modo per correggerlo -- so già dove devo arrivare, ma devo trovare la strada per arrivarci. Sono ottimista!

@TheDarkJuster
Ma che "perdono"! Mica hai obblighi, eh, è già tanto che tu abbia deciso di rispondermi, cosa che apprezzo. Ora mi metto lì e cerco di capire cosa mi stai dicendo (per ora ho solo preso atto del fatto che c'è un tuo intervento, poi ti dirò;).
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.
13/08/19 11:47
AldoBaldo
int *a = malloc(3*4*5*2*sizeof(int));
vedilo come un array [3][4][5][2]

int valCella = *(a+2*4*5*2+3*5*2+1);
Equivale ad a[2][3][....

@ TheDarkJuster
Allora... detta terra terra: non son certo d'aver capito ma credo di sì.

Parti con un puntatore ad int, che indica l'inizio di un blocco di memoria di 3*4*5*2*sizeof(int) byte. Può essere lo spazio nel quale memorizzare 3*4*5*2=120 valori di tipo int. Se sizeof(int) sono 4 byte, allora si tratta di 480 byte in tutto. Fin qui ci arrivo.

Con la seconda formula, se non ho frainteso, mi stai dicendo che posso individuare la posizione di una certa "cella" usando l'aritmetica dei puntatori, giusto? In quel modo si può "simulare" un array multidimensionale a partire dal puntatore a. Se è questo che mi volevi dire, è cosa che mi era già nota e senza dubbio funziona.

Quel che volevo ottenere io, però, è cosa un po' diversa: a me interessa ottenere un puntatore che posso impiegare come se fosse un vero array multidimensionale, senza usare esplicitamente su di esso l'aritmetica dei puntatori. Tipo...

// non ho un...
int *a;
// bensì un...
int ****a4dPtr;

// a4d rappresenta il punto di inizio di un array a quattro
// dimensioni, e posso accedere alle "celle" dell'array a
// quattro dimensioni con [][][][], tipo...

int ****a4dPtr; // ovviamente, bisognera' poi allocarne lo spazio

a4dPtr[1][1][3][1] = -23; // -23 e' un valore a caso

// l'idea era ottenere dal mio array a quattro dimensioni allocato
// in memoria dinamica lo stesso comportamento che avrei ottenuto
// se avessi dichiarato...

int a4d[3][4][5][2];

a4d[1][1][3][1] = -23; // -23 e' un valore a caso


Mi perdo ancora qualcosa? Perché sto camminando un po' al limite delle mie possibilità di comprensione, temo.

Per quel che ne so (dopo avere letto in passato e sperimentato in questi giorni), per avere l'equivalente di un array multidimensionale allocato dinamicamente non basta allocare lo spazio-dati, ma serve anche allocare uno spazio aggiuntivo in testa al blocco che deve contenere i puntatori che fanno riferimento alle varie dimensioni, come in una specie di matriosca.

La libreria che sto cercando di mettere insieme (senza pretesa di farne qualcosa di "professionale";) dovrebbe servire a "automatizzare" in qualche misura il procedimento di allocazione della memoria, predisposizione dei puntatori "di intestazione" e conservazione in una struttura dei dati relativi alle carratteristiche dell'array multidimensionale allocato.

Sto cercando di arrivare a qualcosa che mi permetta di fare...

#include "amd.h"

int main() {
    AMD_struct amd = {0};
    
    int errore = AMD_Crea( &amd, sizeof(int), 4, 3, 4, 5, 2 );
    
    if( AMDErr_NoErr == errore ) {
        int ****alias4d = amd.d;
        
        // ora alias4d puo' essere usato con alias4d[][][][]
        
        AMD_Distruggi( &amd );
    }    
}
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.