07/04/17 17:00
AldoBaldo
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ì:
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?
Dov'è la falla nel mio ragionamento? Cosa sbaglio?
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ì:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> typedef struct struct_esempio_t { char str1[24]; char str2[32]; } esempio_t; void **crea_matrice( uint32_t dim_el, uint32_t n_righe, uint32_t n_colonne ); void distruggi_matrice( void ***matrice, uint32_t n_righe ); int main() { const uint32_t kQRighe=4, kQColonne=3; // dimensioni della matrice esempio_t **matrice = (esempio_t**) crea_matrice( sizeof(esempio_t), kQRighe, kQColonne ); if( matrice != NULL ) { uint32_t r, c; // contatori char buff[32]; // ausialiario, per comporre le stringhe d'esempio // "popola" la matrice for( r=0; r<kQRighe; ++r ) { for( c=0; c<kQColonne; ++c ) { sprintf( buff, "(r%dc%d) str1", r, c ); strcpy( matrice[r][c].str1, buff ); sprintf( buff, "(r%dc%d) str2", r, c ); strcpy( matrice[r][c].str2, buff ); } } // mostra il contenuto della matrice for( r=0; r<kQRighe; ++r ) { for( c=0; c<kQColonne; ++c ) { printf( "riga %d, colonna %d: %s, %s\n", r, c, matrice[r][c].str1, matrice[r][c].str2 ); } } // libera la matrice e ne annulla il puntatore distruggi_matrice( (void***) &matrice, kQRighe ); } return 0; } void **crea_matrice( uint32_t dim_el, uint32_t n_righe, uint32_t n_colonne ) { // alloca i puntatori alle righe, tutti NULL void **matrice = calloc( n_righe, sizeof(void*) ); if( matrice != NULL ) { // se l'allocazione e' riuscita... uint32_t r; // contatore for( r=0; r<n_righe; ++r ) { // per ogni riga // alloca in blocco tutte le colonne sulla riga matrice[r] = calloc( n_colonne, dim_el*n_colonne ); if( matrice[r] == NULL ) { // se l'allocazione NON e' riuscita... distruggi_matrice( &matrice, r ); break; } } } return matrice; // NULL in caso d'errore } void distruggi_matrice( void ***matrice, uint32_t n_righe ) { if( matrice != NULL ) { void **m = *matrice; // per praticita' di lettura if( m != NULL ) { // se la matrice esiste... uint32_t r; // contatore delle righe for( r=0; r<n_righe; ++r ) // per ogni riga della matrice... free( m[r] ); // libera in blocco tutte le colonne sulla riga free( m ); // libera l'array dei puntatori alle righe *matrice = NULL; // annulla il puntatore alla matrice } } }
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?
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 07/04/17 17: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.