¿Por qué este código C de inversión de cadena está causando una falla de segmentación? [duplicate]

8 minutos de lectura

avatar de usuario
James

Estoy tratando de escribir código para invertir una cadena en su lugar (solo estoy tratando de mejorar en la programación C y la manipulación del puntero), pero no puedo entender por qué obtengo una Fallo de segmentación:

#include <string.h>

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 maneras de hacer esto, pero estoy interesado en saber qué es específicamente en mi codigo está causando la falla de segmentación.

Actualizar: He incluido la función de llamada según lo solicitado.

  • Segfault casi siempre significa que está tratando de desreferenciar un puntero nulo. Ejecute su código a través de GDB, encuentre en qué línea está fallando y vea qué puntero es nulo (0x000000) allí.

    – bola mate

    23 de octubre de 2009 a las 17:03

  • Además, ¿por qué estás usando dos variables (i y j)? Absolutamente puedes hacer esto con el contador.

    – bola mate

    23 de octubre de 2009 a las 17:04

  • ¿Puedes publicar el código que crea el char* y lo pasa a reverse()?

    – noche

    23 de octubre de 2009 a las 17:07

  • y no uses strlen() en una condición de bucle.

    –Carl Norum

    23 de octubre de 2009 a las 17:13

  • @Carl Norum: Si miras con cuidado, strlen no se usa en la condición de bucle en el ejemplo de código anterior.

    – Ant

    23 de octubre de 2009 a las 21:20

avatar de usuario
Hormiga

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

¿Cómo llamas a tu función?

Agregado: está pasando un puntero a un literal de cadena. Los literales de cadena no se pueden modificar. No puede invertir un literal de cadena.

Pase un puntero a una cadena modificable en su lugar

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

Esto ya se ha explicado hasta la saciedad aquí. "teststring" es un literal de cadena. El literal de cadena en sí mismo es un objeto no modificable. En la práctica, los compiladores podrían (y lo harán) colocarlo en la memoria de solo lectura. Cuando inicializas un puntero como ese

char *s = "teststring";

el puntero apunta directamente al comienzo de la cadena literal. Cualquier intento de modificar lo que s está apuntando se considera que fallan en el caso general. Puedes leerlo, pero no puedes escribir en él. Por esta razón, se recomienda encarecidamente apuntar a los literales de cadena solo con variables de puntero a const.

const char *s = "teststring";

Pero cuando declaras tu s como

char s[] = "teststring";

obtienes una matriz completamente independiente s ubicado en la memoria modificable ordinaria, que es solo inicializado con cadena literal. Esto significa que esa matriz modificable independiente s obtendrá su valor inicial copiado del literal de cadena. Después de eso tu s array y el literal de cadena continúan existiendo como objetos completamente independientes. El literal sigue siendo no modificable, mientras que su s matriz es modificable.

Básicamente, la última declaración es funcionalmente equivalente a

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

  • Esto funcionó. Gracias. Pero, ¿puede explicar con más detalle cuál es la diferencia entre las dos inicializaciones de cadena? Es decir, ¿por qué la forma en que creé una cadena literal mientras que la sintaxis de ‘matriz’ crea una cadena modificable?

    – James

    23 de octubre de 2009 a las 17:10

  • Los literales @james String van en almacenamiento de solo lectura. Pero al hacer “char []s = …” está declarando una matriz e inicializándola en un literal, en lugar de obtener un puntero a un literal.

    – asveikau

    23 de octubre de 2009 a las 17:15

  • @james: Vea el texto adicional en mi respuesta.

    – Ant

    23 de octubre de 2009 a las 17:21

avatar de usuario
jaredpar

Su código podría estar fallando en el segmento por varias razones. Aquí están los que vienen a la mente

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

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

EDITAR

Según su muestra, el número 2 es definitivamente la respuesta. Un literal de cadena en C/C++ no se puede modificar. El tipo adecuado es en realidad const char* y no char*. Lo que debe hacer es pasar una cadena modificable a ese búfer.

Ejemplo rápido:

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

  • La “cadena de prueba” literal se encuentra en algún lugar de la memoria de solo lectura y no se le permite escribir en ella. Esta solía ser una práctica común, es decir, una forma de asignar algo de memoria, pero la mayoría de los sistemas modernos no lo permiten. El ejemplo de JaredPar funciona porque strdup asigna memoria que luego posee.

    –Tim Allman

    23 de octubre de 2009 a las 17:19

¿Estás probando algo así?

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

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

Su declaración 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 “teststring”, 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 “teststring”, que por supuesto está en el segmento de código, pero hay una operación de copia adicional en curso, a la pila en este caso.

avatar de usuario
Sinan Ünür

Ver 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 programa falla si trato de asignar un nuevo valor a p[i].

Responder:

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

Como inicializador de una matriz de char, como en la declaración 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 sin nombre de caracteres, y esta matriz sin nombre puede almacenarse en la memoria de solo lectura y, por lo tanto, no necesariamente puede modificarse. En un contexto de expresión, la matriz se convierte de inmediato en un puntero, como de costumbre (consulte la sección 6), por lo que la segunda declaración se inicializa p para apuntar al primer elemento de la matriz sin nombre.

Algunos compiladores tienen un interruptor que controla si los literales de cadena se pueden escribir o no (para compilar 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 detección de errores).

(énfasis mío)

Ver también Volver a lo básico por Joel.

  • Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página enlazada cambia. – De la revisión

    – Sree

    17 de enero de 2018 a las 11:11

  • @Sree Debería arreglarse ahora.

    – Sinan Ünür

    17 de enero de 2018 a las 12:39

avatar de usuario
usuario193272

¿Qué compilador y depurador estás usando? Usando gcc y gdb, compilaría el código con el indicador -g y luego lo ejecutaría en gdb. Cuando se produce un error de segmento, solo haría un seguimiento (comando bt en gdb) y vería cuál es la línea infractora que causa el problema. Además, solo ejecutaría el código paso a paso, mientras “veo” los valores del puntero en gdb y sé dónde está exactamente el problema.

Buena suerte.

  • Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página enlazada cambia. – De la revisión

    – Sree

    17 de enero de 2018 a las 11:11

  • @Sree Debería arreglarse ahora.

    – Sinan Ünür

    17 de enero de 2018 a las 12:39

avatar de usuario
Vishal

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

¿Ha sido útil esta solución?