Oppure

Loading
03/08/19 8:02
AldoBaldo
Per puro intrattenimento presente e futuro, ieri invece di dedicarmi all'opera di Piero Bartezzaghi o rimbecillirmi davanti alla TV, mi interrogavo sul modo in cui potrebbe essere possibile "generalizzare" in C (non C++) la creazione in memoria dinamica di matrici a due dimensioni che non fossero tipizzate. Dal momento che non ho alcuna formazione "ufficiale", ovviamente mi son venute in mente le cose più fantasiose, magari ingenue o semplicemente idiote, e ho provato a realizzarne una nella quale si usano solo puntatori a void e una struttura di supporto per conservare le "impostazioni" che descrivono la matrice creata. Ho dato al codice una forma tale da poterne fare una specie di "minilibreria" indipendente, così da poterla riutilizzare al bisogno.

Mi piacerebbe sapere se quel che ho messo insieme ha una sua dignità o è inutilizzabile (se non addirittura "pericoloso" - nelle prove casalinghe che ho fatto sembra funzionare tutto secondo le aspettative, ma non si sa mai) e se c'è qualche punto nel quale avrei potuto fare meno "rigiri" da impasticcato.

Primo file, m2d.h

#ifndef M2D_H_INCLUDED
#define M2D_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include <string.h>

typedef struct {
    void *dm;
    size_t w, h;
    size_t dEl;
} M2D_Struct_t;

enum {
    M2DErr_NoErr,
    M2DErr_NullPtr,
    M2DErr_NoMem,
    M2DErr_DatiEsistenti,
    M2DErr_DatiInesistenti,
    M2DErr_DimZero,
    M2DErr_CampiNonValidi,
    M2DErr_CampiIncompatibili
};

int M2D_Crea( M2D_Struct_t *m, size_t w, size_t h, size_t dEl );
int M2D_Duplica( M2D_Struct_t *mDup, const M2D_Struct_t mOrig );
int M2D_Copia( M2D_Struct_t mDest, const M2D_Struct_t mSorg );
int M2D_CampiValidi( const M2D_Struct_t s );
int M2D_CampiCompatibili( const M2D_Struct_t s1, const M2D_Struct_t s2 );
int M2D_Distruggi( M2D_Struct_t *m );

const char *M2D_DescrizioneErrore( int codice );


/*==============================================================================

CAMPI DELLA STRUTTURA M2D_Struct_t

 w      w: [w]idth, ovvero "larghezza" della matrice
        La quantita' delle colonne in una matrice

 h      h: [h]eight, ovvero "altezza" della matrice
        La quantita' delle righe in una matrice

 dEl    dEl; [d]imensioni degli [El]ementi della matrice
        Le dimensioni, in byte, di ciascuno degli elementi di una matrice

 dm     dm: [d]ati della [m]atrice
        Punta allo spazio in memoria occupato dalla matrice;
        In realta', non si tratta di un vero puntatore, bensi' di un...
          - puntatore a DOPPIA indirezione...
          - che punta al puntatore del primo elemento...
          - di un array di puntatori di h elementi...
          - ciascuno dei quali e' il puntatore a un array di w elementi...
          - ciascuno dei quali occupa dEl byte.
        In C, questo puntatore si puo' tranquillamente assegnare a un int**,
        a un char**, a un float** o a qualsiasi altro puntatore a doppia
        indirezione a qualsiasi tipo.
        In C++ si puo' fare la stessa cosa, ma occorre procedere con un cast
        esplicito.


VALORE DI RITORNO DELLE FUNZIONI

Tutte le funzioni restituiscono un codice d'errore che descrive la situazione
in uscita. I codici sono quelli dell'enumerazione gli elementi della quale
iniziano col prefisso M2DErr_:

  M2DErr_NoErr                Nessun errore, tutto a posto
  M2DErr_NullPtr              E' stato incontrato un puntatore NULL laddove
                              non ci sarebbe dovuto essere un puntatore NULL
  M2DErr_NoMem                E' fallita un'allocazione di memoria necessaria
  M2DErr_DatiEsistenti        Una struttura M2D_Struct_t per la quale ci si
                              aspettava che il campo dm fosse NULL, contiene
                              invece un campo dm non NULL
  M2DErr_DatiInesistenti      Una struttura M2D_Struct_t per la quale ci si
                              aspettava che il campo dm non fosse NULL,
                              contiene invece un campo dm NULL
  M2DErr_DimZero              E' stato richiesto di elaborare una matrice
                              riguardante elementi di dimensioni zero, oppure
                              con almeno una delle dimensioni w o h zero.
  M2DErr_CampiNonValidi       Una struttura passata come parametro presenta
                              campi non validi.
  M2DErr_CampiiIncompatibili  I campi di due strutture passate come parametri
                              hanno caratteristiche tali da renderle
                              incompatibili nei termini dell'operazione
                              richiesta.

==============================================================================*/

#ifdef __cplusplus
}
#endif

#endif // M2D_H_INCLUDED


Secondo file, m2d.c

#include "m2d.h"

static const size_t kSizeofVoidPtr = sizeof(void*);

static void *M2D_DistruggiSpazioMemoria( void *m,size_t w,size_t h,size_t dEl )
{
    if( m ) {
        if( (size_t)-1 != h ) {
            for( size_t r=0; r<h; ++r ) {
                void *aux;

                memcpy( &aux, m+r*kSizeofVoidPtr, kSizeofVoidPtr );

                if( aux )
                    free( aux );
            }
        }

        free( m );
        m = NULL;
    }

    return m;
}

int M2D_Crea( M2D_Struct_t *m, size_t w, size_t h, size_t dEl )
{
    void *dmTmp;

    if( !m )
        return M2DErr_NullPtr;
    if( m->dm )
        return M2DErr_DatiEsistenti;
    if( !w || !h || !dEl )
        return M2DErr_DimZero;

    dmTmp = calloc( h, kSizeofVoidPtr );

    if( dmTmp ) {
        size_t r;

        for( r=0; r<h; ++r ) {
            void *aux = calloc( w, dEl );

            if( aux )
                memcpy( dmTmp+r*kSizeofVoidPtr, &aux, kSizeofVoidPtr );
            else
                break;
        }

        if( r == h ) {
            m->dm  = dmTmp;
            m->w   = w;
            m->h   = h;
            m->dEl = dEl;
            return M2DErr_NoErr;
        }
        else {
            m = M2D_DistruggiSpazioMemoria( m, w, h-1, dEl );
        }
    }

    return M2DErr_NoMem;
}

int M2D_Duplica( M2D_Struct_t *mDup, const M2D_Struct_t mOrig )
{
    int err = M2D_Crea(mDup,mOrig.w,mOrig.h,mOrig.dEl);

    if( M2DErr_NoErr == err )
        err = M2D_Copia( *mDup, mOrig );

    return err;
}

int M2D_Copia( M2D_Struct_t mDest, const M2D_Struct_t mSorg )
{
    if( !mDest.dm )
        return M2DErr_DatiInesistenti;
    if( !M2D_CampiValidi(mSorg) )
        return M2DErr_CampiNonValidi;
    if( !M2D_CampiCompatibili(mDest,mSorg) )
        return M2DErr_CampiIncompatibili;

    for( size_t r=0; r<mSorg.h; ++r ) {
        void *aux_mSorg, *aux_mDest;
        memcpy( &aux_mSorg, mSorg.dm+r*kSizeofVoidPtr, kSizeofVoidPtr );
        memcpy( &aux_mDest, mDest.dm+r*kSizeofVoidPtr, kSizeofVoidPtr );
        memcpy( aux_mDest, aux_mSorg, mSorg.w*mSorg.dEl );
    }

    return M2DErr_NoErr;
}

int M2D_CampiValidi( const M2D_Struct_t s )
{
    return NULL!=s.dm && 0!=s.w && 0!=s.h && 0!=s.dEl;
}

int M2D_CampiCompatibili( const M2D_Struct_t s1, const M2D_Struct_t s2 )
{
    return s1.w==s2.w && s1.h==s2.h && s1.dEl==s2.dEl;
}

int M2D_Distruggi( M2D_Struct_t *m )
{
    if( !m )
        return M2DErr_NullPtr;
    if( !M2D_CampiValidi(*m) )
        return M2DErr_CampiNonValidi;

    m->dm = M2D_DistruggiSpazioMemoria( m->dm, m->w, m->h, m->dEl );

    m->w   = 0;
    m->h   = 0;
    m->dEl = 0;

    return M2DErr_NoErr;
}

const char *M2D_DescrizioneErrore( int codice )
{
    switch( codice ) {
    case M2DErr_NoErr:
        return "nessun errore";
    case M2DErr_NullPtr:
        return "puntatore NULL";
    case M2DErr_NoMem:
        return "allocazione di memoria fallita";
    case M2DErr_DatiEsistenti:
        return "la struttura fa riferimento a dati gia' allocati";
    case M2DErr_DatiInesistenti:
        return "la struttura non presenta il puntatore ai dati allocati";
    case M2DErr_DimZero:
        return "la struttura descrive una matrice con dimensioni inesistenti";
    case M2DErr_CampiNonValidi:
        return "uno o piu' campi della struttura con valore non valido";
    case M2DErr_CampiIncompatibili:
        return "uno o piu' campi con valori incompatibili tra le strutture";
    default:
        return "errore imprevisto";
    }
}
Ultima modifica effettuata da AldoBaldo 03/08/19 8:32
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.