falla de segmentación strtok

Estoy tratando de entender por qué el siguiente fragmento de código está dando un error 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 literales de cadena, pero en este caso, la line apunta directamente a la cadena "this is a test" que internamente es una matriz de char . ¿Hay alguna line tokenizing sin copiarla en una matriz?

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

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

ADVERTENCIA: Sigue la digresión.

El literal de cadena "this is a test" es de una expresión de 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, de tipo char* .

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

Algunos comstackdores 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 comstackdores 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 locking del progtwig.

Entonces, ¿por qué los literales de cadena no son const ? Como realmente no deberías tratar de modificarlos, ciertamente tendría sentido, y C ++ sí hace que los literales de cadena sean 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 comstackdores antes de eso). Entonces, un progtwig previo a ANSI podría verse así:

 #include  print_string(s) char *s; { printf("%s\n", s); } main() { print_string("Hello, world"); } 

No había forma de hacer cumplir el hecho de que print_string no tiene permitido modificar la cadena apuntada por s . Hacer que los literales de cadena const en ANSI C habría roto el código existente, 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 de C.)

Como dijiste, no puedes modificar un literal de cadena, que es lo que hace strtok . Tu tienes que hacer

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

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

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

El comstackdor de C crea cadenas constantes en tiempo de comstackció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.

Estoy seguro de que te pegarán por esto … pero “strtok ()” es intrínsecamente inseguro y propenso a cosas como violaciones de acceso.

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

Pruebe esto en su lugar:

 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); } 

Strok modifica su primer argumento para tokenizarlo. Por lo tanto, no se puede pasar una cadena literal, ya que es de 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.

¿Qué punto estás tratando de hacer con tu observación “… es internamente una serie de caracteres” ?

El hecho de que "this is a test" es 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 una cadena literal. Es por eso que se bloquea.

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