Forum - C/C++
- Array multidimensionale generico in C?
AldoBaldo (Member )
Guru
Messaggi: 699
Iscritto: 08/01/2015
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
Codice sorgente - presumibilmente C++
#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
Codice sorgente - presumibilmente C/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.
AldoBaldo (Member )
Guru
Messaggi: 699
Iscritto: 08/01/2015
Cambiate un po' di cose, aggiunte un po' di altre...
File amd.h :
Codice sorgente - presumibilmente C++
#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 :
Codice sorgente - presumibilmente 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.
TheDarkJuster (Member )
Guru^2
Messaggi: 1620
Iscritto: 27/09/2013
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) ?
AldoBaldo (Member )
Guru
Messaggi: 699
Iscritto: 08/01/2015
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.
Ultimo (Member )
Guru
Messaggi: 877
Iscritto: 22/05/2010
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.
If ok Then GOTO Avanza else GOTO Inizia
TheDarkJuster (Member )
Guru^2
Messaggi: 1620
Iscritto: 27/09/2013
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.
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
AldoBaldo (Member )
Guru
Messaggi: 699
Iscritto: 08/01/2015
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.
AldoBaldo (Member )
Guru
Messaggi: 699
Iscritto: 08/01/2015
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...
Codice sorgente - presumibilmente C#
// 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...
Codice sorgente - presumibilmente C++
#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.
AldoBaldo (Member )
Guru
Messaggi: 699
Iscritto: 08/01/2015
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 :
Codice sorgente - presumibilmente 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.
==============================================================================*/
#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 :
Codice sorgente - presumibilmente 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 :
Codice sorgente - presumibilmente 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 :
Codice sorgente - presumibilmente C++
#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 :
Codice sorgente - presumibilmente 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 il 17/08/2019 alle 23: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.