2012-01-22 23 views
10

Estoy tratando de entender por qué el siguiente fragmento de código se da un fallo de segmentación:strtok fallo de segmentación

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    tokenize("this is a test"); 
} 

Sé que strtok() en realidad no tokenize en los literales de cadena, pero en este caso, line apunta directamente a la cadena "this is a test" que es internamente una matriz de char. ¿Hay algún tokenizing line sin copiarlo en una matriz?

+2

Dude - "this is a test" es STRING LITERAL. Lo que significa que es un * LEA SOLAMENTE * "array of char". Incluso puede salirse con la suya tratando de modificarlo sin fallar en ciertas plataformas. Pero definitivamente es un no-no en * ANY * plataforma :) – paulsm4

Respuesta

14

El problema es que está intentando modificar un literal de cadena. Si lo hace, el comportamiento de su programa no estará definido.

Decir que no se permite modificar una cadena literal es una simplificación excesiva. Decir que los literales de cadena son const es incorrecto; Ellos no están.

ADVERTENCIA: Digression follows.

La cadena literal "this is a test" es de una expresión del tipo char[15] (14 para la longitud, más 1 para la terminación '\0').En la mayoría de los contextos, incluido este, dicha expresión se convierte implícitamente en un puntero al primer elemento de la matriz, del tipo char*.

El comportamiento de intentar modificar la matriz a la que hace referencia una cadena literal no está definido, no porque sea const (no lo es), sino porque el estándar C especifica que no está definido.

Algunos compiladores pueden permitirle salirse con la suya. Su código podría modificar realmente la matriz estática correspondiente al literal (lo que podría causar una gran confusión más adelante).

La mayoría de los compiladores modernos, sin embargo, almacenan la matriz en la memoria de solo lectura, no en la ROM física, sino en una región de la memoria que está protegida contra modificaciones por el sistema de memoria virtual. El resultado de intentar modificar dicha memoria suele ser una falla de segmentación y un bloqueo del programa.

¿Por qué no son literales de cadena const? Como realmente no deberías tratar de modificarlos, ciertamente tendría sentido, y C++ sí hace literales de cadena const. La razón es histórica. La palabra clave const no existía antes de que fuera introducida por el estándar ANSI C de 1989 (aunque fue probablemente implementado por algunos compiladores antes de eso). Así, un programa pre-ANSI podría tener este aspecto:

#include <stdio.h> 

print_string(s) 
char *s; 
{ 
    printf("%s\n", s); 
} 

main() 
{ 
    print_string("Hello, world"); 
} 

No había manera de hacer cumplir el hecho de que print_string no está permitido modificar la cadena apuntada por s. Hacer cadenas literales const en ANSI C habría roto el código existente, lo que el comité ANSI C intentó evitar. No ha habido una buena oportunidad desde entonces para hacer un cambio en el lenguaje. (Los diseñadores de C++, en su mayoría Bjarne Stroustrup, no estaban tan preocupados por la compatibilidad con versiones anteriores) C.

+0

gran explicación !!! – ademar111190

+1

¿Le importaría explicar al infractor? –

2

Como dijiste, no puedes modificar una cadena literal, que es lo que hace strtok. Que tiene que hacer

char str[] = "this is a test"; 
tokenize(str); 

Esto crea la matriz str y inicializa con this is a test\0, y pasa un puntero a ella a tokenize.

0

Estoy seguro de que recibirás una paliza por esto ... pero "strtok()" es inherentemente inseguro y propenso a cosas como violaciones de acceso.

Aquí, la respuesta es casi seguro que se utiliza una constante de cadena.

Tal vez puedas probar:

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    char buff[80]; 
    strcpy (buff, "this is a test"); 
    tokenize(buff); 
} 
+1

Si va a mencionar la naturaleza insegura de strtok, también debemos recordar que strncpy es mucho más seguro que strcpy. Aunque strcpy es perfectamente seguro para una cadena constante en tiempo de compilación, una refactorización posterior podría convertir la llamada strcpy en una vulnerabilidad de desbordamiento del búfer. –

1

Strok modifica su primer argumento con el fin de que tokenize. Por lo tanto, no se puede pasar una cadena literal, ya que es del tipo const char * y no se puede modificar, de ahí el comportamiento indefinido. Tienes que copiar el literal de la cadena en una matriz char que se puede modificar.

2

Hay una muy buena razón para intentar tokenizar una cadena constante en tiempo de compilación causará un error de segmentación: la cadena constante está en la memoria de solo lectura.

El compilador de C crea secuencias constantes en tiempo de ejecución en el ejecutable, y el sistema operativo las carga en una memoria de solo lectura (.rodata en un archivo * nix ELF). Dado que esta memoria está marcada como de solo lectura, y dado que strtok escribe en la cadena que ingresa, se produce un error de segmentación al escribir en la memoria de solo lectura.

1

¿Qué punto está tratando de hacer con su "... es internamente una matriz de char" observación?

El hecho de que "this is a test" sea internamente una matriz de char no cambia nada en absoluto. Sigue siendo un literal de cadena (todos los literales de cadena son matrices de caracteres no modificables). Su strtok todavía intenta tokenizar un literal de cadena. Es por eso que se bloquea.

0

Acabo de golpear el error de falla de segmentación de tratar de usar printf para imprimir el token (cmd en su caso) después se convirtió en NULL

Cuestiones relacionadas