¿Por qué este código C de inversión de cadena provoca un error de segmentación?

Intento escribir código para invertir una cadena en su lugar (estoy tratando de mejorar en la progtwigción C y la manipulación del puntero), pero no puedo entender por qué estoy obteniendo una falla de segmentación :

#include  void reverse(char *s); int main() { char* s = "teststring"; reverse(s); return 0; } void reverse(char *s) { int i, j; char temp; for (i=0,j = (strlen(s)-1); i < j; i++, j--) { temp = *(s+i); //line 1 *(s+i) = *(s+j); //line 2 *(s+j) = temp; //line 3 } } 

Son las líneas 2 y 3 las que están causando la falla de segmentación. Entiendo que puede haber mejores formas de hacerlo, pero estoy interesado en descubrir qué es lo que específicamente en mi código está causando la falla de segmentación.

Actualización : he incluido la función de llamada según lo solicitado.

No hay forma de decir solo de ese código. Lo más probable es que esté pasando un puntero que apunta a memoria no válida, memoria no modificable u otro tipo de memoria que simplemente no se puede procesar de la manera en que se procesa aquí.

¿Cómo llamas a tu función?

Agregado: está pasando un puntero a un literal de cadena. Los literales de cadena no son modificables. No puede invertir un literal de cadena.

Pase un puntero a una cadena modificable en su lugar

 char s[] = "teststring"; reverse(s); 

Esto ya ha sido explicado hasta la muerte aquí. "teststring" es un literal de cadena. El literal de cadena en sí es un objeto no modificable. En la práctica, los comstackdores pueden (y lo harán) ponerlo en la memoria de solo lectura. Cuando inicializas un puntero como ese

 char *s = "teststring"; 

el puntero apunta directamente al comienzo del literal de la cadena. Cualquier bash de modificar lo que está apuntando se considera que falla en el caso general. Puedes leerlo, pero no puedes escribir en él. Por esta razón, se recomienda encarecidamente que apunte a literales de cadena únicamente con variables de puntero a const.

 const char *s = "teststring"; 

Pero cuando declaras tu s como

 char s[] = "teststring"; 

obtienes una matriz totalmente independiente s ubicada en la memoria ordinaria modificable, que acaba de inicializarse con una cadena literal. Esto significa que esa matriz modificable independiente obtendrá su valor inicial copiado de la cadena literal. Después de eso, su matriz y el literal de cadena continuarán existiendo como objetos completamente independientes. El literal sigue siendo no modificable, mientras que el conjunto de s es modificable.

Básicamente, la última statement es funcionalmente equivalente a

 char s[11]; strcpy(s, "teststring"); 

Su código podría estar segfaulting por una serie de razones. Aquí están los que vienen a la mente

  1. s es NULL
  2. s apunta a una cadena const que se mantiene en la memoria de solo lectura
  3. s no es NULL terminado

Creo que el # 2 es el más probable. ¿Puede mostrarnos el sitio de llamada de reversa?

EDITAR

Basado en su muestra n. ° 2 es definitivamente la respuesta. Un literal de cadena en C / C ++ no es modificable. El tipo correcto es en realidad const char* y no char* . Lo que tienes que hacer es pasar una cadena modificable a ese buffer.

Ejemplo rápido:

 char* pStr = strdup("foobar"); reverse(pStr); free(pStr); 

¿Estás probando esto algo así?

 int main() { char * str = "foobar"; reverse(str); printf("%s\n", str); } 

Esto hace que str una cadena sea literal y probablemente no podrá editarla (segfaults para mí). Si define char * str = strdup(foobar) debería funcionar bien (lo hace para mí).

Tu statement es completamente incorrecta:

 char* s = "teststring"; 

“Teststring” se almacena en el segmento de código, que es de solo lectura, como el código. Y, s es un puntero a “testingtring”, al mismo tiempo, está intentando cambiar el valor de un rango de memoria de solo lectura. Por lo tanto, falla de segmentación.

Pero con:

 char s[] = "teststring"; 

s se inicializa con “testtring”, que por supuesto está en el segmento de código, pero hay una operación de copia adicional en curso, a la stack en este caso.

¿Qué comstackdor y depurador estás usando? Usando gcc y gdb, comstackría el código con el indicador -g y luego lo ejecutaría en gdb. Cuando se segmenta, simplemente haría una traza inversa (comando bt en gdb) y vería cuál es la línea ofensiva que causa el problema. Además, simplemente ejecutaba el código paso a paso, mientras “miraba” los valores del puntero en gdb y sabía dónde estaba exactamente el problema.

Buena suerte.

Como algunas de las respuestas proporcionadas anteriormente, la memoria de cadena es de solo lectura. Sin embargo, algunos comstackdores ofrecen una opción para comstackr cadenas de escritura. Por ejemplo, con gcc , las versiones 3.x son compatibles -fwritable-strings pero las versiones más nuevas no.

Vea la pregunta 1.32 en la lista de preguntas frecuentes de C:

¿Cuál es la diferencia entre estas inicializaciones?

 char a[] = "string literal"; char *p = "string literal"; 

Mi progtwig se bloquea si trato de asignar un nuevo valor a p[i] .

Responder:

Un literal de cadena (el término formal para una cadena de comillas dobles en origen C) se puede usar de dos maneras diferentes:

Como iniciador de una matriz de char, como en la statement de char a[] , especifica los valores iniciales de los caracteres en esa matriz (y, si es necesario, su tamaño).

En cualquier otro lugar, se convierte en una matriz estática no nombrada de caracteres, y esta matriz sin nombre se puede almacenar en la memoria de solo lectura, y que, por lo tanto, no se puede modificar necesariamente . En un contexto de expresión, la matriz se convierte a la vez en un puntero, como es habitual (ver sección 6), por lo que la segunda statement inicializa p para apuntar al primer elemento de la matriz sin nombre.

Algunos comstackdores tienen un interruptor que controla si los literales de cadena son editables o no (para comstackr código antiguo), y algunos pueden tener opciones para hacer que los literales de cadena se traten formalmente como matrices de const char (para una mejor captura de errores).

( énfasis mío )

Ver también Back to Basics por Joel .

Creo que strlen no puede funcionar dado que s no tiene terminación NULL. Entonces, el comportamiento de su iteración no es el que espera. Como el resultado de strlen será superior a la longitud, escribirás en la memoria donde no deberías estar.

Además, s apunta a una cadena constante retenida por una memoria de solo lectura. No puedes modificarlo. Intente iniciar s utilizando la función get como se hace en el ejemplo de strlen