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++ - Creare vettori/matrici di puntatori con poco codice
Forum - C/C++ - Creare vettori/matrici di puntatori con poco codice

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


Messaggi: 346
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 12:30
Domenica, 30/07/2017
E rieccomi con uno dei miei "esperimenti" per reinventare la ruota...

L'idea, questa volta, è avere a disposizione delle funzioni "stand alone" per allocare e deallocare in memoria dinamica vettori e matrici di qualsiasi tipo con il minor sforzo possibile (una riga di codice, una verifica). Dopo un po' di lavorio mentale (ho i miei limiti) sono arrivato a "sintetizzare" quattro funzioncine, due per creare/distruggere vettori e due per creare/distruggere matrici. Mentre le funzioni che riguardano i vettori non hanno bisogno nessun supporto esterno, quelle che riguardano le matrici s'appoggiano alle funzioni che riguardano i vettori.

Ve le propongo perché mi piacerebbe sapere se ci vedete qualche problema, soprattutto per quel che riguarda eventuali puntatori impazziti o memoria non deallocata a dovere. Nel frattempo continuo con i test, che finora ho completato solo in minima parte.

Edit: noto ora che per qualche ragione la formattazione del codice viene "disturbata" dalla presenza degli apostrofi all'interno dei commenti; per aggirare la questione metto i commenti DOPO il codice, sperando che funzioni.

Codice sorgente - presumibilmente C++

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h> /* vedi nota */
  4.  
  5. /*===> distruggi_vettore() <==================================================*/
  6.  
  7. int distruggi_vettore( void *vettore, size_t qEl ) {
  8.     if( vettore && qEl ) {
  9.         uintptr_t *v = vettore; /* puntatore ausiliario */
  10.         size_t e; /* contatore per gli elementi del vettore */
  11.  
  12.         for( e=0; e<qEl; ++e )
  13.             free( (void*)v[e] );
  14.         free( vettore );
  15.         return 1; /* 1 = tutto bene */
  16.     }
  17.  
  18.     return 0; /* 0 = errore */
  19. }
  20.  
  21. /*===> crea_vettore() <=======================================================*/
  22.  
  23. void *crea_vettore( size_t qEl, size_t dimEl ) {
  24.     uintptr_t *v = NULL; /* v = vettore */
  25.  
  26.     if( qEl && dimEl ) {
  27.         v = calloc( qEl, sizeof(uintptr_t) );
  28.  
  29.         if( v ) {
  30.             size_t e; /* contatore per gli elementi  del vettore*/
  31.  
  32.             for( e=0; e<qEl; ++e ) {
  33.                 v[e] = (uintptr_t) calloc( 1, dimEl );
  34.                 if( !v[e] ) break;
  35.             }
  36.  
  37.             if( e < qEl ) { /* allocazione non completata */
  38.                 distruggi_vettore( v, e );
  39.                 v = NULL;
  40.             }
  41.         }
  42.     }
  43.  
  44.     return v;
  45. }
  46.  
  47. /*===> distruggi_matrice() <==================================================*/
  48.  
  49. int distruggi_matrice( void *matrice, size_t qRighe, size_t qColonne ) {
  50.     if( matrice && qRighe && qColonne ) {
  51.         uintptr_t **m = matrice; /* puntatore ausiliario */
  52.         size_t r; /* contatore per le righe della matrice */
  53.  
  54.         for( r=0; r<qRighe; ++r )
  55.             distruggi_vettore( m[r], qColonne );
  56.         free( m );
  57.         return 1; /* 1 = tutto bene */
  58.     }
  59.  
  60.     return 0; /* 0 = errore */
  61. }
  62.  
  63. /*===> crea_matrice() <=======================================================*/
  64.  
  65. void *crea_matrice( size_t qRighe, size_t qColonne, size_t dimEl ) {
  66.     uintptr_t **m = NULL; /* m = matrice */
  67.  
  68.     if( qRighe && qColonne && dimEl ) {
  69.         m = calloc( qRighe, sizeof(uintptr_t) );
  70.  
  71.         if( m ) {
  72.             size_t r; /* contatore delle righe della matrice */
  73.  
  74.             for( r=0; r<qRighe; ++r ) {
  75.                 m[r] = crea_vettore( qColonne, dimEl );
  76.                 if( !m[r] ) break;
  77.             }
  78.  
  79.             if( r < qRighe ) { /* allocazione non completata */
  80.                 for( --r; r<((size_t)-1); --r )
  81.                     distruggi_vettore( m[r], qColonne );
  82.                 free( m );
  83.                 m = NULL;
  84.             }
  85.         }
  86.     }
  87.  
  88.     return m;
  89. }
  90.  
  91. /*==============================================================================
  92. ESEMPI DI USO
  93. ==============================================================================*/
  94.  
  95. void esempio_vettore( void ) {
  96.     const int qe = 10;
  97.     int **vettore = NULL; // doppia indirezione! (qualsiasi tipo)
  98.     int e;
  99.  
  100.     vettore = crea_vettore( qe, sizeof(**vettore) );
  101.  
  102.     if( vettore ) {
  103.         /* "popola" il vettore con valori crescenti */
  104.         for( e=0; e<qe; ++e )
  105.             *vettore[e] = e+1;
  106.  
  107.         /* mostra in console il contenuto del vettore */
  108.         printf( "\nVETTORE\n" );
  109.         for( e=0; e<qe; ++e )
  110.             printf( "%d%c", *vettore[e], e<qe-1?' ':'\n' );
  111.  
  112.         /* libera la memoria allocata */
  113.         if( !distruggi_vettore(vettore,qe) )
  114.             printf( "\nVettore non distrutto.\n" );
  115.     }
  116.     else {
  117.         printf( "\nVettore non creato.\n" );
  118.     }
  119. }
  120.  
  121. void esempio_matrice( void ) {
  122.     const int qr = 5, qc = 5; /* qr = quantita righe; qc = quantita colonne */
  123.     int ***matrice = NULL; // tripla indirezione! (qualsiasi tipo)
  124.     int r, c; /* contatori per le righe e per le colonne */
  125.  
  126.     matrice = crea_matrice( qr, qc, sizeof(***matrice) );
  127.  
  128.     if( matrice ) {
  129.         /* "popola" la matrice con valori dei quali la prima cifra
  130.            rappresenta la riga e la seconda cifra la colonna */
  131.         for( r=0; r<qr; ++r )
  132.             for( c=0; c<qc; ++c )
  133.                 *matrice[r][c] = (r+1)*10 + c+1;
  134.  
  135.         /* mostra in console il contenuto della matrice */
  136.         printf( "\nMATRICE\n" );
  137.         for( r=0; r<qr; ++r )
  138.             for( c=0; c<qc; ++c )
  139.                 printf( "%d%c", *matrice[r][c], c<qc-1?' ':'\n' );
  140.  
  141.         /* libera la memoria allocata */
  142.         if( !distruggi_matrice(matrice,qr,qc) )
  143.             printf( "\nMatrice non distrutta." );
  144.     }
  145.     else {
  146.         printf( "\nMatrice non creata.\n" );
  147.     }
  148. }
  149.  
  150. int main() {
  151.     esempio_vettore();
  152.     esempio_matrice();
  153.  
  154.     printf( "\nPremi \"invio\" per lasciare il programma...\n\n" );
  155.     while( getchar() != '\n' );
  156.  
  157.     return 0;
  158. }



NOTA su stdint.h e uintptr_t
Le funzioni fanno uso del tipo uintptr_t, definito nel file di include stdint.h. La descrizione di uintptr_t che viene data in quel file è: "Integer type capable of holding object pointers".

Commento su distruggi_vettore()
Dato un vettore di puntatori generici che puntano a memoria allocata dinamicamente con malloc, calloc o realloc, la funzione libera la memoria puntata dai singoli puntatori elementi del vettore e quella allocata dall'array di puntatori stesso.
Ne consegue che, in uscita, TUTTA la memoria dinamica risulterà liberata e il puntatore passato come parametro in "vettore" sarà un puntatore non valido.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il puntatore passato nel parametro "vettore" sia indicato come void* per evitare la necessità di un cast esplicito, ci si attende che il parametro sia un puntatore a doppia indirezione (ad esempio, char** o int**).

Commento su crea_vettore()
Crea un vettore di puntatori generici e per ciascun elemento di quel vettore alloca con calloc uno spazio di memoria dinamica pari a dimEl byte.
Ne consegue che, in uscita, sarà allocata una memoria complessiva pari a qEl moltiplicato per la dimensione di un puntatore (nei sistemi a 32 bit, 4 byte) PIU' dimEl*qEl byte.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il valore di ritorno sia indicato come void* per evitare la necessità di un cast esplicito, si tenga presente che la sua reale natura è quella di un puntatore a doppia indirezione, per cui deve essere assegnato a una variabile (ad esempio) di tipo char** o int**.

Commento su distruggi_matrice()
Data una matrice bidimensionale di puntatori generici che puntano a memoria allocata dinamicamente con malloc, calloc o realloc, la funzione libera la memoria puntata dai singoli puntatori elementi della matrice e quella allocata dalla matrice di puntatori stessa.
Ne consegue che, in uscita, TUTTA la memoria dinamica risulterà liberata e il puntatore passato come parametro in "matrice" sarà un puntatore non valido.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il puntatore passato nel parametro "matrice" sia indicato come void* per evitare la necessità di un cast esplicito, ci si attende che il parametro sia un puntatore a tripla indirezione (ad esempio, char*** o int***).

Commento su crea_matrice()
Crea una matrice di puntatori generici e per ciascun elemento di quella matrice alloca con calloc uno spazio di memoria dinamica pari a dimEl byte.
Ne consegue che, in uscita, sarà allocata una memoria complessiva pari a qEl moltiplicato per la dimensione di un puntatore (nei sistemi a 32 bit, 4 byte)
PIU' dimEl*qRighe*qColonne byte.
Se la funzione ha successo restituisce 1, in caso d'errore restituisce 0.
N.B. Nonostante il valore di ritorno sia indicato come void* per evitare la necessità di un cast esplicito, si tenga presente che la sua reale natura è quella di un puntatore a tripla indirezione, per cui deve essere assegnato a una variabile (ad esempio) di tipo char*** o int***.

Ultima modifica effettuata da AldoBaldo il 30/07/2017 alle 13:31


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


Messaggi: 97
Iscritto: 14/04/2017

Segnala al moderatore
Postato alle 17:18
Domenica, 30/07/2017
Salve,,,

ho fatto una prova con questo main()
e mi da qualche problema, ma credo che sia sopratutto perchè non uso bene le tue funzioni.

Codice sorgente - presumibilmente C++

  1. //aggiungere #include <string.h>
  2. // IDE: VS2017 Community
  3. int main(){
  4.  
  5. /*******************************************************************/
  6.     int vett[10];
  7.     char *stringa = calloc( 10, sizeof( char ) );
  8. /*******************************************************************/
  9.     int **vettore = crea_vettore(10,sizeof(**vettore));
  10.     char **string = crea_vettore(10,sizeof(**string));
  11. /*******************************************************************/
  12.  
  13.  
  14.     *vettore[0] = 0;
  15.     *vettore[1] = 10;
  16.     *vettore[2] = 200;
  17.     *vettore[3] = -300;
  18.     *vettore[4] = 400;
  19.  
  20.  
  21.     strcpy( stringa, "provauno" );
  22.  
  23.     *string[0] = 'p';
  24.     *string[1] = 'r';
  25.     *string[2] = 'o';
  26.     *string[3] = 'v';
  27.     *string[4] = 'a';
  28.     *string[5] = ' ';
  29.     *string[6] = 'f';
  30.     *string[7] = 'u';
  31.     *string[8] = 'n';
  32.     *string[9] = '\0';
  33.  
  34.  
  35.     printf( "\n\nLa stringa=%s --- DIM = %d\n", (*string), strlen( *string ) );
  36.     printf( "La *string=%s --- DIM = %d\n", (stringa), strlen( stringa ) );
  37.  
  38.     printf( "Dimensione complessiva di VETT[10] =%d\n", sizeof( vett ) );
  39.     printf( "Dimensione complessiva di *vettore =%d\n\n\n\n", sizeof( *vettore ) );
  40.  
  41.  
  42.     distruggi_vettore( string, 10 );
  43.     distruggi_vettore( vettore, 10 );
  44.     free( stringa );
  45.  
  46.     printf( "La stringa=%s --- DIM = %d\n", (stringa), strlen( stringa ) );
  47.     printf( "La *string=%s --- DIM = %d\n\n", (*string), strlen( *string ) );
  48.  
  49.     printf( "Dimensione complessiva di VETT[10] =%d byte\n", sizeof( vett ) );
  50.     printf( "Dimensione complessiva di *vettore =%d byte\n", sizeof( *vettore ) );
  51.  
  52.  
  53.     return 0;
  54. }



in poche parole:
come faccio ad utilizzarlo per le stringhe??
come posso avere la dimensione totale del vettore?
dopo aver distrutto i vettori..
string da errore se usato, vettore invece lo visualizza correttamente.

Ultima modifica effettuata da Mikelius il 30/07/2017 alle 17:20


"Io ne ho viste cose che voi umani non potreste immaginarvi...."
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 5475
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 19:36
Domenica, 30/07/2017
Se hai scritto quello prima della peperonata, figuriamoci dopo ... :-)


Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 5475
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 19:57
Domenica, 30/07/2017
Testo quotato

come faccio ad utilizzarlo per le stringhe??



Non puoi. Perché i vettori che sono allocati in questo modo dal nostro Paganini (con affetto ...) non hanno gli elementi in indirizzi successivi in memoria, essendo foglie di una lista di puntatori.

Per capirci, un vettore di char allocato in questo modo è fatto

p ->
--- p1 -> 'a'
--- p2 -> 'b'
--- p3 -> 'c'
--- p4 -> '\0'

in modo che gli indirizzi di 'a' 'b' 'c' '\0' non sono successivi (come DEVE essere per una stringa ASCIIZ del C) ed essi sono posizionati, potenzialmente, ovunque in memoria.

In questo modo, fra l'altro, per QUALSIASI vettore allocato in questo modo (anche per gli int o per i double ...) fallirà qualsiasi funzione del tipo strcpy, memcpy, memmove e simili.

Infine, la sizeof non può funzionare perché il tipo di dati su cui agisce è SEMPRE un puntatore (a prescindere dal dato a cui punta) quindi restituirà sempre 4 (per sistemi a 32 bit) oppure 8 (per sistemi a 64 bit).


Ultima modifica effettuata da nessuno il 30/07/2017 alle 19:59


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


Messaggi: 97
Iscritto: 14/04/2017

Segnala al moderatore
Postato alle 20:12
Domenica, 30/07/2017
Testo quotato

Postato originariamente da nessuno:

Non puoi.




leggendo di char** mi era parso che si potesse, o almeno lo volesse rendere possibile. in questo modo, se non si puo usarlo per stringhe, meglio specificarlo nel sorgente (pignoleria MODE OFF).

Per la sizeof(), si lo so che ritorna sempre 4 , 8 o 2568 a seconda la macchina perche ci sono puntatori.
Lo chiedevo proprio per questo. se uno volesse sarere il numero degli elementi dell'array?
con i vettori normali calcolo  sizeof(array)/sizeof(array[0]) , con questo metodo, se il vettore venisse generato in una funzione ed usato in altre?
Usare una struttura:

Codice sorgente - presumibilmente C++

  1. struct
  2. {
  3.    int NElementi;
  4.    int DimTot;
  5.    int **vettore;
  6.    void ***Peperonata;
  7. }



o si rinuncia a certe info e a certe funzioni, oppure  si deve complicare il codice.
L'idea però mi piace.

Ultima modifica effettuata da Mikelius il 30/07/2017 alle 20:15


"Io ne ho viste cose che voi umani non potreste immaginarvi...."
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 5475
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 20:59
Domenica, 30/07/2017
Testo quotato

Postato originariamente da Mikelius:

o 2568



2568 ?

Testo quotato

con i vettori normali calcolo  sizeof(array)/sizeof(array[0])



Neanche con quelli "normali" se lo fai all'interno di una funzione a cui passi il vettore.

Fra l'altro l'inefficienza (dal punto di vista dell'allocazione e della frammentazione di memoria) di questo codice è enorme.


Ultima modifica effettuata da nessuno il 30/07/2017 alle 21:04


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


Messaggi: 346
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 21:58
Domenica, 30/07/2017
Nessuno ha colto perfettamente il punto e lo ha spiegato meglio di come avrei saputo fare io (e non mi stupisce).

L'idea di base è l'allocazione in memoria dinamica di ogni elemento del vettore/matrice, perché... non lo so! :) (comunque la peperonata, che ho cucinato io stesso con le verdure che ho coltivato io stesso era eccelsa!)

Scherzi a parte (si fa per dire), di base volevo che con quelle funzioni si potessero creare vettori/matrici di tipo a piacere con una sola riga di codice, al limite allocando intere strutture a raffica, in un colpo solo, tipo...

Codice sorgente - presumibilmente C

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #include "allocazioni.h"
  5.  
  6. /*==============================================================================
  7. Definisce un quid di stupidaggini che richiedono inutilmente un pacco di memoria.
  8. ==============================================================================*/
  9.  
  10. typedef struct RICCONE {
  11.     char cognome[64];
  12.     char nome[64];
  13.     int id;
  14. } RICCONE;
  15.  
  16. typedef struct TESORETTO {
  17.     char luogo[64];
  18.     double ammontare;
  19. } TESORETTO;
  20.  
  21. typedef struct PACCO {
  22.     RICCONE proprietario;
  23.     TESORETTO soldoni[1024];
  24. } PACCO;
  25.  
  26. /*==============================================================================
  27. Genera una matrice 5*5 di strutture PACCO allocate in memoria dinamica con una
  28. manciata risibile di esilaranti righe di codice.
  29. ==============================================================================*/
  30.  
  31. int main() {
  32.     const int qr = 5, qc = 5; /* qr = quantita' righe; qc = quantita' colonne */
  33.  
  34.     PACCO ***matrice = crea_matrice( qr, qc, sizeof(***matrice) );
  35.  
  36.     if( matrice ) {
  37.         /* fai quel che vuoi con la matrice appena allocata */
  38.  
  39.         /* libera la memoria allocata */
  40.         if( !distruggi_matrice(matrice,qr,qc) )
  41.             printf( "\nMatrice non distrutta." );
  42.     }
  43.     else {
  44.         printf( "\nMatrice non creata.\n" );
  45.     }
  46.  
  47.     printf( "\nPremi \"invio\" per lasciare il programma...\n\n" );
  48.     while( getchar() != '\n' );
  49.  
  50.     return 0;
  51. }


Ultima modifica effettuata da AldoBaldo il 30/07/2017 alle 22:20


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


Messaggi: 346
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 22:20
Domenica, 30/07/2017
Mikelius, volevo evitare di associare strutture "dedicate" a queste funzioni perché le volevo indipendenti da tutto e da tutti, nell'ottica grezza del "fai questa cosa e piantala lì", magari con un semplicissimo copia-e-incolla di codice o un'inclusione senza troppe complicazioni. Cioè, il contenuto delle funzioni è effettivamente un po' cervellotico, da psicopatia dispeptica post-prandiale, però quando si tratta di usarle basta davvero poco (anche se effettivamente non si tratta di vettori dei tipi usati, bensì di vettori di puntatori ai tipi usati). L'utilizzo che avevo in mente riguarda programmi di minime dimensioni, di quelli dove l'immediatezza nella stesura è un valore al quale sacrificare tante finezze.

Per generare il vettore/matrice in una funzione e usarlo in un'altra dovresti semplicemente passare alla seconda funzione anche il numero di elementi/righe-colonne. Per conoscerne le dimensioni dovresti conoscere anche le dimensioni del singolo elemento. Ad esempio, un'eventuale chiamata a mostra_matrice( matrice, qr, qc, sizeof(***matrice) ); nel main ...

Codice sorgente - presumibilmente C/C++

  1. int mostra_pacco( PACCO *p ) {
  2.     int ok = 0;
  3.  
  4.     if( p ) {
  5.         int i;
  6.  
  7.         printf( "id: %04d", p->proprietario.id );
  8.         printf( " %s", p->proprietario.cognome );
  9.         printf( " %s\n", p->proprietario.nome );
  10.  
  11.         for( i=0; i<32; ++i ) {
  12.             printf( "%.2lf euro", p->soldoni[i].ammontare );
  13.             printf( " (%s)\n", p->soldoni[i].luogo );
  14.         }
  15.     }
  16.  
  17.     return ok;
  18. }
  19.  
  20. int mostra_matrice( PACCO ***m, size_t qr, size_t qc, size_t dimEl ) {
  21.     int ok = 0;
  22.  
  23.     if( m && qr && qc ) {
  24.         size_t r, c;
  25.  
  26.         printf( "Dimensioni di un PACCO: %u byte\n", dimEl );
  27.         printf( "Dimensioni della matrice: %u byte\n\n", dimEl*qr*qc );
  28.  
  29.         for( r=0; r<qr; ++r )
  30.             for( c=0; c<qc; ++c )
  31.                 mostra_pacco( m[r][c] );
  32.  
  33.         ok = 1;
  34.     }
  35.  
  36.     return ok;
  37. }


Ultima modifica effettuata da AldoBaldo il 30/07/2017 alle 22:39


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


Messaggi: 346
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 22:44
Domenica, 30/07/2017
nessuno, effettivamente avrei potuto allocare un unico blocco per i dati e altri blocchi per i puntatori, oppure un unico blocco in tutto con un'organizzazione tipo header+dati, però non so se ne sarei stato capace. Tra l'altro: sarebbe effettivamente stato meglio? Varrebbe lo spaccacervello? Se mi dici di sì ci provo!


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