2012-02-27 5 views
14
#include<stdio.h> 

int main() 
{ 
    char *name = "Vikram"; 
    printf("%s",name); 
    name[1]='s'; 
    printf("%s",name); 
    return 0; 
} 

Non è presente alcuna uscita stampata sul terminale e viene visualizzato solo un errore di segmentazione. Ma quando corro in GDB, ottengo seguenti -Esecuzione di printf() e errore di segmentazione

Program received signal SIGSEGV, Segmentation fault. 
0x0000000000400525 in main() at seg2.c:7 
7  name[1]='s'; 
(gdb) 

Questo significa programma ricevono guasto SEG il 7 linea (ovviamente non posso scrivere sulla matrice char costante). Allora perché printf() della riga numero 6 non viene eseguito?

+0

Non ne sono del tutto sicuro. Funziona come previsto sul mio Mac con OSX Lion (conforme a LLVM, debugato con LLDB). –

risposta

30

Ciò è dovuto al buffer del flusso di stdout. Se non si esegue fflush(stdout) o si stampa una nuova riga "\n", l'output può essere memorizzato nel buffer.

In questo caso, segfaulting prima che il buffer venga svuotato e stampato.

si può provare questo invece:

printf("%s",name); 
fflush(stdout);  // Flush the stream. 
name[1]='s';   // Segfault here (undefined behavior) 

o:

printf("%s\n",name); // Flush the stream with '\n' 
name[1]='s';   // Segfault here (undefined behavior) 
+6

Nota che 'fflush' è davvero il modo giusto per farlo - non è garantito che una nuova riga faccia scattare un flush (e sono stato morso da quel comportamento prima). –

4

La ragione si stanno ottenendo un errore di segmentazione è che stringhe letterali C sono di sola lettura secondo lo standard C, e si stanno tentando di scrivere "s" sul secondo elemento dell'array letterale "Vikram".

Il motivo per cui non si ottiene alcun output è perché il programma sta eseguendo il buffering del proprio output e si arresta in modo anomalo prima che abbia la possibilità di svuotare il buffer. Lo scopo della libreria stdio, oltre a fornire funzioni di formattazione amichevoli come printf (3), è quello di ridurre il sovraccarico delle operazioni di I/O mediante il buffering dei dati nei buffer in memoria e l'output di flushing solo quando necessario, e solo l'esecuzione di input occasionalmente invece di costantemente. L'input e l'output effettivi non si verificheranno nel caso generale quando si chiama la funzione stdio, ma solo quando il buffer di output è pieno (o il buffer di input è vuoto).

Le cose sono leggermente diverse se un oggetto FILE è stato impostato in modo che si scarichi costantemente (come stderr), ma in generale, questo è l'essenza.

Se si esegue il debug, è consigliabile fprintf su stderr per assicurare che le stampe di debug vengano svuotate prima di un arresto anomalo.

9

Per prima cosa si deve terminare la stampa con "\ n" (o almeno l'ultima). Ma questo non è legato al segfault.

Quando il compilatore compila il codice, divide il file binario in diverse sezioni. Alcuni sono di sola lettura, mentre altri sono scrivibili. La scrittura in una sezione di sola lettura può causare un segfault. I valori letterali delle stringhe di solito sono posizionati in una sezione di sola lettura (gcc dovrebbe inserirli in ".rodata"). Il nome del puntatore punta a quella sezione ro. Pertanto è necessario utilizzare

Nella mia risposta ho usato alcuni "potrebbe" "dovrebbe". Il comportamento dipende dal sistema operativo, dal compilatore e dalle impostazioni di compilazione (lo script linker definisce le sezioni).

Aggiunta

-Wa,-ahlms=myfile.lst 

a linea di comando di gcc produce un file chiamato myfile.lst con il codice assembler generato. In alto potete vedere

.section .rodata 
.LC0: 
    .string "Vikram" 

il che dimostra che la stringa è in Vikram.

Lo stesso codice utilizzando (Deve essere portata globale, altrimenti gcc può memorizzare in pila, notare che è un array e non un puntatore)

char name[] = "Vikram"; 

produce

.data 
    .type name, @object 
    .size name, 7 
name: 
    .string "Vikram" 

L' la sintassi è un po 'diversa, ma guarda come è ora nella sezione .data, che è in lettura-scrittura. Tra l'altro questo esempio funziona.

+1

Se si nota, l'OP non sta chiedendo perché si verifica il segfault, ma perché la stringa non è stata stampata in primo luogo. –

+1

sebbene questo potrebbe non essere esattamente la risposta alla domanda, il suggerimento e la spiegazione su .rodata e .data sono utili. – vts

0

Per impostazione predefinita, quando stdout è collegato a un terminale, lo streaming è in modalità buffer di linea. In pratica, nel tuo esempio l'assenza di '\n' (o di un flusso di flusso esplicito) è il motivo per cui non vengono stampati i caratteri.

ma in teoria un comportamento indefinito non è limitato (dal standard "comportamento [...] per il quale questa norma internazionale impone nessun requisito") e il segfault può accadere anche prima che si verifichi il comportamento non definito, ad esempio prima la prima chiamata printf!

+0

Quindi ... stai dicendo che il comportamento è così indefinito che può agire * indietro nel tempo *? –