28/06/17 8:27
AldoBaldo
Ciao a te che leggi.
Sto passando il tempo tentando di mettere insieme una micro-libreria (per uso personale, nel senso che non voglio farne chissà che) che tratti array dinamici di puntatori di qualsiasi tipo. Come al solito, più insisto più dubbi mi vengono, quindi tento di coinvolgerti.
L'idea parte dal presupposto, che credo corretto, che un puntatore è un puntatore, ovvero un indirizzo di memoria, ovvero un numero che ha una dimensione fissa determinata dal tipo di computer che si sta usando (16 bit ai "miei tempi", poi 32 bit, oggi si viaggia verso i 64 bit...). Per questo dovrei poter trattare un array di puntatori char* come un array di puntatori double* o di puntatori pinco_palla* senza che cambi gran che.
Dunque, definisco una struttura ARRAY_DINAMICO fatta così:
Nella mia fantasia, char **pEl dovrebbe corrispondere a char *pEl[], ovvero una serie di indirizzi (numeri) ciascuno dei quali ha la stessa dimensione in bit che avrebbe in double *pEl[] o pinco_palla *pEl[]. Ipotizzando che char* sia 32 bit, anche double* e' 32 bit e pinco_palla* è 32 bit, giusto?
Allora io alloco char **pEl così:
...dove cap è la capacità corrente dell'array e AD_INC è una costante che definisce un certo numero fisso di elementi da usare ogni volta che si espande o contrae l'array (in questo caso lo sto espandendo). In realtà uso realloc(), ma non credo faccia differenza. Le funzioni di "espansione" e "contrazione" complete sono:
Ora c'è il punto per me critico: quando implemento funzioni per "scrivere" e "leggere" i puntatori posso usare puntatori generici void* anche se la memoria è stata allocata come char**? Ad esempio con una funzioni tipo:
...rischio qualche cedimento della struttura dell'Universo se procedo a questo modo?
Il compilatore che sto usanto (gcc in mingw) non batte ciglio e compila senza neanche un warning, però se non ho capito male a volte ci sono cosette che esulano dallo standard e che determinano comportamenti indefiniti che alcuni compilatori segnalano e altri no.
Lo so che son domande un po' così, però ogni tanto entro in questi odiosi loop di dubbi confusionari e mi torna davvero comodo un parere "a freddo". Mi aiuti, senza voli pindarici troppo alti sopra la mia testa?
Sto passando il tempo tentando di mettere insieme una micro-libreria (per uso personale, nel senso che non voglio farne chissà che) che tratti array dinamici di puntatori di qualsiasi tipo. Come al solito, più insisto più dubbi mi vengono, quindi tento di coinvolgerti.
L'idea parte dal presupposto, che credo corretto, che un puntatore è un puntatore, ovvero un indirizzo di memoria, ovvero un numero che ha una dimensione fissa determinata dal tipo di computer che si sta usando (16 bit ai "miei tempi", poi 32 bit, oggi si viaggia verso i 64 bit...). Per questo dovrei poter trattare un array di puntatori char* come un array di puntatori double* o di puntatori pinco_palla* senza che cambi gran che.
Dunque, definisco una struttura ARRAY_DINAMICO fatta così:
typedef struct { char **pEl; // puntatori correntemente contenuti nell'array size_t qEl; // quantita' degli elementi correntemente contenuti nell'array size_t cap; // capacita' massima corrente dell'array } ARRAY_DINAMICO;
Nella mia fantasia, char **pEl dovrebbe corrispondere a char *pEl[], ovvero una serie di indirizzi (numeri) ciascuno dei quali ha la stessa dimensione in bit che avrebbe in double *pEl[] o pinco_palla *pEl[]. Ipotizzando che char* sia 32 bit, anche double* e' 32 bit e pinco_palla* è 32 bit, giusto?
Allora io alloco char **pEl così:
char **tmp = calloc( cap+AD_INC)*sizeof(*tmp) );
...dove cap è la capacità corrente dell'array e AD_INC è una costante che definisce un certo numero fisso di elementi da usare ogni volta che si espande o contrae l'array (in questo caso lo sto espandendo). In realtà uso realloc(), ma non credo faccia differenza. Le funzioni di "espansione" e "contrazione" complete sono:
int AD_IncrementaCapacita( ARRAY_DINAMICO *ad ) { if( NULL != ad ) { char **tmp = realloc( // segue nella riga sotto ad->pEl, (ad->cap+AD_INC)*sizeof(*tmp) ); if( NULL != tmp ) { memset( tmp+ad->cap, 0, AD_INC*sizeof(*tmp) ); // annulla i nuovi puntatori ad->pEl = tmp; // "consolida" il nuovo array, accettandolo in pEl ad->cap += AD_INC; // aggiorna la capacita' dell'array return AD_OK; } else return !AD_OK; // errore } else return !AD_OK; // errore } int AD_RiduciCapacita( ARRAY_DINAMICO *ad ) { if( NULL != ad ) { if( ad->cap-AD_INC >= ad->qEl ) { char **tmp = realloc( // segue nella riga sotto ad->pEl, (ad->cap>AD_INC?ad->cap-AD_INC:0)*sizeof(*tmp) ); if( NULL != tmp ) { ad->pEl = tmp; // "consolida" il nuovo array, accettandolo in pEl ad->cap -= AD_INC; // aggiorna la capacita' dell'array return AD_OK; } else return !AD_OK; // errore } else return !AD_OK; // errore } else return !AD_OK; // errore }
Ora c'è il punto per me critico: quando implemento funzioni per "scrivere" e "leggere" i puntatori posso usare puntatori generici void* anche se la memoria è stata allocata come char**? Ad esempio con una funzioni tipo:
int AD_Inserisci( ARRAY_DINAMICO *ad, void *el, size_t pos ) { if( NULL != ad && NULL != el && pos <= ad->qEl ) { if( ad->qEl+1 > ad->cap ) if( !AD_IncrementaCapacita(ad) ) return !AD_OK; // errore memmove( ad->pEl+pos+1, ad->pEl+pos, (ad->qEl-pos)*sizeof(*ad->pEl) ); ad->pEl[pos] = el; ++ad->qEl; return AD_OK; // ok } else return !AD_OK; // errore } void *AD_Leggi( const ARRAY_DINAMICO *ad, size_t pos ) { if( NULL != ad && pos < ad->qEl ) return ad->pEl[pos]; else return NULL; // errore }
...rischio qualche cedimento della struttura dell'Universo se procedo a questo modo?
#include <stdlib.h> #include <stdio.h> #include <string.h> #include "array_dinamico.h" typedef struct pinco_palla { char una_stringa[32]; float un_numero; } PINCO_PALLA; int main() { PINCO_PALLA pp, *ppPtr; ARRAY_DINAMICO ad; strcpy( pp.una_stringa, "pippo" ); pp.un_numero = 10.2f; // 10.2 e' senz'altro un bel numero AD_Azzera( &ad ); if( AD_OK == AD_Inserisci(&ad,&pp,0) ) { ppPtr = (PINCO_PALLA*) AD_Leggi( &ad, 0 ); printf( "%s, %f\n\n", ppPtr->una_stringa, ppPtr->un_numero ); } else printf( "Errore!!!\n\n" ); getchar(); return 0; }
Il compilatore che sto usanto (gcc in mingw) non batte ciglio e compila senza neanche un warning, però se non ho capito male a volte ci sono cosette che esulano dallo standard e che determinano comportamenti indefiniti che alcuni compilatori segnalano e altri no.
Lo so che son domande un po' così, però ogni tanto entro in questi odiosi loop di dubbi confusionari e mi torna davvero comodo un parere "a freddo". Mi aiuti, senza voli pindarici troppo alti sopra la mia testa?
Ultima modifica effettuata da AldoBaldo 28/06/17 8:41
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.