2013-04-03 3 views
7

Sto scrivendo un semplice programma C che apre un file e legge ogni riga in un array, dove LISTS è il numero massimo di righe nel file che desidero leggere.Errore di segmentazione la seconda volta che printf viene chiamato

Tuttavia, quando MASTER_LIST è inferiore alle righe LISTS, ricevo un errore di segmentazione su printf() la seconda volta, ma non il primo (ho commentato per mostrare dove).

Sono un po 'confuso da questo comportamento, e mi chiedo che cosa lo causi e cosa posso fare per eluderlo. Idealmente, smetterei di leggere quando fgets trova la fine del file.

#define MASTER_LIST "master_list.txt" 
#define LINE 64 
#define LISTS 32 

char **lists = malloc(LISTS * sizeof(char *)); 
int i; 
for (i = 0; i < LISTS; i++) { 
    lists[i] = malloc(LINE * sizeof(char)); 
} 

/*Open the file for reading.*/ 
FILE *fp = fopen(MASTER_LIST, "r"); 
if (fp != NULL) { 
    /*Each line of the file, up to LISTS is read into lists.*/ 
    for (i = 0; i < LISTS; i++){ 

     lists[i] = fgets(lists[i], LINE, fp); 
     /*NO SEGFAULT HERE*/ printf("Line Read: %s\n", lists[i]);     
    } 
} 

/*print out each line*/ 
for(i = 0; i < LISTS; i++){ 
    printf("Are we segfaulting yet? %d\n", i); 
    /*HERE I GET A SEGFAULT*/ printf("%s\n", lists[i]); 
    printf("How about now? %d\n", i);  
} 
+1

Sei sicuro che le stringhe siano terminate correttamente con un ''\ 0'' alla fine? –

+1

Ho avuto l'impressione che Fgets abbia aggiunto automaticamente il terminatore nullo dopo ogni fine di riga. Sono sicuro che il mio file di testo ha solo caratteri di nuova riga per separare le righe. –

+0

hai provato a correre sotto valgrind? –

risposta

9

fgets rendimenti NULL se non riesce a leggere i caratteri, che accadrebbe se il vostro file contiene meno di LISTS linee. Tentativo di printf un puntatore NULL non è un comportamento definito.


Questo è un buon momento per ricordare a noi stessi che il comportamento non definito è davvero indefinito. Sembra che lo printf debba arrestare il primo tempo in giro. Ma lo spec. C non dice nulla su cosa dovrebbe accadere, quindi il tuo printf stampa semplicemente (null) (molte librerie Linux lo fanno, per esempio).

Perché lo secondo si arresta in modo anomalo? È perché è stato utilizzato il modello

printf("%s\n", lists[i]); 

Molti compilatori ottimizzare questo per

puts(lists[i]); 

e sul vostro sistema, puts fa non controllo per un puntatore NULL e così è segfaults.

Morale della trama? Non fare mai affidamento su comportamenti indefiniti o aspettarsi risultati coerenti.

+0

Quindi potrebbe stampare la prima volta e poi segfault il prossimo? –

+1

Il comportamento indefinito è così a volte. (Anche se, ammetto che questo è un po 'più strano del normale). – nneonneo

+0

La prima volta che attraverso di essa avrà trovato il marcatore "end-of-file", e solo dato un NULL stringa terminata (che è buono). È quando tenti di leggere di nuovo dopo la fine del file che 'fgets' restituirà NULL e romperà le cose. – Alan