Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - Matrice dinamica e void* in C - cast o non cast?
Forum - C/C++ - Matrice dinamica e void* in C - cast o non cast?

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


Messaggi: 700
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 19:00
Venerdì, 07/04/2017
Per cominciare, un saluto a te che leggi. Indi...

Una cosa che mi capita di usare piuttosto spesso nei miei programmini in C sono le matrici bidimensionali, quelle sul genere di matrice[nRighe][nColonne], per intendersi. Per varie ragioni trovo a volte utile allocarle dinamicamente, ed è un compito che dopo un po' diventa noioso. Per questo ho pensato di provare a mettere insieme una funzione da poter copia-e-incollare ogni qualvolta mi dovesse servire. Dovendo fare in modo che sia adattabile a qualsiasi tipo di dati, ho pensato di fare in modo che restituisca un void**, così:

Codice sorgente - presumibilmente C++

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdint.h>
  5.  
  6. typedef struct struct_esempio_t {
  7.     char str1[24];
  8.     char str2[32];
  9. } esempio_t;
  10.  
  11. void **crea_matrice(
  12.     uint32_t dim_el, uint32_t n_righe, uint32_t n_colonne );
  13. void distruggi_matrice( void ***matrice, uint32_t n_righe );
  14.  
  15. int main() {
  16.     const uint32_t kQRighe=4, kQColonne=3; // dimensioni della matrice
  17.  
  18.     esempio_t **matrice =
  19.         (esempio_t**) crea_matrice( sizeof(esempio_t), kQRighe, kQColonne );
  20.  
  21.     if( matrice != NULL ) {
  22.         uint32_t r, c; // contatori
  23.         char buff[32]; // ausialiario, per comporre le stringhe d'esempio
  24.  
  25.         // "popola" la matrice
  26.         for( r=0; r<kQRighe; ++r ) {
  27.             for( c=0; c<kQColonne; ++c ) {
  28.                 sprintf( buff, "(r%dc%d) str1", r, c );
  29.                 strcpy( matrice[r][c].str1, buff );
  30.                 sprintf( buff, "(r%dc%d) str2", r, c );
  31.                 strcpy( matrice[r][c].str2, buff );
  32.             }
  33.         }
  34.  
  35.         // mostra il contenuto della matrice
  36.         for( r=0; r<kQRighe; ++r ) {
  37.             for( c=0; c<kQColonne; ++c ) {
  38.                 printf( "riga %d, colonna %d: %s, %s\n",
  39.                         r, c, matrice[r][c].str1, matrice[r][c].str2 );
  40.             }
  41.         }
  42.  
  43.         // libera la matrice e ne annulla il puntatore
  44.         distruggi_matrice( (void***) &matrice, kQRighe );
  45.     }
  46.     return 0;
  47. }
  48.  
  49. void **crea_matrice(
  50.     uint32_t dim_el, uint32_t n_righe, uint32_t n_colonne ) {
  51.  
  52.     // alloca i puntatori alle righe, tutti NULL
  53.     void **matrice = calloc( n_righe, sizeof(void*) );
  54.  
  55.     if( matrice != NULL ) { // se l'allocazione e' riuscita...
  56.         uint32_t r; // contatore
  57.  
  58.         for( r=0; r<n_righe; ++r ) { // per ogni riga
  59.             // alloca in blocco tutte le colonne sulla riga
  60.             matrice[r] = calloc( n_colonne, dim_el*n_colonne );
  61.  
  62.             if( matrice[r] == NULL ) { // se l'allocazione NON e' riuscita...
  63.                 distruggi_matrice( &matrice, r );
  64.                 break;
  65.             }
  66.         }
  67.     }
  68.  
  69.     return matrice; // NULL in caso d'errore
  70. }
  71.  
  72. void distruggi_matrice( void ***matrice, uint32_t n_righe ) {
  73.     if( matrice != NULL ) {
  74.         void **m = *matrice; // per praticita' di lettura
  75.  
  76.         if( m != NULL ) { // se la matrice esiste...
  77.             uint32_t r; // contatore delle righe
  78.  
  79.             for( r=0; r<n_righe; ++r ) // per ogni riga della matrice...
  80.                 free( m[r] ); // libera in blocco tutte le colonne sulla riga
  81.  
  82.             free( m ); // libera l'array dei puntatori alle righe
  83.             *matrice = NULL; // annulla il puntatore alla matrice
  84.         }
  85.     }
  86. }



L'idea pare funzionare, anche se mi aspetto che qualcuno più pratico di me possa indicarmi qualche errore.

La questione che vorrei porre, però, non riguarda gli (eventuali) errori, bensì la ragione per la quale il compilatore (mingw) mi impone di effettuare il cast di tipo da void** a esempio_t** e viceversa. So che quel cast sarebbe obbligatorio (almeno tanto quanto è deprecato) nel caso di un programma in C++, ma il mio esempio è un programmino in C... in C, un puntatore a doppia indirezione di tipo void** non dovrebbe essere liberamente "intercambiabile" con un puntatore a doppia indirezione di qualsiasi altro tipo anche senza fare il cast? void** non è utilizzabile come puntatore generico almeno quanto il void* restituito (per dire) da calloc() che può essere assegnato a un puntatore di qualsiasi tipo senza cast?

Codice sorgente - presumibilmente C/C++

  1. int *ptrNum = calloc( 1, sizeof(*ptrNum) ); // da void* a int* senza cast!



Dov'è la falla nel mio ragionamento? Cosa sbaglio?

Ultima modifica effettuata da AldoBaldo il 07/04/2017 alle 19:02


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.
PM Quote
Avatar
lumo (Member)
Expert


Messaggi: 449
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 19:07
Venerdì, 07/04/2017
Dovrebbe essere proprio come dici tu, prova a mettere tutto in un
Codice sorgente - presumibilmente C/C++

  1. #ifdef __cplusplus
  2.  
  3. #endif



È una macro che tutti i compilatori C++ definiscono (mentre quelli C no) quindi dovrebbe dirti subito se per caso stai compilando con mingw++.

P.S.: se posso fare solo un appunto al codice, i parametri della funzione li vedrei bene come size_t

PM Quote
Avatar
Template (Member)
Pro


Messaggi: 177
Iscritto: 09/12/2015

Segnala al moderatore
Postato alle 19:13
Venerdì, 07/04/2017
Ponendo che la funzione che hai scritto sia corretta (non ho tempo per controllarla):

Il tipo void ** non è equivalente a void *: sono entrambi puntatori, ma il primo è del tipo "puntatore ad un puntatore (che a sua volta punta a void)", mentre il secondo è del tipo "puntatore a void".
Questo vuol dire che per il tipo void ** si applica quanto statuito nelle sezioni 6.6 e 6.8 del Reference manual del linguaggio C inserito nel testo di Kernighan-Ritchie (seconda edizione, appendice A), ovvero non è possibile un cast implicito ed inoltre a seconda dell'implementazione potrebbero non essere possibili alcuni cast espliciti.

EDIT: vedesi anche la risposta data qui: http://stackoverflow.com/questions/25427587/void-a-generic ...

Ultima modifica effettuata da Template il 07/04/2017 alle 19:22
PM Quote
Avatar
lumo (Member)
Expert


Messaggi: 449
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 19:22
Venerdì, 07/04/2017
Ok avevo letto troppo velocemente, in effetti con i puntatori multipli il discorso cambia e Template dice giusto (infatti mi pareva strano che calloc nel tuo codice funzionasse e altrove no).

PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 700
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 21:37
Venerdì, 07/04/2017
Testo quotato

Postato originariamente da lumo:

Dovrebbe essere proprio come dici tu, prova a mettere tutto in un
Codice sorgente - presumibilmente C/C++

  1. #ifdef __cplusplus
  2.  
  3. #endif




Ho provato: ne deriva questo...

||=== Build: Debug in Matrice (compiler: GNU GCC Compiler) ===|
C:\Program Files\CodeBlocks\MinGW\bin\..\lib\gcc\mingw32\4.9.2\..\..\..\libmingw32.a(main.o):main.c:(.text.startup+0xa7)||undefined reference to `WinMain@16'|
||error: ld returned 1 exit status|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 2 second(s)) ===|


...dal che deduco che il programma venga effettivamente compilato come C e non come C++.

Ora provo a "sondare" la questione della doppia indirezione, poi "ritorno".


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.
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 700
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 21:58
Venerdì, 07/04/2017
Testo quotato

Postato originariamente da lumo:
se posso fare solo un appunto al codice, i parametri della funzione li vedrei bene come size_t



Puoi fare tutti gli appunti che vuoi, per me è grasso che cola!

Intendi così...

void **crea_matrice( size_t dim_el, uint32_t n_righe, uint32_t n_colonne );

... o così?

void **crea_matrice( size_t dim_el, size_t n_righe, size_t n_colonne );

(a me sembra più sensata la prima)


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.
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 700
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 22:08
Venerdì, 07/04/2017
Ho letto l'intera pagina sulla questione del void** e direi che fa strame della mia funzione crea_matrice(). Dovrò pensare a qualcos'altro. Grazie a Template per avermi ricondotto sulla "retta via", tirandomi coi piedi per terra. Però non mi arrendo... ancora! :heehee:


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.
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 700
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 22:35
Venerdì, 07/04/2017
Una soluzione banale potrebbe essere quella di "legare" ogni particolare funzione crea_matrice a un determinato tipo, in modo esplicito, magari apponendo il nome del tipo al nome della funzione. Con una soluzione del genere basta copiare e incollare la funzione d'esempio e effettuare una sostituzione di tutte le sottostringhe del codice che identificano il tipo in questione. Con un tipo chiamato TIPO_ES, ad esempio...

Codice sorgente - presumibilmente C++

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdint.h>
  5.  
  6. typedef struct struct_TIPO_ES { // TIPO_ES = tipo esempio
  7.     int r, c;
  8.     char str[32];
  9. } TIPO_ES;
  10.  
  11. TIPO_ES **crea_matrice_TIPO_ES( uint32_t qRighe, uint32_t qColonne );
  12. void distruggi_matrice_TIPO_ES( TIPO_ES ***matrice, uint32_t qRighe );
  13.  
  14. int main() {
  15.     const uint32_t kQRighe=4, kQColonne=3; // dimensioni della matrice
  16.  
  17.     TIPO_ES **matrice = crea_matrice_TIPO_ES( kQRighe, kQColonne );
  18.  
  19.     if( matrice != NULL ) {
  20.         uint32_t r, c; // contatori
  21.         char buff[32]; // ausialiario, per comporre le stringhe d'esempio
  22.  
  23.         // "popola" la matrice
  24.         for( r=0; r<kQRighe; ++r ) {
  25.             for( c=0; c<kQColonne; ++c ) {
  26.                 matrice[r][c].r = r;
  27.                 matrice[r][c].c = c;
  28.                 sprintf( buff, "r%dc%d", matrice[r][c].r, matrice[r][c].c );
  29.                 strcpy( matrice[r][c].str, buff );
  30.             }
  31.         }
  32.  
  33.         // mostra il contenuto della matrice
  34.         for( r=0; r<kQRighe; ++r ) {
  35.             for( c=0; c<kQColonne; ++c ) {
  36.                 printf( "TIPO_ES.r = %d   TIPO_ES.c = %d   TIPO_ES.str = %s\n",
  37.                         matrice[r][c].r, matrice[r][c].c, matrice[r][c].str );
  38.             }
  39.  
  40.             printf( "\n" );
  41.         }
  42.  
  43.         // libera la matrice e ne annulla il puntatore
  44.         distruggi_matrice_TIPO_ES( &matrice, kQRighe );
  45.     }
  46.     return 0;
  47. }
  48.  
  49. TIPO_ES **crea_matrice_TIPO_ES( uint32_t qRighe, uint32_t qColonne ) {
  50.     // alloca i puntatori alle righe, tutti NULL
  51.     TIPO_ES **matrice = calloc( qRighe, sizeof(TIPO_ES*) );
  52.  
  53.     if( matrice != NULL ) { // se l'allocazione e' riuscita...
  54.         uint32_t r; // contatore
  55.  
  56.         for( r=0; r<qRighe; ++r ) { // per ogni riga
  57.             // alloca in blocco tutte le colonne sulla riga
  58.             matrice[r] = calloc( qColonne, sizeof(*matrice[r])*qColonne );
  59.  
  60.             if( matrice[r] == NULL ) { // se l'allocazione NON e' riuscita...
  61.                 distruggi_matrice_TIPO_ES( &matrice, r );
  62.                 break;
  63.             }
  64.         }
  65.     }
  66.  
  67.     return matrice; // NULL in caso d'errore
  68. }
  69.  
  70. void distruggi_matrice_TIPO_ES( TIPO_ES ***matrice, uint32_t qRighe ) {
  71.     if( matrice != NULL ) {
  72.         TIPO_ES **m = *matrice; // per praticita' di lettura
  73.  
  74.         if( m != NULL ) { // se la matrice esiste...
  75.             uint32_t r; // contatore delle righe
  76.  
  77.             for( r=0; r<qRighe; ++r ) // per ogni riga della matrice...
  78.                 free( m[r] ); // libera in blocco tutte le colonne sulla riga
  79.  
  80.             free( m ); // libera l'array dei puntatori alle righe
  81.             *matrice = NULL; // annulla il puntatore alla matrice
  82.         }
  83.     }
  84. }



In questo modo non serve neanche comunicare alla funzione le dimensioni del tipo dei dati, perché può ricavarle da sola tramite sizeof. Inoltre non rimane traccia di cast di alcun genere, che non è certo un male. Suggerimenti?

P.S. Se solo il C avesse contezza dell'overload di funzioni... magari in qualche sua incarnazione più recente di quella che conosco io (ormai almeno ventennale) si può usare l'overload o è roba solo per il C++ com'era "ai miei tempi"?

Ultima modifica effettuata da AldoBaldo il 07/04/2017 alle 22:40


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.
PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 23:06
Venerdì, 07/04/2017
Scusa la mia ignoranza, ma un qualcosa tipo

Codice sorgente - presumibilmente C++

  1. void* crea( int Size_Tipo_in_Byte, int Col, int Rig )
  2. {
  3.     void *r = NULL;
  4.     r = malloc( Size_Tipo_in_Byte*Col*Rig );
  5.     return r;
  6. }


Cioè avendo la dimensione di un singolo elemento e righe/Colonne, ti allochi la dimensione necessaria per il tutto, poi nel mai utilizzi il puntatore come un vettore

Codice sorgente - presumibilmente C++

  1. #define RIGHE 3
  2. #define COLONNE 2
  3. int main(){
  4.  
  5.     int R, C;
  6.     char *p ;
  7.     int  *m = NULL;
  8.     int i =0 ;
  9.     p = crea(sizeof(char),RIGHE,COLONNE);
  10.  
  11.     strcpy( p, "ProvaMat" );
  12.  
  13.     for(R=0;R<RIGHE;R++ )
  14.         for(C=0;C<COLONNE;C++ )
  15.     printf( "%c", p[(R*COLONNE)+C] );
  16.  
  17.     printf( "---\n\n\n" );
  18.    m=crea(sizeof(int),RIGHE,COLONNE);
  19.  
  20.        for(R=0;R<RIGHE;R++ )
  21.         for(C=0;C<COLONNE;C++ )
  22.             m[(R*COLONNE) + C] = i++;
  23.  
  24.     for(R=0;R<RIGHE;R++ )
  25.         for(C=0;C<COLONNE;C++ )
  26.     printf( "%d", m[(R*COLONNE)+C] );
  27.  
  28.     return 0;
  29. }


Il problema potrebbe nascere con le stringhe


Ultima modifica effettuata da il 07/04/2017 alle 23:07
PM Quote
Pagine: [ 1 2 ] Precedente | Prossimo