Visualizza la versione completa : [C] problema con stampa_matrice allocata dinamicamente
mbistato
18-08-2014, 12:55
Ciao a tutti,
sto cercando di scrivere un programma in C che riesca a caricare una matrice da file e stamparla a video.
Il codice finora scritto è il seguente:
#include <stdio.h>#include <stdlib.h>
#define MAX_C 50
#define MAX_R 50
typedef char** matrix;
int R, C;
matrix matricevip;
void leggi_matrice(const char* nomefile, matrix m, int *r, int *c);
void stampa_matrice(matrix m, int r, int c);
int main(int argc, char *argv[])
{
char* nomefile = argv[1];
if(argc!=2)
{
printf("Numero di argomenti inseriti errato!\n");
exit(EXIT_FAILURE);
}
else
{
//metto la & prima delle variabili per indicare che sto effetuando un passaggio di parametri per riferimento (ovvero per indirizzo)
//e non per valore
leggi_matrice(nomefile, matricevip, &R, &C);
stampa_matrice(matricevip, R, C);
}
return 0;
}
void leggi_matrice(const char* nomefile, matrix m, int *r, int *c)
{
//**matricevip è un doppio puntatore(un puntatore per le righe e uno per le colonne)
char buffer[MAX_C], *res, temp;
int i = 0, j = 0;
FILE *ptrvip = fopen(nomefile, "r");
if(nomefile == NULL)
printf("Impossibile aprire il file %s\n", nomefile);
else
{
printf("File %s letto correttamente!\n\n", nomefile);
//Acquisizione numero di colonne
res = fgets(buffer, MAX_C, ptrvip);
while(res[i] != '\0')
{
*c = *c +1;
i++;
}
C = *c;
//Riposiziono il cursore all'inizio del file
fseek(ptrvip, 0, 0);
//Conto il numero della righe
while(1)
{
res = fgets(buffer, MAX_R, ptrvip);
if(res == NULL)
break;
*r = *r + 1;
}
R = *r;
//Allocazione dinamica della memoria
m = (char**) malloc (*c * sizeof(char*));//alloco lo spazio per le colonne
for(i = 0; i < *c; i++)
m[i] = (char*) malloc (*r * sizeof(char));//alloco lo spazio per le righe
//Riposiziono il cursore all'inizio del file
fseek(ptrvip, 0, 0);
//Caricamento della matrice da file
while(!feof(ptrvip))
{
for(i = 0; i < *r; i++)
for(j = 0; j < *c; j++)
{
fscanf(ptrvip, "%c", &temp);
if(temp != '\0')
m[i][j] = temp;
}
}
fclose(ptrvip);
}
}
void stampa_matrice(matrix m, int r, int c)
{
printf("\n");
int i,j;
for (i=0;i<r;++i)
{
for (j=0;j<c-1;++j)
printf("%c",m[i][j]);
printf("\n");
}
printf("\n");
}
La lettura del file e il caricamento della matrice va a buon fine, ma il programma smette di funzionare non appena richiamo la funzione "stampa_matrice".
Qualcuno è in grado di capire il perchè?
Grazie
int R, C;
Dichiari le variabili, ma non le inizializzi a 0, quindi le incrementi e poi utilizzi, sicuramente il loro valore non è quello che ti aspetti, probabile che nella funzione stampa superi le dimensioni della matrice.
fammi sapere se l'errore era questo.
mbistato
18-08-2014, 14:41
Ho inizializzato le variabili ma il problema persiste.
Il contenuto del mio file è il seguente:
JohnLennonXXXXX
PaulMcCartneyXX
GeorgeHarrisonX
RingoStarrXXXXX
StuartSutcliffe
Ho fatto una verifica facendomi stampare la matrice man mano che viene caricata modificando il seguente pezzo di codice:
while(!feof(ptrvip))
{
for(i = 0; i < R; i++)
for(j = 0; j < C; j++)
{
fscanf(ptrvip, "%c", &temp);
if(temp != '\0'){
m[i][j] = temp;
printf("%c", m[i][j]);
}
}
printf("\n");
}
Il risultato della stampa è:
JohnLennonXXXXX
PaulMcCartneyXX
GeorgeHarrisonX
RingoStarrXXXXX
StuartSutcliffee
In pratica mi stampa una "e" in più al fondo.
Nel codice esistono tanti problemi "minori" (ad esempio, che senso ha controllare con feof la fine del file se sai esattamente quanti caratteri leggere? Oppure controllare if(nomefile == NULL) invece di controllare l'handle restituito dalla fopen).
Ma la questione che genera l'errore è un'altra. Alla funzione leggi_matrice devi passare un puntatore a matrix (ovvero un puntatore triplo)
matrix *m
altrimenti non potrai mai modificarlo all'interno della funzione.
mbistato
19-08-2014, 08:21
Nel codice esistono tanti problemi "minori" (ad esempio, che senso ha controllare con feof la fine del file se sai esattamente quanti caratteri leggere? Oppure controllare if(nomefile == NULL) invece di controllare l'handle restituito dalla fopen).
Il testo del problema dice che non si conosce a priori la dimensione del file, ecco il motivo per cui uso feof.
Ma la questione che genera l'errore è un'altra. Alla funzione leggi_matrice devi passare un puntatore a matrix (ovvero un puntatore triplo)
matrix *m
altrimenti non potrai mai modificarlo all'interno della funzione.
Ho provato ma ottengo errori quando alloco lo spazio in memoria.
#include <stdio.h>
#include <stdlib.h>
#define MAX_C 50
#define MAX_R 50
typedef char** matrix;
int R, C;
matrix *matricevip;
void leggi_matrice(const char* nomefile, matrix *m, int *r, int *c);
void stampa_matrice(matrix *m, int r, int c);
int main(int argc, char *argv[])
{
char* nomefile = argv[1];
if(argc!=2)
{
printf("Numero di argomenti inseriti errato!\n");
exit(EXIT_FAILURE);
}
else
{
//metto la & prima delle variabili per indicare che sto effetuando un passaggio di parametri per riferimento (ovvero per indirizzo)
//e non per valore
leggi_matrice(nomefile, matricevip, &R, &C);
stampa_matrice(matricevip, R, C);
}
return 0;
}
void leggi_matrice(const char* nomefile, matrix *m, int *r, int *c)
{
//**matricevip è un doppio puntatore(un puntatore per le righe e uno per le colonne)
char buffer[MAX_C], *res, temp;
int i = 0, j = 0;
FILE *ptrvip;
if((ptrvip=fopen(nomefile, "r"))== NULL)
printf("Impossibile aprire il file %s\n", nomefile);
else
{
printf("File %s letto correttamente!\n\n", nomefile);
//Acquisizione numero di colonne
res = fgets(buffer, MAX_C, ptrvip);
while(res[i] != '\0')
{
*c = *c +1;
i++;
}
C = *c;
//Riposiziono il cursore all'inizio del file
fseek(ptrvip, 0, 0);
//Conto il numero della righe
while(1)
{
res = fgets(buffer, MAX_R, ptrvip);
if(res == NULL)
break;
*r = *r + 1;
}
R = *r;
//Allocazione dinamica della memoria
m = (char**) malloc (*c * sizeof(char*));//alloco lo spazio per le colonne
for(i = 0; i < *c; i++)
m[i] = (char*) malloc (*r * sizeof(char));//alloco lo spazio per le righe
//Riposiziono il cursore all'inizio del file
fseek(ptrvip, 0, 0);
//Caricamento della matrice da file
while(!feof(ptrvip))
{
for(i = 0; i < *r; i++)
for(j = 0; j < *c; j++)
{
fscanf(ptrvip, "%c", &temp);
if(temp != '\0')
m[i][j] = temp;
}
}
fclose(ptrvip);
}
}
void stampa_matrice(matrix *m, int r, int c)
{
printf("\n");
int i,j;
for (i=0;i<r;++i)
{
for (j=0;j<c-1;++j)
printf("%c",m[i][j]);
printf("\n");
}
printf("\n");
}
Domanda: come posso fare senza allocare dinamicamente la matrice in memoria?
Sto cercando la strada più semplice per farlo ma questi puntatori mi mettono il bastone tra le ruote :dhò:
Ho provato a fare quest'altra prova ma mi da errore quando richiamo le funzioni nel main in cui gli passo la matrice:
#include <stdio.h>
#include <stdlib.h>
#define MAX_C 50
#define MAX_R 50
int R, C;
void leggi_matrice(const char* nomefile, char mat[][MAX_C]);
void stampa_matrice(char mat [][MAX_C]);
void salva_file(const char* nomefile, char mat [][MAX_C]);
void delete_c(char mat [][MAX_C], int colonna);
void delete_r(char mat [][MAX_C], int riga);
int main(int argc, char *argv[])
{
char tipo;
char matricevip[MAX_R][MAX_C];
int numero;
char* nomefile = argv[1];
if(argc!=2)
{
printf("Numero di argomenti inseriti errato!\n");
exit(EXIT_FAILURE);
}
else
{
leggi_matrice(matricevip, nomefile);
stampa_matrice(matricevip);
}
printf("\n");
printf("Comando: ");
scanf("%c %d", &tipo, &numero);
//La riga che segue serve per togliere l'invio dal buffer
while(tipo != 'x'){
switch(tipo)
{
case 'c':
if(!numero){
printf("Errore: colonna non specificata!\n");
}
else if(numero < 0 || numero > C){
printf("Errore: il numero della colonna specificata eccede la dimenzione max del file!\n");
}
else
{
printf("test_1");
delete_c(matricevip, numero);
printf("Rimossa colonna %d", numero);
stampa_matrice(matricevip);
}
printf("Comando: ");
scanf("%c %d", tipo, &numero);
continue;
case 'r':
if(!numero){
printf("Errore: riga non specificata!\n");
}
else if(numero < 0 ||numero > R){
printf("Errore: il numero della riga specificata eccede la dimenzione max del file!\n");
}
else
{
delete_r(matricevip, numero);
printf("Rimossa riga %d", numero);
stampa_matrice(matricevip);
}
printf("Comando: ");
scanf("%c %d", tipo, &numero);
continue;
case 'q':
salva_file(nomefile, matricevip);
break;
default:
printf("Comando non valido");
exit(EXIT_FAILURE);
break;
}
}
return 0;
}
void leggi_matrice(const char* nomefile, char mat[MAX_R][MAX_C])
{
//**matricevip è un doppio puntatore(un puntatore per le righe e uno per le colonne)
char buffer[MAX_C], *res, temp;
int i = 0, j = 0, R = 0, C = 0;
FILE *ptrvip = fopen(nomefile, "r");
if(nomefile == NULL)
printf("Impossibile aprire il file %s\n", nomefile);
else
{
printf("File %s letto correttamente!\n\n", nomefile);
//Acquisizione numero di colonne
res = fgets(buffer, MAX_C, ptrvip);
while(res[i] != '\0')
{
C = C +1;
i++;
}
//Riposiziono il cursore all'inizio del file
fseek(ptrvip, 0, 0);
//Conto il numero della righe
while(1)
{
res = fgets(buffer, MAX_R, ptrvip);
if(res == NULL)
break;
R = R + 1;
}
//Riposiziono il cursore all'inizio del file
fseek(ptrvip, 0, 0);
//Caricamento della matrice da file
while(!feof(ptrvip))
{
for(i = 0; i < R; i++)
for(j = 0; j < C; j++)
{
fscanf(ptrvip, "%c", &temp);
if(temp != '\0')
mat[i][j] = temp;
}
printf("\n");
}
fclose(ptrvip);
}
}
void stampa_matrice(char mat[MAX_R][MAX_C])
{
printf("\n");
int i,j;
for (i=0;i<R;++i)
{
for (j=0;j<C;++j)
printf("%c", mat[i][j]);
}
printf("\n");
}
void salva_file(const char* nomefile, char mat[MAX_R][MAX_C]){
int i = 0, j = 0;
FILE *ptrvip = fopen(nomefile, "w");
if(nomefile == NULL)
printf("Impossibile aprire il file %s\n", nomefile);
else{
for(i=0; i<R; i++){
for(j=0; j<C; j++)
{
fprintf(ptrvip, "%c\n", mat[i][j]);
}
fprintf(ptrvip, "%c\n", '\n');
}
}
}
void delete_c(char mat[MAX_R][MAX_C], int colonna){
int i,j;
if(colonna == C-1)
C = C - 1;
else{
for(i=0; i<R; i++){
for(j=colonna; j<C; j++)
{
mat[i][j] = mat[i][j+1];
}
}
C = C - 1;
}
printf("La matrice è: \n");
stampa_matrice(mat);
}
void delete_r(char mat[MAX_R][MAX_C], int riga){
int i,j;
if(riga == R-1)
R = R - 1;
else{
for(i=riga; i<R-1; i++){
for(j=0; j<C; j++)
{
mat[i][j] = mat[i+1][j];
}
}
R = R - 1;
}
printf("La matrice è: \n");
stampa_matrice(mat);
}
Il testo del problema dice che non si conosce a priori la dimensione del file, ecco il motivo per cui uso feof.
Ma questo vale fino a quando non hai trovato R e C
Una volta trovate il numero di colonne e righe la feof non ti serve più
Domanda: come posso fare senza allocare dinamicamente la matrice in memoria?
Risposta: non puoi
Sto cercando la strada più semplice per farlo ma questi puntatori mi mettono il bastone tra le ruote
I puntatori sono il cuore di C. Se non li comprendi e non sai utilizzarli, praticamente non potrai utilizzare il C.
Ti ho detto di utilizzare un puntatore triplo ma non mi pare tu l'abbia fatto. Devi passare il puntatore a matricevip
leggi_matrice(&matricevip, nomefile);
modificando opportunamente la funzione leggi_matrice
mbistato
19-08-2014, 14:53
Si ok cmq ho risolto senza allocare la memoria dinamicamente :-D
Non puoi risolvere senza allocare la memoria se non sai quanto è grande il file ...
mbistato
19-08-2014, 15:39
Non puoi risolvere senza allocare la memoria se non sai quanto è grande il file ...
Dal testo del problema, so che il file non supera le dimensioni MAX_R x MAX_C
Ecco il codice funzionante:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_R 50
#define MAX_C 50
int main(int narg, char *argv[])
{
char mat[MAX_R][MAX_C];
/*il vettore riga mi serve per andare a*/
char riga[MAX_R];
/*op sta per operazione e sarebbe quello che tu hai chiamato "tipo" nel tuo programma*/
char op;
/*posizione invece sarebbe il numero della riga o della colonna che hai scelto di cancellare*/
int pos;
/*eff_r ed eff_c sono rispettivamente il numero di righe e colonne della matrice di caratteri letta dal file*/
int eff_r, eff_c;
int i,j;
FILE *fp;
/*flag_ciclo è una variabile che useremo per ciclare l'esecuzione del programma finche non si verificano errori o finchè l'utente non esca
dal programma*/
int flag_ciclo;
if (narg!=2)
{
printf("Numero di parametri errato. \n");
exit (1);
}
fp = fopen(argv[1], "r");
if (fp == NULL)
{
printf("Errore nell'apertura del file %s \n", argv[1]);
exit (1);
}
i = 0;
/*leggo il file puntato da fp carattere per carattere e lo immagazzino in mat[i] (questo è quello che fa la funzione fgets).
Il while si conclude quando si arriva alla fine del file*/
while (fgets(mat[i],MAX_C,fp)!=NULL)
{
i++;
}
fclose(fp);
printf("File %s letto correttamente. \n", argv[1]);
/*memorizzo nella variabile effc la lunghezza di ogni riga (nota che sottraggo 1 perchè l'ultimo carattere presente nel vettore di stringhe
mat è il terminatore di stringa '\0' che ovviamete non devo contare). Dunque effc rappresenta il numero di colonne della matrice*/
eff_c = strlen(mat[0])-1;
/*analogamente eff_r sarà il numero di righe della matrice che era memorizzato nella variabile i incrementata
durante il while precedente*/
eff_r = i;
flag_ciclo = 0;
/*tale ciclo mi serve perchè ogni volta che do il comando da tastiera, a fine esecuzione, il programma deve permettermi di digitare
ulteriori comandi (in caso ti spiego meglio a voce)*/
while(!flag_ciclo)
{
printf("Comando: ");
/*La funzione gets brutalmente legge tutta la stringa corrispondente all’assegnazione e la funzione sscanf
la scompone nei due elementi nome e valore del nome. La funzione sscanf restituisce il numero di campi che sono stati letti, in
questo caso sarebbero 2: op e pos */
gets(riga);
j = sscanf(riga,"%c %d",&op,&pos);
/*Qui ci sono due controlli per verificare che il numero di riga o di colonna sia stato immesso sulla linea di comando*/
if ((op == 'r')&&(j == 1))
{
printf("Errore riga non specificata \n");
}
else if ((op == 'c')&&(j == 1))
{
printf("Errore colonna non specificata \n");
}
else
/*Se l'utente ha inserito il comando completo verifico in quale caso mi trovo:*/
switch(op)
{
case 'x':
printf("Programma terminato senza salvataggio. \n");
return 1;
break;
case 'r':
/*se la posizione inserita supera il massimo numero di righe della matrice e/o la posizione < 1, visualizzo l'errore*/
if ((pos > eff_r)||(pos < 1))
{
printf("Errore: riga non specificata correttamente \n");
break;
}
else
{
//Rimuovi riga: copio ciclicamente riga+1 in riga e tolgo una riga totale
printf("Rimossa riga %d. La matrice contiene:\n", pos);
for (pos = pos-1; pos < (eff_r-1); pos++)
{
/*la funzione strcpy copia il contenuto di mat[pos+1] in mat[pos]. In pratica per eliminare la riga, sovrascrivo
la riga+1 in riga */
strcpy(mat[pos],mat[pos+1]);
}
/*decremento, giustamente, il numero di righe della matrice*/
eff_r--;
//Stampo la matrice dopo l'operazione
for (i = 0; i<eff_r; i++)
{
for (j = 0; j< eff_c;j++)
{
printf("%c", mat[i][j]);
}
printf("\n");
}
}
break;
case 'c':
/*al solito controllo che la posizione della colonna scelta sia una posizione ammissibile, ovvero che non superi i
limiti inferiore e superiore*/
if ((pos > eff_c)||(pos < 1))
{
printf("Errore: colonna non specificata correttamente \n");
break;
}
else
{
//Rimuovi colonna: copio ciclicamente colonna+1 in colonna e tolgo una colonna totale
printf("Rimossa colonna %d. La matrice contiene:\n", pos);
for (i = 0; i < eff_r; i++)
for (j = pos - 1; j < (eff_c-1);j++)
{
/*qui avrei potuto utilizzare anche la funzione strcpy come fatto per le righe*/
mat[i][j] = mat[i][j+1];
}
eff_c--;
//Stampo la matrice dopo l'operazione
for (i = 0; i<eff_r; i++)
{
for (j = 0; j< eff_c;j++)
{
printf("%c", mat[i][j]);
}
printf("\n");
}
}
break;
case 'q':
//Salvo il file copiando il contenuto della matrice nel file.
fp = fopen (argv[1], "w");
if (fp == NULL)
{
printf("Errore nell'apertura del file %s in scrittura \n", argv[1]);
exit (1);
}
for (i = 0; i<eff_r; i++)
{
for (j = 0; j< eff_c;j++)
{
fprintf(fp,"%c", mat[i][j]);
}
fprintf(fp,"\n");
}
fclose(fp);
printf("Il file %s e' stato salvato correttamente. \n", argv[1]);
return 1;
break;
default:
printf("Errore: comando sconosciuto \n");
}
}
return 0;
}
Dal testo del problema, so che il file non supera le dimensioni MAX_R x MAX_C
Questo cambia le cose, così il problema è diverso. Avresti dovuto precisarlo prima ...