Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - Array dinamico in C - son sulla strada giusta per una libreria generica?
Forum - C/C++ - Array dinamico in C - son sulla strada giusta per una libreria generica?

Pagine: [ 1 2 3 4 ] Precedente | Prossimo
Avatar
AldoBaldo (Member)
Expert


Messaggi: 296
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 10:27
Mercoledì, 28/06/2017
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ì:

Codice sorgente - presumibilmente C/C++

  1. typedef struct {
  2.     char **pEl; // puntatori correntemente contenuti nell'array
  3.     size_t qEl; // quantita' degli elementi correntemente contenuti nell'array
  4.     size_t cap; // capacita' massima corrente dell'array
  5. } 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ì:

Codice sorgente - presumibilmente C/C++

  1. 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:

Codice sorgente - presumibilmente C#

  1. int AD_IncrementaCapacita( ARRAY_DINAMICO *ad ) {
  2.     if( NULL != ad ) {
  3.         char **tmp = realloc( // segue nella riga sotto
  4.             ad->pEl, (ad->cap+AD_INC)*sizeof(*tmp) );
  5.  
  6.         if( NULL != tmp ) {
  7.             memset( tmp+ad->cap, 0, AD_INC*sizeof(*tmp) ); // annulla i nuovi puntatori
  8.             ad->pEl = tmp;   // "consolida" il nuovo array, accettandolo in pEl
  9.             ad->cap += AD_INC; // aggiorna la capacita' dell'array
  10.             return AD_OK;
  11.         } else return !AD_OK; // errore
  12.     } else return !AD_OK; // errore
  13. }
  14.  
  15. int AD_RiduciCapacita( ARRAY_DINAMICO *ad ) {
  16.     if( NULL != ad ) {
  17.         if( ad->cap-AD_INC >= ad->qEl ) {
  18.             char **tmp = realloc( // segue nella riga sotto
  19.                 ad->pEl, (ad->cap>AD_INC?ad->cap-AD_INC:0)*sizeof(*tmp) );
  20.  
  21.             if( NULL != tmp ) {
  22.                 ad->pEl = tmp;   // "consolida" il nuovo array, accettandolo in pEl
  23.                 ad->cap -= AD_INC; // aggiorna la capacita' dell'array
  24.                 return AD_OK;
  25.             } else return !AD_OK; // errore
  26.         } else return !AD_OK; // errore
  27.     } else return !AD_OK; // errore
  28. }



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:

Codice sorgente - presumibilmente C#

  1. int AD_Inserisci( ARRAY_DINAMICO *ad, void *el, size_t pos ) {
  2.     if( NULL != ad && NULL != el && pos <= ad->qEl ) {
  3.         if( ad->qEl+1 > ad->cap )
  4.             if( !AD_IncrementaCapacita(ad) )
  5.                 return !AD_OK; // errore
  6.  
  7.         memmove( ad->pEl+pos+1, ad->pEl+pos, (ad->qEl-pos)*sizeof(*ad->pEl) );
  8.         ad->pEl[pos] = el;
  9.         ++ad->qEl;
  10.         return AD_OK; // ok
  11.     } else return !AD_OK; // errore
  12. }
  13.  
  14. void *AD_Leggi( const ARRAY_DINAMICO *ad, size_t pos ) {
  15.     if( NULL != ad && pos < ad->qEl )
  16.         return ad->pEl[pos];
  17.     else return NULL; // errore
  18. }



...rischio qualche cedimento della struttura dell'Universo se procedo a questo modo?

Codice sorgente - presumibilmente C++

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include "array_dinamico.h"
  5.  
  6. typedef struct pinco_palla {
  7.     char una_stringa[32];
  8.     float un_numero;
  9. } PINCO_PALLA;
  10.  
  11. int main() {
  12.     PINCO_PALLA pp, *ppPtr;
  13.     ARRAY_DINAMICO ad;
  14.    
  15.     strcpy( pp.una_stringa, "pippo" );
  16.     pp.un_numero = 10.2f; // 10.2 e' senz'altro un bel numero
  17.    
  18.     AD_Azzera( &ad );
  19.    
  20.     if( AD_OK == AD_Inserisci(&ad,&pp,0) ) {
  21.         ppPtr = (PINCO_PALLA*) AD_Leggi( &ad, 0 );
  22.         printf( "%s, %f\n\n", ppPtr->una_stringa, ppPtr->un_numero );
  23.     } else printf( "Errore!!!\n\n" );
  24.    
  25.     getchar();
  26.     return 0;
  27. }



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 il 28/06/2017 alle 10:41


Ma cosa vuoi che ne sappia? Io ci gioco, col codice, mica ci lavoro!
PM Quote
Avatar
Mikelius (Member)
Pro


Messaggi: 65
Iscritto: 14/04/2017

Segnala al moderatore
Postato alle 10:55
Mercoledì, 28/06/2017
Testo quotato

Postato originariamente da AldoBaldo:

Codice sorgente - presumibilmente C/C++

  1. char **tmp = calloc( cap+AD_INC)*sizeof(*tmp) );




Qui mi pare che manchi una parentesi, ma sarà errore di battitura XD



Comunque, è come dici, in teoria la dimensione di un puntatore è fissata dal sistema in quanto la dimensione di un indirizzo non dovrebbe essere variabile. Però non so i problemi che si creano a creare l' ARRAY void* e successivamente usare nel main un cast() ad esempio.



Ultima modifica effettuata da Mikelius il 28/06/2017 alle 10:58


"Io ne ho viste cose che voi umani non potreste immaginarvi...."
PM Quote
Avatar
AldoBaldo (Member)
Expert


Messaggi: 296
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 11:19
Mercoledì, 28/06/2017
Hai ragione: è un errore di battitura (infatti la versione che ho nei miei codici è diversa e usa realloc(); copiando, incollando e modificando mi son partite un paio di inesattezze).

Doveva essere:

Codice sorgente - presumibilmente C/C++

  1. char **tmp = malloc( (cap+AD_INC)*sizeof(*tmp) );



Scrivi: "non so i problemi che si creano a creare l' ARRAY void* e successivamente usare nel main un cast", che è proprio quel che sto cercando di capire! Mi sembra che non debbano essercene, ma sarebbe utile se chi se ne intende più di me mi desse conferma o smentita certa.

Ovvio che quando si fanno cast occorre sempre stare attenti, ma quella è una caratteristica comune del C, tanto che le funzioni di allocazione usano tutte void*. Una volta chiarito il mio dubbio, senz'altro mi metterò col cesello in mano a rifinire tutti i dettagli necessari per evitare errori dovuti ai cast.

Ultima modifica effettuata da AldoBaldo il 28/06/2017 alle 11:21


Ma cosa vuoi che ne sappia? Io ci gioco, col codice, mica ci lavoro!
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 5379
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 14:17
Mercoledì, 28/06/2017
In questa linea

ad->pEl[pos] = el;

devi dirlo che è un puntatore a char

ad->pEl[pos] = (char *)el;


Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
PM Quote
Avatar
lumo (Member)
Expert


Messaggi: 357
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 23:08
Mercoledì, 28/06/2017
Un giorno ti convincerò ad usare incrementi moltiplicativi invece che addizionali quando estendi l'array, purtroppo sono un po' impegnato in questi giorni.

PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1405
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 23:20
Mercoledì, 28/06/2017
Una precisazione: la dimensione di un puntatore dipende da tre fattori: CPU, modo di operazione (modalità reale o protetta) e se il so ha attivato la paginazione. Credo che un indirizzi di memoria virtuale in alcuni sistemi sia di 48bit, correggettermi se sbaglio.

PM Quote
Avatar
AldoBaldo (Member)
Expert


Messaggi: 296
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 7:32
Giovedì, 29/06/2017
Grazie per le risposte, anche se i miei dubbi non si dissipano ancora. Vi rispondo uno per uno.

==============================

Nessuno, perché quel cast è necessario? A me sembra come quando si fa una cosa tipo...

Codice sorgente - presumibilmente C/C++

  1. int *intPtr = malloc( sizeof(*intPtr) );



In quel caso non si usa il cast, no? Si prende il puntatore void* e lo si usa da quel momento in poi in intPtr come se fosse un puntatore int* qualsiasi. Mi son perso qualcosa (d'altro)?

==============================

Lumo, ricordo la discussione in merito alla tipologia di incremento. Le mie "resistenze" risalgono a quando facevo cose per un computer con 1 MB di RAM in tutto e una gestione della memoria non "protetta", dove ogni programma era "confinato" entro uno spazio preallocato dal sistema e non estendibile che per forza di cose doveva essere alquanto ristretto. Ci sono imprinting dai quali è difficile liberarsi anche quando le condizioni di contorno cambiano drasticamente. E' questo che rende vecchie le persone di una certa età.

==============================

TheDarkJuster, quel che osservi (e di cui non so nulla) che ricadute ha sul caso specifico? E' una cosa che influisce sulla portabilità del codice (che per quel che ho in mente sarebbe poco importante) o può creare problemi nel momento in cui uno stesso programma già compilato viene fatto "girare" in una modalità piuttosto che un'altra anche sullo stesso computer?


Ma cosa vuoi che ne sappia? Io ci gioco, col codice, mica ci lavoro!
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 5379
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 9:49
Giovedì, 29/06/2017
In C++ è obbligatorio. Non in C.

Ma il codice è bene che sia "portabile" anche verso compilatori C++.


Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
PM Quote
Avatar
AldoBaldo (Member)
Expert


Messaggi: 296
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 11:44
Giovedì, 29/06/2017
@nessuno

Ah, ecco! Ora i conti tornano: io intendevo fare una cosa in C, non in C++. Del resto gli array dinamici in C++ fanno parte dello standard, quindi troverei futile fare qualcosa di personalizzato. Comunque, sì, in C++ quel cast sarebbe inevitabile (se no non compila).


Ma cosa vuoi che ne sappia? Io ci gioco, col codice, mica ci lavoro!
PM Quote
Pagine: [ 1 2 3 4 ] Precedente | Prossimo