Forum - C/C++
- Array dinamico in C - son sulla strada giusta per una libreria generica? - Pagina 3
Pagine: [ 1 2 3 4 ]
|
AldoBaldo (Member )
Guru
Messaggi: 700
Iscritto: 08/01/2015
Nel frattempo, ho messo insieme un programmino di esempio per provare l'array così com'è. Devo dire che mi pare funzioni come si deve, anche se non ne ho minimamente provato le prestazioni in termini di velocità (non mi sono neppure posto il problema). Non provoca disastri e, a giudicare da qualche "passaggio" tramite debugger, direi che non genera neppure perdite di memoria o altri fenomeni del genere.
Ho diviso il tutto in cinque file, il "perno" dei quali sono array_dinamico.h e array_dinamico.c.
I file main.c, gestione_schede.h e gestione_schede.c sono invece i file nei quali provo ad applicare l'array dinamico per vedere se funziona oppure no.
Che dici, se non vengono fuori errori, una cosa del genere può essere utile tra i programmi di pierotofy.it? In che sezione? Esempi? Moduli e librerie?
Edit del 1/7/2017: ho trovato e corretto due errori in array_dinamico.c, uno dei quali grave.
file main.c
Codice sorgente - presumibilmente C++
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "array_dinamico.h"
#include "gestione_schede.h"
int esci( ARR_DIN* ad, const char * msg, int codice ) ;
/*==============================================================================
Predispone una struttura ARR_DIN che conterra' TOT_SCHEDE puntatori che puntano
ad aree di memoria allocate dinamicamente con calloc() da AD_AggiungiInCoda().
Ciascun'area di memoria puntata avra' le dimensioni specificate tramite
sizeof(SP), passato ad AD_Inizializza().
Nell'esempio, il "fattore di incremento" dell'array e' prefissato in blocchi di
5 elementi alla volta, il che fa si' che la prima chiamata a AD_AggiungiInCoda()
provochi l'allocazione dello spazio necessario per i primi 5 puntatori
(inizialmente tutti NULL). Successivamente, si rende necessario un
ridimensionamento automatico dell'array, il che trattandosi di un programma di
prova ed esempio va benissimo.
Ogni chiamata da AD_AggiungiInCoda() provoca altresi' l'allocazione di uno
spazio di memoria sufficiente a contenere una struttura di tipo SP, inizialmente
occupata da soli zeri. Il puntatore a quell'area di memoria viene aggiunto in
coda all'array dinamico di puntatori trattato tramite la struttura ad di tipo
ARR_DIN.
Subito dopo l'allocazione e l'immagazzinamento del puntatore, la funzione
compila_scheda() si occupa di compilare ogni struttura con dati pseudocasuali
esemplificativi.
I dati immessi nell'array dinamico d'esempio ad vengono ordinati secondo tre
diversi criteri (l'ordinamento avviene spostando i puntatori alle aree di
memoria, non spostando i dati veri e propri). Dopo ogni ordinamento, i dati
vengono visualizzati in console.
==============================================================================*/
int main( ) {
ARR_DIN ad; /* struttura per l'array dinamico */
size_t i; /* un contatore */
srand ( time ( NULL ) ) ;
AD_Inizializza( & ad, sizeof ( SP) , 5 ) ; /* SEMPRE inizializzare ad */
for ( i= 0 ; i< TOT_SCHEDE; ++ i ) {
if ( AD_ERR == AD_AggiungiInCoda( & ad) )
return esci( & ad, "Aggiunta elemento non riuscita." , 0 ) ;
compila_scheda( AD_Leggi( & ad,i) ) ;
}
/* in lettura si possono usare tranquillamente i membri di ARR_DIN */
printf ( "Elementi nell'array (%u)\n \n " , ad.qEl ) ;
ordina( & ad, confronto_alfabetico ) ; /* usa AD_Scambia() */
mostra_schede( & ad, "IN ORDINE ALFABETICO" ) ;
ordina( & ad, confronto_cronologico ) ; /* usa AD_Scambia() */
mostra_schede( & ad, "IN ORDINE CRONOLOGICO" ) ;
ordina( & ad, confronto_identificativo ) ; /* usa AD_Scambia() */
mostra_schede( & ad, "SECONDO L'IDENTIFICATIVO" ) ;
return esci( & ad, NULL , 0 ) ; /* chiama anche AD_Distruggi() */
}
/*==============================================================================
Distrugge l'array dinamico ad (occorre SEMPRE farlo!!!).
Se msg non e' NULL mostra il messaggio indicato.
Mostra la dicitura "premi invio per uscire".
Si pone in attesa che venga premuto "invio".
Restituisce il valore ricevuto tramite il parametro codice.
==============================================================================*/
int esci( ARR_DIN * ad, const char * msg, int codice ) {
AD_Distruggi( ad ) ; /* da ricordare, se no memory leak a iosa! */
if ( NULL ! = msg ) printf ( msg ) ;
printf ( "\n \n Premi \" invio\" per uscire " ) ;
getchar ( ) ; /* attende l'invio */
return codice;
}
file gestione_schede.h
Codice sorgente - presumibilmente C++
/*==============================================================================
Questo header e il file di implementazione correlato (gestione_schede.c) sono
da intendere unicamente come elementi di un piccolo programma d'esempio
finalizzato a illustrare alcune delle modalità di funzionamento dell'array
dinamico implementato in array_dinamico.h e array_dinamico.c.
==============================================================================*/
#ifndef GESTIONE_SCHEDE_H_INCLUDED
#define GESTIONE_SCHEDE_H_INCLUDED
#include "array_dinamico.h"
#define TOT_SCHEDE 10
#define DIM_TAMP 24
typedef unsigned short us_t; /* esclusivamente per brevita' */
typedef const char cc_t; /* esclusivamente per brevita' */
/*==============================================================================
Struttura usata come tipo dato d'esempio da immagazzinare nell'array dinamico.
==============================================================================*/
typedef struct {
char cognome[ DIM_TAMP] ;
char nome[ DIM_TAMP] ;
us_t anno_nascita;
us_t id;
} SP; /* "SP" = scheda personale */
/*==============================================================================
Prototipi di funzioni.
==============================================================================*/
void ordina( ARR_DIN * ad, int ( * confr) ( const SP* s1,const SP* s2) ) ;
int confronto_alfabetico( const SP * s1, const SP * s2 ) ;
int confronto_cronologico( const SP * s1, const SP * s2 ) ;
int confronto_identificativo( const SP * s1, const SP * s2 ) ;
void mostra_schede( ARR_DIN * ad, const char * titolo ) ;
void compila_scheda( SP * s ) ;
#endif /* GESTIONE_SCHEDE_H_INCLUDED */
file gestione_schede.c
Codice sorgente - presumibilmente C++
/*==============================================================================
Questo file di implementazione e l'header correlato (gestione_schede.h) sono da
intendere unicamente come elementi di un piccolo programma d'esempio finalizzato
a illustrare alcune delle modalità di funzionamento dell'array dinamico
implementato in array_dinamico.h e array_dinamico.c.
==============================================================================*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gestione_schede.h"
/*==============================================================================
Una funzione per l'esemplificazione dell'uso di AD_Scambia() nell'ordinamento
dell'array di esempio.
==============================================================================*/
void ordina( ARR_DIN * ad, int ( * confr) ( const SP* s1,const SP* s2) ) {
size_t qEl = ad- > qEl;
if ( qEl > 1 ) {
size_t i, j, scambiato;
for ( scambiato= 0, i= 0 ; i< qEl- 1 ; ++ i, scambiato= 0 ) {
for ( j= 0 ; j< qEl- 1- i; ++ j ) {
if ( confr( AD_Leggi( ad,j) ,AD_Leggi( ad,j+ 1) ) > 0 ) {
AD_Scambia( ad, j, j+ 1 ) ;
scambiato = 1 ;
}
}
if ( ! scambiato ) break ;
}
}
}
/*==============================================================================
Tre funzioni di supporto alla funzione ordina().
==============================================================================*/
int confronto_alfabetico( const SP * s1, const SP * s2 ) {
int comp;
if ( ! ( comp= strcmp ( s1- > cognome,s2- > cognome) ) )
if ( ! ( comp= comp? comp: strcmp ( s1- > nome,s2- > nome) ) )
if ( ! ( comp= confronto_cronologico( s1,s2) ) )
comp = confronto_identificativo( s1, s2 ) ;
return comp;
}
int confronto_cronologico( const SP * s1, const SP * s2 ) {
us_t n1 = s1- > anno_nascita, n2 = s2- > anno_nascita;
int comp;
if ( ! ( comp= n1== n2? 0: ( n1> n2? 1: - 1) ) )
if ( ! ( comp= confronto_alfabetico( s1,s2) ) )
comp = confronto_identificativo( s1, s2 ) ;
return comp;
}
int confronto_identificativo( const SP * s1, const SP * s2 ) {
us_t id1 = s1- > id, id2 = s2- > id;
return id1== id2 ? 0 : ( id1> id2? 1: - 1) ;
}
/*==============================================================================
Due funzioni per la visualizzazione del contenuto delle schede inserite
nell'array di esempio.
==============================================================================*/
void mostra_scheda( SP * s ) {
int nc = printf ( "%s %s" , s- > cognome, s- > nome ) ;
while ( 2* DIM_TAMP- ( nc++ ) ) printf ( " " ) ;
printf ( "(%4d)" , s- > anno_nascita ) ;
for ( nc= 5 ; nc; -- nc ) printf ( " " ) ;
printf ( "ID: %05d\n " , s- > id ) ;
}
void mostra_schede( ARR_DIN * ad, const char * titolo ) {
int i;
printf ( "%s\n " , titolo ) ;
for ( i= 0 ; i< ad- > qEl; ++ i )
mostra_scheda( AD_Leggi( ad,i) ) ;
printf ( "\n " ) ;
}
/*==============================================================================
Due funzioni per la compilazione semicasuale automatica delle schede da inserire
nell'array di esempio.
==============================================================================*/
size_t estrai_unico( size_t * v, size_t * de ) {
size_t i, j = rand ( ) % ( * de) ;
size_t e = v[ j] ;
if ( -- ( * de) ) {
for ( i= j; i< ( * de) ; ++ i )
v[ i] = v[ i+ 1] ;
}
else {
* de = TOT_SCHEDE;
for ( i= 0 ; i< ( * de) ; ++ i )
v[ i] = i;
}
return e;
}
void compila_scheda( SP * s ) {
static size_t v1[ TOT_SCHEDE] = { 0,1,2,3,4,5,6,7,8,9} ;
static size_t v2[ TOT_SCHEDE] = { 0,1,2,3,4,5,6,7,8,9} ;
static size_t da_estrarre1= TOT_SCHEDE;
static size_t da_estrarre2= TOT_SCHEDE;
const char * kStrNomiEsempio[ TOT_SCHEDE] [ 2 ] = {
{ "Abate" , "Agatino" } ,
{ "Benante" , "Bruno" } ,
{ "Caroselli" , "Carlo" } ,
{ "De Filippi" , "Davide" } ,
{ "Ersani" , "Ernesto" } ,
{ "Fanoni" , "Fabio" } ,
{ "Giuffredo" , "Giovanni" } ,
{ "Iliante" , "Ilario" } ,
{ "Lastricotti" , "Luigi" } ,
{ "Marotta" , "Mario" }
} ;
size_t e1 = estrai_unico( v1, & da_estrarre1 ) ;
size_t e2 = estrai_unico( v2, & da_estrarre2 ) ;
strncpy ( s- > cognome, kStrNomiEsempio[ e1] [ 0] , DIM_TAMP- 1 ) ;
s- > cognome[ DIM_TAMP- 1 ] = '\0 ' ;
strncpy ( s- > nome, kStrNomiEsempio[ e2] [ 1] , DIM_TAMP- 1 ) ;
s- > nome[ DIM_TAMP- 1 ] = '\0 ' ;
s- > anno_nascita = 1980 + rand ( ) % 25 ;
s- > id = rand ( ) ;
}
file array_dinamico.h
Codice sorgente - presumibilmente C/C++
/*==============================================================================
ARRAY_DINAMICO
di Aldo Carpanelli - v1.0, giugno 2017
coadiuva la gestione d'una serie automaticamente
estensibile di puntatori a dati di qualsiasi tipo
La libreria ARR_DIN gestisce strutture che si comportano come "collettori" di
puntatori ad aree di memoria allocate dinamicamente. I puntatori trattati per
mezzo di questa libreria vengono immagazzinati in strutture di tipo ARR_DIN per
poterli rapidamente gestire con funzioni dedicate. Le funzioni si occupano di
allocare e deallocare la memoria puntata, per cui chi usa questa libreria non
dovrebbe allocare e/o deallocare direttamente. Non e' cosa saggia, altresi',
modificare direttamente alcun campo delle strutture di tipo ARR_DIN gia' usate
con le funzioni della libreria.
==============================================================================*/
#ifndef ARR_DIN_H
#define ARR_DIN_H
#define AD_OK 1
#define AD_ERR 0
typedef struct {
char **pEl; /* puntatori correntemente contenuti nell'array */
size_t dEl; /* la quantita' di memoria da allocare per ogni elemento */
size_t qEl; /* quantita' degli elementi correntemente nell'array */
size_t cap; /* capacita' massima corrente dell'array */
size_t inc; /* quantita' di puntatori da aggiungere/sottrarre ad */
/* ogni incremento/decremento della capacita' massima */
} ARR_DIN;
/* N.B. : nel primo parametro d'ogni funzione, "ad" sta per "array dinamico" */
int AD_Inizializza( ARR_DIN *ad, size_t dimElemento, size_t dimIncremento );
int AD_Distruggi( ARR_DIN *ad );
void *AD_Leggi( const ARR_DIN *ad, size_t posizione );
int AD_Inserisci( ARR_DIN *ad, size_t posizione );
int AD_Elimina( ARR_DIN *ad, size_t posizione );
int AD_AggiungiInTesta( ARR_DIN *ad );
int AD_EliminaInTesta( ARR_DIN *ad );
int AD_AggiungiInCoda( ARR_DIN *ad );
int AD_EliminaInCoda( ARR_DIN *ad );
int AD_Scambia( ARR_DIN *ad, size_t posizione_1, size_t posizione_2 );
#endif /* ARR_DIN_H */
file array_dinamico.c
Codice sorgente - presumibilmente C/C++
/*==============================================================================
ARRAY_DINAMICO
di Aldo Carpanelli - v1.0, giugno 2017
coadiuva la gestione d'una serie automaticamente
estensibile di puntatori a dati di qualsiasi tipo
La libreria ARR_DIN gestisce strutture che si comportano come "collettori" di
puntatori ad aree di memoria allocate dinamicamente. I puntatori trattati per
mezzo di questa libreria vengono immagazzinati in strutture di tipo ARR_DIN per
poterli rapidamente gestire con funzioni dedicate. Le funzioni si occupano di
allocare e deallocare la memoria puntata, per cui chi usa questa libreria non
dovrebbe allocare e/o deallocare direttamente. Non e' cosa saggia, altresi',
modificare direttamente alcun campo delle strutture di tipo ARR_DIN gia' usate
con le funzioni della libreria.
==============================================================================*/
#include <stdlib.h>
#include <string.h>
#include "array_dinamico.h"
#define AD_DIMCHR sizeof(char) /* dimensioni del tipo char */
#define AD_DIMPTR sizeof(char*) /* dimensioni del puntatore */
/* prototipo d'una funzione utilizzabile solo in questo file */
int AD_ModificaCapacita( ARR_DIN *ad, int dir_modf );
int AD_Inizializza( ARR_DIN *ad, size_t dimEl, size_t dimInc ) {
if( NULL!=ad && 0!=dimEl && 0!=dimInc && 1 == AD_DIMCHR ) {
memset( ad, 0, sizeof(*ad) );
ad->dEl = dimEl;
ad->inc = dimInc;
return AD_OK;
} else return AD_ERR;
}
int AD_Distruggi( ARR_DIN *ad ) {
if( NULL!=ad ) {
size_t i;
for( i=0; i<ad->qEl; ++i )
{ free( ad->pEl[i] ); ad->pEl[i] = NULL; }
free( ad->pEl );
memset( ad, 0, sizeof(*ad) );
return AD_OK;
} else return AD_ERR;
}
void *AD_Leggi( const ARR_DIN *ad, size_t pos ) {
if( NULL!=ad && pos<ad->qEl ) return ad->pEl[pos]; else return NULL;
}
int AD_Aggiungi( ARR_DIN *ad, size_t pos ) {
if( NULL!=ad && pos<=ad->qEl ) {
char *tmp = calloc( ad->dEl, 1 );
if( NULL!=tmp ) {
if( ad->qEl+1 > ad->cap )
if( !AD_ModificaCapacita(ad,1) )
{ free(tmp); return AD_ERR; }
memmove( ad->pEl+pos+1, ad->pEl+pos, (ad->qEl-pos)*AD_DIMPTR );
ad->pEl[pos] = tmp;
++ad->qEl;
return AD_OK;
} else return AD_ERR;
} else return AD_ERR;
}
int AD_Elimina( ARR_DIN *ad, size_t pos ) {
if( NULL!=ad && pos<ad->qEl ) {
free( ad->pEl[pos] );
memmove( ad->pEl+pos, ad->pEl+pos+1, (ad->qEl-pos-1)*AD_DIMPTR );
ad->pEl[--ad->qEl] = NULL;
if( ad->cap-ad->inc >= ad->qEl )
AD_ModificaCapacita(ad,-1);
return AD_OK;
} else return AD_ERR;
}
int AD_AggiungiInTesta( ARR_DIN *ad ) {
if( NULL!=ad ) return AD_Aggiungi( ad, 0 ); else return AD_ERR;
}
int AD_EliminaInTesta( ARR_DIN *ad ) {
if( NULL!=ad ) return AD_Elimina( ad, 0 ); else return AD_ERR;
}
int AD_AggiungiInCoda( ARR_DIN *ad ) {
if( NULL!=ad ) return AD_Aggiungi( ad, ad->qEl ); else return AD_ERR;
}
int AD_EliminaInCoda( ARR_DIN *ad ) {
if( NULL!=ad ) return AD_Elimina( ad, ad->qEl-1 ); else return AD_ERR;
}
int AD_Scambia( ARR_DIN *ad, size_t pos1, size_t pos2 ) {
if( NULL!=ad && pos1<ad->qEl && pos2<ad->qEl ) {
char *tmp = ad->pEl[pos1];
ad->pEl[pos1] = ad->pEl[pos2];
ad->pEl[pos2] = tmp;
return AD_OK;
} else return AD_ERR;
}
/* a seguire, una funzione utilizzabile solo in questo file */
int AD_ModificaCapacita( ARR_DIN *ad, int dir_modf ) { /* dir_modf, 1 o -1 */
/* NULL!=ad e' gia' stato verificato in AD_Aggiungi() o AD_Elimina() */
int inc = dir_modf * ad->inc; /* determina se ampliare o ridurre */
size_t nuova_dim = (ad->cap+inc)*AD_DIMPTR;
char **tmp = realloc( ad->pEl, nuova_dim );
if( NULL!=tmp || 0==nuova_dim ) {
/* memset() azzera i nuovi puntatori, se e' il caso */
if( inc>0 ) memset( tmp+ad->cap, 0, inc*AD_DIMPTR );
ad->pEl = tmp; /* "consolida" il nuovo array, accettandolo in pEl */
ad->cap += inc; /* aggiorna la capacita' dell'array */
return AD_OK;
} else return AD_ERR;
}
Ultima modifica effettuata da AldoBaldo il 01/07/2017 alle 8:21
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.
lumo (Member )
Expert
Messaggi: 449
Iscritto: 18/04/2010
Moduli e librerie va bene, insegnamento e hobby pure.
AldoBaldo (Member )
Guru
Messaggi: 700
Iscritto: 08/01/2015
Grazie Lumo. Vada per "moduli e librerie". Attendo però qualche giorno, perché il fatto che ho individuato degli errori mi induce a pensare che è bene che usi ancora un po' "in proprio" 'sti array dinamici. Hai visto mai che ci siano ancora delle pecche!
Ah, ovviamente sarebbe bello se qualcuno desse a sua volta un'occhiata: quattro, sei, otto occhi vedono meglio di due (i miei).
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.
lumo (Member )
Expert
Messaggi: 449
Iscritto: 18/04/2010
Due consigli:
1) non usare un define per sizeof(char) e sizeof(char*), non è molto sensato e peggiora la leggibilità
2) Io eviterei l'else (soprattutto in riga, blrgh) in tutti quei casi dove il return precedente lo rende implicito.
nessuno (Normal User)
Guru^2
Messaggi: 6403
Iscritto: 03/01/2010
Cose di questo genere
if( NULL!=ad ) return AD_Aggiungi( ad, ad->qEl ); else return AD_ERR;
le scriverei
return (ad ? AD_Aggiungi(ad, ad->qEl) : AD_ERR);
E comunque, al posto di
} else return AD_ERR;
semplicemente
}
return AD_ERR;
Ultima modifica effettuata da nessuno il 02/07/2017 alle 19:56
Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti ( uno dei padri fondatori del moderno Calcolo delle probabilità ) chiamava il gioco del Lotto Tassa sulla stupidità.
AldoBaldo (Member )
Guru
Messaggi: 700
Iscritto: 08/01/2015
Sì, anche se sono più questioni di "stile" che di sostanza posso senz'altro adeguare il codice, se secondo voi che siete più esperti è meglio fare così. Magari migliora la leggibilità del codice, non so (ormai io mi sono abituato a vedere quel tipo di espressioni e non mi "turbano", ma non si può mai dire). Il funzionamento dovrevve comunque essere identico, no?
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.
nessuno (Normal User)
Guru^2
Messaggi: 6403
Iscritto: 03/01/2010
Quando diventano migliaia di righe la leggibilità diventa sostanza.
Righe come
if( NULL!=tmp ) {
devono diventare
if( tmp ) {
Ultima modifica effettuata da nessuno il 02/07/2017 alle 22:06
Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti ( uno dei padri fondatori del moderno Calcolo delle probabilità ) chiamava il gioco del Lotto Tassa sulla stupidità.
AldoBaldo (Member )
Guru
Messaggi: 700
Iscritto: 08/01/2015
Sarà fatto!
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.