¿Por qué strcpy desencadena un error de segmentación con variables globales?


7

así que tengo algo de código C:

#include <stdio.h> 
#include <string.h> 

/* putting one of the "char*"s here causes a segfault */ 
void main() { 
    char* path = "/temp"; 
    char* temp; 
    strcpy(temp, path); 
} 

Esto compila, ejecuta, y se comporta como parece. Sin embargo, si uno o ambos punteros de caracteres se declaran como variables globales, strcpy produce un error de segmentación. ¿Por qué pasó esto? Evidentemente, hay un error en mi comprensión del alcance.

  0

Dado que no creo que realmente vaya a resolver el problema, solo comentaré que strncpy es muy recomendable sobre strcpy. 23 sep. 082008-09-23 18:36:46

  0

Josh Gagnon: En realidad, strncpy no coloca un terminador nulo cuando la longitud de la cadena de entrada es> = el búfer. strcpy es perfectamente seguro si sabes que el buffer es lo suficientemente grande. De lo contrario, use 'snprintf (buffer, buffer_len,"% s ", src)', ya que snprintf siempre pone un terminador nulo (solo asegúrese de que buffer_len> 0). 05 ago. 112011-08-05 06:40:39

  0

@Josh: Prefiero 'strlcpy'. Lamentablemente, glibc no es compatible, por lo que no tengo muchas posibilidades de usarlo. Supongo que siempre podría implementar mi propia implementación y agregarla a mi biblioteca de encabezado personal de nulo chequeo de malloc y funciones relacionadas con archivos, pero aún me molesta que muchas versiones de Unix lo tengan, mientras que Linux generalmente no lo tiene. 05 ago. 112011-08-05 15:42:30

  0

De hecho, eso casi me fastidió el último semestre de la universidad, ya que estaba probando un programa para la cuarta y última calificación de tarea para una de mis clases (que ya había perdido una de las tareas debido a pensar que debía hacerlo tres días después) en realidad fue) remota y accidentalmente conectado al servidor Unix en lugar del de Linux sin darse cuenta, y compiló y ejecutó muy bien allí, pero llegado el momento del examen recibo un correo electrónico de uno de los TA de la clase que dice "su programa no se ejecutará ni compilará en el cuadro de prueba [Linux] ". 05 ago. 112011-08-05 15:45:38

  0

(Afortunadamente había incluido líneas para 'strlcat' y' strncat' [diferencia similar a la existente entre 'strlcpy' y' strncpy' en cuanto a cómo funcionan y qué las implementa] versiones de la operación que se estaba realizando, por lo que era solo cuestión de comentar una línea y descomentar la línea después). No esperaba que tomara tantas cajas de comentarios. 05 ago. 112011-08-05 15:47:57

16

Como se menciona en otros carteles, la raíz del problema es que la temperatura no está inicializada. Cuando se declara como una variable automática en la pila, contendrá la basura que se encuentre en esa ubicación de memoria. Aparentemente para el compilador + CPU + sistema operativo que está ejecutando, la basura en esa ubicación es un puntero válido. El strcpy "tiene éxito" en que no segfault, pero en realidad copió una cadena a una ubicación arbitraria en otro lugar de la memoria. Este tipo de problema de corrupción en la memoria genera temor en los corazones de los programadores de C en todas partes, ya que es extraordinariamente difícil de depurar.

Cuando mueve la declaración de variables temp al alcance global, se coloca en la sección BSS y se pone a cero automáticamente. Los intentos de desreferencia * temp luego dan como resultado una segfault.

Cuando mueve la ruta * al ámbito global, * la temperatura sube una ubicación en la pila. La basura en esa ubicación aparentemente no es un puntero válido, por lo que desreferenciar * temp da como resultado una segfault.

  0

Como era de esperar, al intercambiar el orden de las declaraciones de variable el programa falla por defecto. ¡Gracias! 23 sep. 082008-09-23 19:58:04


8

que se olvidó de asignar e inicializar temp:

temp = (char *)malloc(TEMP_SIZE); 

Sólo asegúrese de TEMP_SIZE es lo suficientemente grande. También puede calcular esto en tiempo de ejecución, a continuación, asegúrese de que el tamaño es lo suficientemente (debe ser al menos strlen (camino))

  0

No es necesario inicializar la memoria a temp - strcpy se encargará de esto, incluso si solo está configurando el término inicial nulo. 23 sep. 082008-09-23 18:38:36

  0

Además, debe tener al menos strlen (ruta) +1 para ajustarse al término nulo o se generarán Cosas malas T (M). 23 sep. 082008-09-23 18:39:23

  0

Quiere decir que no necesita la línea "temp [0] = 0", ya que strcpy() agregará el terminador NULL por usted. 23 sep. 082008-09-23 18:45:32

  0

strcpy no importa a qué apunta la cadena de destino, siempre que sea lo suficientemente grande como para contener el resultado. La temperatura [0] = 0 no tiene sentido e incluso si la cadena fuente está vacía, seguirá siendo establecida por strcpy. Si desea borrar toda la cadena, debe usar memset (u otra cosa) 23 sep. 082008-09-23 18:47:43

  0

Lo siento, no entiendo 'strcpy' para 'strcat'. Solucioné mi respuesta 23 sep. 082008-09-23 18:49:59

  0

No es necesario arrojar el puntero devuelto por malloc - malloc devuelve un puntero de vacío y, por lo tanto, no se necesita lanzar. Además, lanzarlo podría hacerte daño porque podría ocultar problemas con malloc que no está definido correctamente (como la definición implícita de malloc está devolviendo un int) en esta línea de código. 04 jun. 092009-06-04 15:10:29


1

La parte importante señalar:
destino cadena dest debe ser lo suficientemente grande como para recibir el dupdo.
En su situación, la temperatura no tiene asignada la memoria para copiar.

Copiado de la página man de strcpy:

DESCRIPTION 
    The strcpy() function copies the string pointed to by src (including 
    the terminating '\0' character) to the array pointed to by dest. The 
    strings may not overlap, and the destination string dest must be large 
    enough to receive the copy. 

9

variable de la temperatura no apunta a ningún almacenamiento (memoria) y es inicializado.

si la temperatura se declara como char temp[32];, entonces el código funcionaría sin importar dónde se declare. Sin embargo, existen otros problemas para declarar la temperatura con un tamaño fijo como ese, pero esa es una pregunta para otro día.

Ahora, ¿por qué falla cuando se declara de forma global y no localmente? Suerte ...

Cuando se declara localmente, el valor de la temperatura proviene de cualquier valor que pueda haber en la pila en ese momento. Es una suerte que apunta a una dirección que no causa un bloqueo. Sin embargo, está destruyendo la memoria utilizada por otra persona.

Cuando se declaran globalmente, en la mayoría de los procesadores, estas variables se almacenarán en segmentos de datos que utilizarán cero demandas de páginas. Por lo tanto, char *temp aparece como si se hubiera declarado char *temp=0.


1

Invoca un comportamiento no definido, ya que no está inicializando la variable temp. Apunta a una ubicación aleatoria en la memoria, por lo que su programa puede funcionar, pero lo más probable es segfault. Usted necesita tener su cadena de destino sea una matriz, o tiene que apuntar a la memoria dinámica:

// Make temp a static array of 256 chars 
char temp[256]; 
strncpy(temp, 256, path); 

// Or, use dynamic memory 
char *temp = (char *)malloc(256); 
strncpy(temp, 256, path); 

Asimismo, el uso strncpy() en lugar de strcpy() para evitar desbordamientos de búfer.

  0

Adam, ¿es una buena idea tener todos esos números mágicos flotando? ;-) 23 sep. 082008-09-23 18:43:12

  0

que todavía tiene errores ya que si la cadena fuente es de 256 longitudes, entonces las cadenas no tienen una terminación nula. 23 sep. 082008-09-23 18:45:08

  0

No es necesario lanzar el puntero devuelto por malloc - malloc devuelve un puntero de vacío y, por lo tanto, no se necesita lanzar. Además, lanzarlo podría hacerte daño porque podría ocultar problemas con malloc que no está definido correctamente (como la definición implícita de malloc está devolviendo un int) en esta línea de código. 04 jun. 092009-06-04 15:11:20

  0

@Daniel Papasian: el molde no es necesario en C, pero es necesario en C++, y normalmente me gusta hacer que mi código C sea lo más compatible posible con C++. El uso de funciones definidas implícitamente sin declararlas está obsoleto en C, y no deben usarse en el nuevo código C. Siempre compilo con -Wall, que incluye -Wimplicit-function-declaration. 04 jun. 092009-06-04 15:28:08


3

Como se mencionó anteriormente, olvidó asignar espacio para la temperatura. Prefiero strdup a malloc+strcpy. Hace lo que quieres hacer.

  0

No sabía sobre strdup. Gracias. 23 sep. 082008-09-23 20:03:06


2

No, esto no funciona independientemente de las variables, simplemente parece que lo hizo porque tienes (no) suerte. Debe asignar espacio para almacenar el contenido de la cadena, en lugar de dejar la variable sin inicializar.

Las variables no inicializadas en la pila apuntan a ubicaciones de memoria prácticamente aleatorias.Si estas direcciones resultan ser válidas, su código pisoteará todo lo que haya allí, pero no obtendrá un error (pero puede generar errores desagradables relacionados con la corrupción de la memoria en cualquier parte de su código).

Globales fallan consistentemente porque generalmente se establecen en patrones específicos que apuntan a la memoria no asignada. Intentar desreferenciarlos le da un segfault inmediatamente (lo cual es mejor, dejarlo para más tarde hace que el error sea muy difícil de rastrear).


2

me gustaría volver a escribir primer fragmento de Adán como

// Make temp a static array of 256 chars 
char temp[256]; 
strncpy(temp, sizeof(temp), path); 
temp[sizeof(temp)-1] = '\0'; 

De esa manera:

1. don't have magic numbers laced through the code, and 
2. you guarantee that your string is null terminated. 

El segundo punto es por la pérdida de la última carbón de la cadena de origen si es > = 256 caracteres de largo.

  0

Al usar wchar_t, esto no funciona. Usar sizeof solo funciona con caracteres. Lo sé, esta es una pregunta acerca de los caracteres, pero me encuentro con muchos errores donde las personas usan sizeof con wchar_t. 23 sep. 082008-09-23 19:25:48