View Full Version : [C]Matrici trasposte
Sto facendo un programma in grado di stampare la trasposta di una matrice data in input. Viene effettuata un'allocazione dinamica per le righe e le colonne, tuttavia al momento dell'inserimento dei numeri, il programma viene terminato inaspettatamente:
#include <stdio.h>
#include <stdlib.h>
void allocate_memory ( int **matrix, int row, int col);
void load_matrix ( int **matrix, int row, int col );
void make_transpose ( int **matrix, int **transpose, int row, int col);
void print_matrix ( int **matrix, int row, int col );
int main (void) {
int **matrix, **transpose; //Matrices
int row=0, column=0;
printf("How many rows for the matrix?\n");
scanf("%d", &row);
printf("How many columns for the matrix?\n");
scanf("%d", &column);
allocate_memory( matrix, row, column);
load_matrix( matrix, row, column);
make_transpose( matrix, transpose, row, column);
print_matrix( matrix, row, column);
print_matrix(transpose, row, column);
return 0;
}
void allocate_memory( int **matrix, int row, int col)
{
int r;
if ((matrix= calloc(row, sizeof(int *)))== NULL) //Memory rows allocation
{
printf("Row allocation error, QUIT");
exit(1);
}
else
{
for (r=0; r < row; r++) //Memory columns allocation
if ((matrix[r]= calloc(col, sizeof(int)))==NULL)
{
printf("Column allocation error, QUIT");
exit(1);
}
}
}
void load_matrix ( int **matrix, int row, int col )
{
int r, c;
for ( r=0; r < row; r++)
for ( c=0; c < col; c++)
{
printf("Insert element %d.%d: ", r, c);
scanf("%d", &matrix[r][c]);
}
}
void make_transpose ( int **matrix, int **transpose, int row, int col)
{
int r, c;
for ( r=0; r < row; r++)
for ( c = 0; c < col; c++)
transpose[r][c] = matrix[c][r];
}
void print_matrix ( int **matrix, int row, int col )
{
int r, c;
for ( r=0; r < row; r++)
{
for ( c =0; c < col; c++)
printf("%d\t", matrix[r][c]);
printf("\n");
}
}
Finchè l'allocazione della memoria era nel main, tutto funzionava perfettamente. Ho optato per metterla in una funzione così anche per la matrice trasposta posso utilizzare la stessa funzione. Tuttavia così facendo allo scanf per caricare i valori dentro la matrice, il programma esce inaspettatamente... Non riesco a venirne a capo...
L'allocazione mi pare sia corretta...
wingman87
14-08-2010, 13:43
La matrice qui è indirizzata tramite un doppio puntatore, se vuoi allocarla in una funzione diversa devi passare l'indirizzo del doppio puntatore, altrimenti le modifiche effettuate su di esso non saranno visibili all'esterno (per via del passaggio di parametri per copia), quindi nella funzione lavorerai con un triplo puntatore.
int **matrix;
allocaMatrice(&matrix);
...
void allocaMatrice(int ***mat){
*mat=(int**)calloc...
...
}
oppure più semplicemente invece di ritornare un int **
tipo:
int** allocate_memory(int rows, int cols);
poi non hai allocato nemmeno la trasposta (e mancano tutte le free)
La matrice qui è indirizzata tramite un doppio puntatore, se vuoi allocarla in una funzione diversa devi passare l'indirizzo del doppio puntatore, altrimenti le modifiche effettuate su di esso non saranno visibili all'esterno (per via del passaggio di parametri per copia), quindi nella funzione lavorerai con un triplo puntatore.
int **matrix;
allocaMatrice(&matrix);
...
void allocaMatrice(int ***mat){
*mat=(int**)calloc...
...
}
Ma c'è modo di allocarla in modo differente? Perchè nell'esempio dell'esercizio la matrice viene allocata con un doppio puntatore ma dentro il main...
oppure più semplicemente invece di ritornare un int **
tipo:
int** allocate_memory(int rows, int cols);
poi non hai allocato nemmeno la trasposta (e mancano tutte le free)
Non mi è mai stato spiegato come si usano i puntatori a funzione, che è quello che mi sembra tu abbia fatto :D
Comunque si lo so che mancano un paio di cose, ma i dettagli li aggiungo quando il programma almeno termina l'esecuzione correttamente. :)
Ma c'è modo di allocarla in modo differente? Perchè nell'esempio dell'esercizio la matrice viene allocata con un doppio puntatore ma dentro il main...
Non mi è mai stato spiegato come si usano i puntatori a funzione, che è quello che mi sembra tu abbia fatto :D
Comunque si lo so che mancano un paio di cose, ma i dettagli li aggiungo quando il programma almeno termina l'esecuzione correttamente. :)
E inoltre non ho nemmeno idea di come si faccia l'allocazione con un triplo puntatore...
Perchè se con due faccio così:
int r;
if ((matrix= calloc(row, sizeof(int *)))== NULL) //Memory rows allocation
{
printf("Row allocation error, QUIT");
exit(1);
}
else
{
for (r=0; r < row; r++) //Memory columns allocation
if ((matrix[r]= calloc(col, sizeof(int)))==NULL)
{
printf("Column allocation error, QUIT");
exit(1);
}
}
Con 3 faccio così??
int r;
if ((*matrix= calloc(row, sizeof(int **)))== NULL) //Memory rows allocation
{
printf("Row allocation error, QUIT");
exit(1);
}
else
{
for (r=0; r < row; r++) //Memory columns allocation
if ((*matrix[r]= calloc(col, sizeof(int *)))==NULL)
{
printf("Column allocation error, QUIT");
exit(1);
}
}
Ma c'è modo di allocarla in modo differente? Perchè nell'esempio dell'esercizio la matrice viene allocata con un doppio puntatore ma dentro il main...
Non mi è mai stato spiegato come si usano i puntatori a funzione, che è quello che mi sembra tu abbia fatto :D
Comunque si lo so che mancano un paio di cose, ma i dettagli li aggiungo quando il programma almeno termina l'esecuzione correttamente. :)no no, non c'entrano nulla i puntatori a funzione, quello che dico è che puoi fare una funzione che ritorna un int ** invece che un void, in cui quello che fai è allocare la matrice esattamente come lo fai nella tua funzione, ma alla fine ritorni il valore del puntatore alla matrice.. tipo
int** allocaMatrice(int r, int c) {
int **matrix = malloc(sizeof(int*) * r);
if (matrix) {
int i;
for (i = 0; i < r; i++) {
matrix[i] = malloc(sizeof(int) * c);
if (!matrix[i]) {
int j;
for (j = 0; j < i; j++)
free(matrix[j]);
free(matrix);
return NULL;
}
}
}
return matrix;
}
così nel main ti basta fare
int **matrix = allocaMatrice(r, c);
e chiaramente controllare che non abbia ritornato NULL per gestire l'errore.. in pratica puoi riutilizzare la tua funzione, solo cambiare il valore di ritorno e togliere il parametro matrix che passi che risulta inutile
inoltre nel codice che hai scritto
if ((*matrix= calloc(row, sizeof(int **)))== NULL)
[...]
if ((*matrix[r]= calloc(col, sizeof(int *)))==NULL)
devi allocare un array di dimensione row di int* (non di int**), e per ogni riga un array di int (non di int*) di dimensione col, questo non cambia
no no, non c'entrano nulla i puntatori a funzione, quello che dico è che puoi fare una funzione che ritorna un int ** invece che un void, in cui quello che fai è allocare la matrice esattamente come lo fai nella tua funzione, ma alla fine ritorni il valore del puntatore alla matrice.. tipo
int** allocaMatrice(int r, int c) {
int **matrix = malloc(sizeof(int*) * r);
if (matrix) {
int i;
for (i = 0; i < r; i++) {
matrix[i] = malloc(sizeof(int) * c);
if (!matrix[i]) {
int j;
for (j = 0; j < i; j++)
free(matrix[j]);
free(matrix);
return NULL;
}
}
}
return matrix;
}
così nel main ti basta fare
int **matrix = allocaMatrice(r, c);
e chiaramente controllare che non abbia ritornato NULL per gestire l'errore.. in pratica puoi riutilizzare la tua funzione, solo cambiare il valore di ritorno e togliere il parametro matrix che passi che risulta inutile
Ah, non sapevo si potessero ritornare puntatori... Non mi era mai stato detto :muro:
Poi quando inserisci:
if(matrix)
Oppure
if (!matrix[i]) Che significa? inserire il nome della sola variabile fa controllare che questa sia TRUE? ( in questo caso che non sia vuota? )
{ Ovviamente con ! fa il NOT, questo lo so }
Per il resto, provo e ti faccio sapere.
inoltre nel codice che hai scritto
if ((*matrix= calloc(row, sizeof(int **)))== NULL)
[...]
if ((*matrix[r]= calloc(col, sizeof(int *)))==NULL)
devi allocare un array di dimensione row di int* (non di int**), e per ogni riga un array di int (non di int*) di dimensione col, questo non cambia
Eh ma li ho aggiunti perchè pensavo che aggiungendo un puntatore agli altri due, si dovesse aggiungere un asterisco anche ad ognuno degli int :D
EDIT: Ci sono riuscito!! Però non capisco questa parte di codice che hai scritto tu:
if (!matrix[i]) {
int j;
for (j = 0; j < i; j++)
free(matrix[j]);
free(matrix);
return NULL;
metti caso che riesca ad allocare il vettore di int* (vale a dire le righe) e poi magari anche qualche colonna, ma poi malloc cominci a restituire NULL, devi deallocare tutto quello che hai allocato prima di restituire NULL, sennò hai un memory leak
sottovento
17-08-2010, 09:14
Dovresti allocare memoria anche per transpose, cosi' da evitare crash
metti caso che riesca ad allocare il vettore di int* (vale a dire le righe) e poi magari anche qualche colonna, ma poi malloc cominci a restituire NULL, devi deallocare tutto quello che hai allocato prima di restituire NULL, sennò hai un memory leak
Ah, ho capito... Da come aveva spiegato il professore, avevo capito che malloc ( o calloc ) controlla prima se c'è ( nel totale ) memoria sufficiente per allocare ciò che gli serve, se si inizia l'allocazione, se no restituisce NULL. Non avevo capito che questo controllo viene fatto in corso d'opera, ovvero:
Controllo
Alloco
Controllo
Alloco
ecc...
Dovresti allocare memoria anche per transpose, cosi' da evitare crash
Si bhe, ho fatto una funzione a parte proprio per non dover scrivere lo stesso codice due volte! :D
Per la deallocazione ho fatto così, è corretto? Non mi restituisce errori e il programma funziona correttamente, però non sono certo sia giusto perchè ho pensato si possa presentare lo stesso problema che avevo durante il tentativo di fare una funzione solo per l'allocazione:
Il prototipo:
void deallocate_memory ( int **matrix, int **transpose, int row, int col);
Nel main.c:
deallocate_memory(matrix, transpose, row, column);
Dentro la funzione deallocate_memory:
void deallocate_memory ( int **matrix, int **transpose, int row, int col)
{
int r;
for ( r=0; r < row; r++)
{
free(matrix[r]);
free(transpose[r]);
}
free(matrix);
free(transpose);
}
Poi quando inserisci:
if(matrix)
Oppure
if (!matrix[i]) Che significa? inserire il nome della sola variabile fa controllare che questa sia TRUE? ( in questo caso che non sia vuota? )
{ Ovviamente con ! fa il NOT, questo lo so }
Viene valutata l'espressione all'interno delle parentesi,la quale è FALSE se l'espressione restituisce 0, TRUE per ogni altro numero. Questa regola però non è detto che valga per tutti i linguaggi di programmazione.
Per il resto la tua deallocazione è corretta.
sottovento
17-08-2010, 12:08
void deallocate_memory ( int **matrix, int **transpose, int row, int col)
{
int r;
for ( r=0; r < row; r++)
{
free(matrix[r]);
free(transpose[r]);
}
free(matrix);
free(transpose);
}
E' corretta, ma non molto conveniente. Se infatti deallocassi una sola matrice alla volta, avresti un'implementazione piu' semplice (si lo so, e' un esempio e la semplicita' e' solo questione di due righe) e la potresti usare dappertutto, anche nel caso che nel tuo programma ci sia una terza matrice contenente la somma ed una quarta contenente la differenza, ed una quinta....
Osservazione giustissima... Ho fatto inoltre caso al fatto che in quel modo c'è un errore nel caso in cui la matrice non sia quadrata, dato che le righe della matrice sono in realtà le colonne della trasposta, quindi non puoi operare nello stesso ciclo...
Ah, ho capito... Da come aveva spiegato il professore, avevo capito che malloc ( o calloc ) controlla prima se c'è ( nel totale ) memoria sufficiente per allocare ciò che gli serve, se si inizia l'allocazione, se no restituisce NULL. Non avevo capito che questo controllo viene fatto in corso d'opera, ovvero:
Controllo
Alloco
Controllo
Alloco
ecc...certo che è così, ma quando fai la prima malloc in quella funzione nessuno ha idea del fatto che hai intenzione di allocare anche le colonne.. lì non c'è una malloc, ma un numero variabile.. una qualsiasi di queste malloc potrebbe fallire
ps
(!matrix[i]) equivale a (matrix[i] == 0) cioè a (matrix[i] == NULL) visto che NULL è definito come 0x0
Viene valutata l'espressione all'interno delle parentesi,la quale è FALSE se l'espressione restituisce 0, TRUE per ogni altro numero. Questa regola però non è detto che valga per tutti i linguaggi di programmazione.
Chiaro, grazie! Per ora mi concentro sul C che ne ho da imparare :D
E' corretta, ma non molto conveniente. Se infatti deallocassi una sola matrice alla volta, avresti un'implementazione piu' semplice (si lo so, e' un esempio e la semplicita' e' solo questione di due righe) e la potresti usare dappertutto, anche nel caso che nel tuo programma ci sia una terza matrice contenente la somma ed una quarta contenente la differenza, ed una quinta....
Si lo so che dal punto di vista funzionale sarebbe stato meglio avere una funzione che deallochi una singola matrice, così da poterla riutilizzare, però non mi serviva in questo programma...
Osservazione giustissima... Ho fatto inoltre caso al fatto che in quel modo c'è un errore nel caso in cui la matrice non sia quadrata, dato che le righe della matrice sono in realtà le colonne della trasposta, quindi non puoi operare nello stesso ciclo...
Si, non ci avevo pensato... Infatti ho provato ora e da errore... :muro:
certo che è così, ma quando fai la prima malloc in quella funzione nessuno ha idea del fatto che hai intenzione di allocare anche le colonne.. lì non c'è una malloc, ma un numero variabile.. una qualsiasi di queste malloc potrebbe fallire
Ok, capito!
ps
(!matrix[i]) equivale a (matrix[i] == 0) cioè a (matrix[i] == NULL) visto che NULL è definito come 0x0 Bene! ;)
Si lo so che dal punto di vista funzionale sarebbe stato meglio avere una funzione che deallochi una singola matrice, così da poterla riutilizzare, però non mi serviva in questo programma...
Come non ti serve, è proprio la soluzione al tuo problema (quello delle matrici rettangolari) invece...
void deallocate_memory ( int **matrix, int **transpose, int row, int col)
{
deallocate_matrix(matrix,row);
deallocate_matrix(transpose,col);
}
void deallocate_matrix(int **matrix, int row)
{
int r;
for ( r=0; r < row; r++)
{
free(matrix[r]);
}
free(matrix);
}
E' un codice scritto così su due piedi quindi potrebbe essermi sfuggito qualcosa, però logicamente è così che si potrebbe risolvere il problema...
Come non ti serve, è proprio la soluzione al tuo problema (quello delle matrici rettangolari) invece...
void deallocate_memory ( int **matrix, int **transpose, int row, int col)
{
deallocate_matrix(matrix,row);
deallocate_matrix(transpose,col);
}
void deallocate_matrix(int **matrix, int row)
{
int r;
for ( r=0; r < row; r++)
{
free(matrix[r]);
}
free(matrix);
}
E' un codice scritto così su due piedi quindi potrebbe essermi sfuggito qualcosa, però logicamente è così che si potrebbe risolvere il problema...
Si si hai ragione. Ho poi ricontrollato bene e testato il programma e in effetti dava dei problemi ( crash e vari ), questo perchè mi ero scordato che se si fa la trasposta, la grandezza della riga di questa corrisponde alla lunghezza della matrice originale.. Io invece nel programma passavo sempre righe e colonne senza invertirle. Infatti poi con matrici non quadrate venivano fuori dei casini.
Poi anche per la deallocazione era necessario fare una funzione generica così da poter invertire righe e colonne per la trasposta.
Devo dire che sto esercizio è servito come il pane per capire bene come funzionano ste matrici allocate dinamicamente.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.