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

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

No hay salida impresa en el terminal y acaba de obtener un error de segmentación. Pero cuando lo ejecuto en GDB, consigo siguiente -Ejecución de printf() y falla de segmentación

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

Esto significa programa reciben culpa SEG en la línea 7 (obviamente no puedo escribir en la matriz de caracteres constante). Entonces, ¿por qué printf() de la línea número 6 no se ejecuta?

+0

No estoy del todo seguro. Funciona como se esperaba en mi mac con OSX Lion (cumplido con LLVM, depurado con LLDB). –

Respuesta

30

Esto se debe al almacenamiento en memoria intermedia de secuencia de stdout. A menos que haga fflush(stdout) o imprima una nueva línea "\n", la salida puede almacenarse en un búfer.

En este caso, se segfaulting antes de que se vacíe e imprima el búfer.

Usted puede intentar esto en su lugar:

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

Tenga en cuenta que 'fflush' es realmente la manera correcta de hacerlo: no se garantiza que una nueva línea desencadene un color (y he sido mordido por ese comportamiento antes). –

4

La razón por la que está recibiendo un fallo de segmentación es que los literales de cadena C son de sólo lectura de acuerdo con el estándar de C, y usted están intentando escribir 's' sobre el segundo elemento de la matriz literal "Vikram".

La razón por la que no obtiene ninguna salida es porque su programa está almacenando en búfer su salida y se bloquea antes de que tenga la oportunidad de descargar su búfer. El propósito de la biblioteca stdio, además de proporcionar funciones de formato amigables como printf (3), es reducir la sobrecarga de las operaciones de E/S al almacenar en búfer los datos en búferes en memoria y solo enjuagar la salida cuando sea necesario, y solo realizar la entrada ocasionalmente en lugar de constantemente En el caso general, la entrada y salida real no ocurrirá en el momento en que llame a la función stdio, sino solo cuando el búfer de salida esté lleno (o el búfer de entrada esté vacío).

Las cosas son ligeramente diferentes si un objeto FILE se ha configurado para que se vacíe constantemente (como stderr), pero en general, esa es la esencia.

Si está depurando, lo mejor es fprintf a stderr para asegurarse de que sus impresiones de depuración se vacíen antes de producirse un bloqueo.

9

Primero debe terminar sus printfs con "\ n" (o al menos el último). Pero eso no está relacionado con segfault.

Cuando el compilador compila su código, divide el binario en varias secciones. Algunos son de solo lectura, mientras que otros son escribibles. Escribir en una sección de solo lectura puede causar una segfault. Los literales de cadena generalmente se colocan en una sección de solo lectura (gcc debe ponerlo en ".rodata"). El nombre del puntero apunta a esa sección ro. Por lo tanto, debe usar

const char *name = "Vikram"; 

En mi respuesta, he usado algunos "puede" "debería". El comportamiento depende de su sistema operativo, el compilador y la configuración de compilación (el script del enlazador define las secciones).

Adición

-Wa,-ahlms=myfile.lst 

de línea de comandos de gcc produce un archivo llamado myfile.lst con el código ensamblador generado. En la parte superior se puede ver

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

Lo que demuestra que la cadena está en Vikram.

El mismo código utilizando (Debe ser en el ámbito global, de lo contrario gcc puede almacenar en la pila, tenga en cuenta que es una matriz y no un puntero)

char name[] = "Vikram"; 

produce

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

El la sintaxis es un poco diferente, pero vea cómo está ahora en la sección .data, que es de lectura-escritura. Por cierto, este ejemplo funciona.

+1

Si lo nota, el OP no pregunta por qué ocurre la segfault, pero por qué la cadena no se imprimió en primer lugar. –

+1

aunque esta puede no ser la respuesta exacta a la pregunta, la sugerencia y la explicación sobre .rodata y .data son útiles. – vts

0

Por defecto cuando stdout está conectado a un terminal, el flujo se almacena en la línea. En la práctica, en su ejemplo, la ausencia de '\n' (o de una descarga de flujo explícita) es la razón por la cual no se imprimen los caracteres.

Pero en teoría no está limitada comportamiento indefinido (desde el Standard "el comportamiento [...] para los que esta norma no impone requisitos") y la violación de segmento puede ocurrir incluso antes de que ocurra el comportamiento indefinido, por ejemplo antes la primera llamada printf!

+0

Entonces ... ¿está diciendo que el comportamiento es tan indefinido que puede actuar * al revés en el tiempo *? –