En la página de manual de Linux de strncpy leí:
Si la longitud de src es menor que n, strncpy() escribe
bytes nulos a dest para garantizar que se escriba un total de n bytes.
En este caso límite (sin \0 al final de ambas cadenas) donde n>4:
char dest[8]="qqqqqqqq"; char src[4] = "abcd"; strncpy(dest, src, 5);
imprimir dest char por char da "abcdqqqq". Como src no tiene \0, no se copia \0 de src a dest, pero si entiendo correctamente la página de manual, se deben copiar otros 4 caracteres en cualquier caso, y deberían ser \0. Además, si src es "abc" (por lo que termina en NUL), dest contiene "abc\0\0qqq". Agrego todo el código que usé para probar (sí, va al carácter 8 para verlo también):
#include <stdio.h> #include <string.h> int main() { char dest[8]="qqqqqqqq"; char src[4] = "abcd"; // "abc" strncpy(dest, src, 5); for (int i=0; i<9; i++) printf("%2x ", dest[i]); putchar('\n'); return 0; }
¿Es esta una implementación defectuosa o me estoy perdiendo algo?
Si la longitud de src
Ese es el problema, su src
no es una cadena terminada en nulo, por lo que strncpy
no tiene idea de cuánto tiempo es. La página de manual también tiene un código de muestra que muestra cómo funciona strncpy
: se detiene en un byte nulo y, si no encuentra uno, simplemente sigue leyendo hasta que llega a n
. Y en su caso, lee los 4 caracteres de src
y obtiene el quinto de dest
porque es el siguiente en la memoria. (Puede intentar imprimir src[4]
, nada lo detendrá y obtendrá la q
de dest
. ¿No es agradable C?)
Parece que está utilizando strncpy()
con un argumento fuente que no es una cadena sino una matriz de caracteres. Entonces, ¿por qué esperas que se comporte con sensatez? El manual de Linux dice claramente que toma una cadena como argumento de origen y que la copia "incluido el byte nulo de terminación" (que supone tal byte). Tenga en cuenta que no existe tal cosa como una "cadena no terminada". Una matriz de caracteres es solo una matriz de caracteres, y no se garantiza que las funciones de cadena funcionen en ella.
Sin embargo, el comportamiento específico que está observando es esperado y está documentado.
La justificación de strncpy()
en el estándar POSIX contiene la siguiente oración:
Si no hay un byte de carácter
NUL
en los primerosn
bytes de la matriz a la que apuntas2
, el resultado no termina en nulo.
... donde s2
es lo que llamas src
.
El manual de strncpy()
en al menos Ubuntu y OpenBSD contiene textos similares.
Veamos esto lógicamente, usando citas de la página del manual de GNU con fecha 2017-09-15, como se incluye en Debian 10:
Si la longitud de src es menor que n , strncpy () escribe bytes nulos adicionales en dest para garantizar que se escriba un total de n bytes.
Como dices correctamente (más o menos), n == 5
. Entonces, ¿cuál es la "longitud de src "? Como todos deberíamos saber, las cadenas C tienen terminación nula. La página del manual sugiere esto:
La función strcpy () copia la cadena a la que apunta src , incluido el byte nulo final ('\0')... La función strncpy () es similar, excepto que se copian como máximo n bytes de src .
Sin embargo, su cadena no termina en nulo. Entonces, después de leer los 4 bytes que definiste, strncpy
intenta leer un quinto byte, ¿y qué encuentra? Aparentemente, el primer byte de dest
(en realidad, esto depende de la implementación, hasta donde yo sé). Todavía no ha encontrado un nulo para terminar la cadena, entonces, ¿sigue leyendo? No, porque como se dijo anteriormente:
se escriben un total de n bytes
y
se copian como máximo n bytes de src .
Entonces copia los 5 bytes "abcdq"
en dest
.
Tu dijiste:
los otros 4 caracteres deben copiarse en cualquier caso, y deben ser \0s
Esto implica una longitud total de 8 bytes. ¿De dónde sacarías esto? Esta es la "longitud" (o al menos la longitud declarada de la matriz) de dest
, no src
, y en cualquier caso se anularía por el hecho de que n < 8
.
El caso en el que src
termina en nulo es sencillo.
Así que no, la implementación no es defectuosa .
En realidad, tuviste suerte:
Las cuerdas no pueden superponerse
Lo hacen aquí, así que, técnicamente, cualquier cosa podría pasar. De hecho, un comentario sobre la pregunta dice que Valgrind advierte sobre esto.
y la cadena de destino dest debe ser lo suficientemente grande para recibir la copia. ¡Cuidado con los desbordamientos de búfer!
Este descuido con las longitudes de las cadenas debería ser una advertencia para tener mucho cuidado antes de crear una saturación del búfer.
En otra nota, si bien es posible que una función como strncpy
tenga un error, es muy poco probable . Las implementaciones estándar se han probado exhaustivamente en una amplia variedad de entornos. Cualquier error aparente es mucho más probable que sea un error en su propio código, o una falta de comprensión de su propio código, como es el caso aquí.