2010-05-14 26 views
6

Después de saber que strncmp no es lo que parece y strlcpy no está disponible en mi sistema operativo (Linux), pensé que podría intentarlo y escribirlo yo mismo.C extraño comportamiento de matriz

Encontré una cita de Ulrich Drepper, el mantenedor libc, que publicó una alternativa al strlcpy usando mempcpy. Tampoco tengo mempcpy, pero su comportamiento fue fácil de replicar. En primer lugar, este es el caso de prueba que tiene

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

#define BSIZE 10 

void insp(const char* s, int n) 
{ 
    int i; 

    for (i = 0; i < n; i++) 
     printf("%c ", s[i]); 

    printf("\n"); 

    for (i = 0; i < n; i++) 
     printf("%02X ", s[i]); 

    printf("\n"); 

    return; 
} 

int copy_string(char *dest, const char *src, int n) 
{ 
    int r = strlen(memcpy(dest, src, n-1)); 
    dest[r] = 0; 

    return r; 
} 

int main() 
{ 
    char b[BSIZE]; 
    memset(b, 0, BSIZE); 

    printf("Buffer size is %d", BSIZE); 

    insp(b, BSIZE); 

    printf("\nFirst copy:\n"); 
    copy_string(b, "First", BSIZE); 
    insp(b, BSIZE); 
    printf("b = '%s'\n", b); 

    printf("\nSecond copy:\n"); 
    copy_string(b, "Second", BSIZE); 
    insp(b, BSIZE); 

    printf("b = '%s'\n", b); 

    return 0; 
} 

Y este es el resultado:

Buffer size is 10      
00 00 00 00 00 00 00 00 00 00 

First copy: 
F i r s t  b  =  
46 69 72 73 74 00 62 20 3D 00 
b = 'First' 

Second copy: 
S e c o n d   
53 65 63 6F 6E 64 00 00 01 00 
b = 'Second' 

Se puede ver en la representación interna (las líneas insp() creados) que hay algo de ruido mezclado, como la cadena de formato printf() en la inspección después de la primera copia, y una 0x01 extranjera en la segunda copia.

Las cadenas se copian intactas y manejan correctamente cadenas fuente demasiado largas (ignoremos el posible problema con pasar 0 como longitud a copy_string por ahora, lo arreglaré más adelante).

¿Pero por qué hay contenidos de matrices foráneas (a partir de la cadena de formato) dentro de mi destino? Es como si el destino en realidad se hubiera REDIMTADO para coincidir con la nueva longitud.

+1

¿Qué es un conjunto extraño? – WhirlWind

+0

En este caso me refiero a la cadena literal de printf, es decir, "b = '% s'", que se "entremezcló" con mi matriz "b", el destino – LukeN

+0

Tenga en cuenta que la fuente de 'strlcpy()' y 'strlcat () 'está bastante disponible bajo una licencia liberal: http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/lib/libc/string/strlcpy.c?content-type=text%2Fplain –

Respuesta

4

El final de la cadena está marcada con un \ 0, la memoria después de eso puede ser cualquier cosa, a menos que el sistema operativo la bloquee deliberadamente, entonces es lo que sea que haya quedado al azar.

Nota: en este caso, el "problema" no está en copy_string, está copiando exactamente 10chars, pero la memoria después de "first" en el código principal es simplemente aleatoria.

+0

Oh dios, no consideré que memcpy() no se detenga en '\ 0', estúpido, estúpido. – LukeN

2

Como no se detiene en el tamaño de fuente, se detiene en el tamaño de destino, que es más grande que el origen, por lo que está copiando la cadena fuente más un poco de basura pasada.

Puede ver fácilmente que está copiando su cadena fuente, con su terminador nulo. Pero dado que está copiando 10 bytes y las dos cadenas "Primera" y "Segunda" son más cortas que 10 bytes, también está copiando los bytes adicionales pasados.

1

El uso de memcpy(dest, src, n-1) invoca un comportamiento indefinido si dest y src no son ambos al menos n-1 de longitud. Por ejemplo, First\0 tiene seis caracteres de longitud, pero usted lee n-1 (9) caracteres; el contenido de la memoria más allá del final del literal de cadena no está definido, al igual que el comportamiento de su programa cuando lee esa memoria.

0

El "material" adicional está allí porque ha pasado el tamaño del búfer a memcpy. Copiará muchos caracteres, incluso cuando la fuente sea más corta.

me gustaría hacer las cosas un poco diferente:

void copy_string(char *dest, char const *src, size_t n) { 
    *dest = '\0'; 
    strncat(dest, src, n); 
} 

A diferencia strncpy, strncat se define a trabajar como la mayoría de la gente razonablemente esperar.

+0

La gente a menudo espera que 'strncat' funcione como' strlcat', es decir, esperan que tome la * longitud * completa del búfer de destino, mientras que en realidad toma el * remainder * de la longitud disponible para la concatenación. – AnT

+0

Usted realmente quiere 'if (n> 0) strncat (dest, src, n - 1)' (suponiendo que 'n' es el tamaño del buffer de destino). – caf