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.
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
jaredpar
Su código podría estar fallando en el segmento por varias razones. Aquí están los que vienen a la mente
- s es NULO
- s apunta a una cadena const que se mantiene en la memoria de solo lectura
- 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.
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
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
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 gcc
versiones 3.x compatibles -fwritable-strings
pero las versiones más nuevas no.
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