¿Cuál es la mejor alternativa para strncpy ()?

La función strncpy() no siempre termina en nulo, así que quiero saber cuál es la mejor alternativa que siempre termina en nulo. Quiero una función que si:

 strlen(src) >= n /*n is the number of characters to be copied from source*/ 

no hay necesidad de agregar más código como este:

 buf[sizeof(buf)-1] = 0; 

Si desconoce la longitud de la cadena que desea copiar, puede usar snprintf aquí. Esta función envía salida formateada a str . Actúa de manera similar a sprintf() , pero en cambio no escribe más bytes asignados por str . Si la cadena resultante es más larga que n-1 caracteres, los caracteres restantes se omiten. También incluye siempre el terminador nulo \0 , a menos que el tamaño del búfer sea 0 .

Esta sería una alternativa a strncpy() o strcpy() , si realmente no quieres usarlo. Sin embargo, agregar manualmente un terminador nulo al final de la cadena con strcpy() es siempre un enfoque simple y eficiente. Es muy normal en C agregar un terminador nulo al final de cualquier cadena procesada.

Aquí hay un ejemplo básico de usar sprintf() :

 #include  #include  #include  #define SIZE 1024 int main(void) { const size_t N = SIZE; char str[N]; const char *example = "Hello World"; snprintf(str, sizeof(str), "%s", example); printf("String = %s, Length = %zu\n", str, strlen(str)); return 0; } 

Que se imprime:

 String = Hello World, Length = 11 

Este ejemplo muestra que snprintf() copió sobre "Hello World" en str , y también agregó un terminador \0 al final.

Nota: strlen() solo funciona en cadenas terminadas en nulo, y causará un comportamiento indefinido si la cadena no termina en nulo. snprintf() también necesita más comprobación de errores, que se puede encontrar en la página del manual .

Como han dicho otros, este no es un enfoque eficiente, pero está ahí si lo buscas.

Como alternativa a la respuesta que sugirió snprintf() : (Nota: problema si n <= 0 )

 size_t sz = sizeof buf; /*n is the number of characters to be copied from source*/ int n = (int) sz - 1; snprintf(buf, sz, "%s", src); 

El código puede usar la siguiente precisión :

"... el número máximo de bytes que se escribirán para s conversiones de s . ..." C11 §7.21.6.1 4

 sprintf(buf, "%.*s", n, src); 

Tiene la ventaja sutil de que src no necesita ser una cadena , solo una matriz de caracteres.

Otra herramienta para cuerdas.

Usa la función strlcpy() . strlcpy() toma el tamaño completo del búfer de destino y garantiza la terminación NULL si hay espacio. Lea la página del manual para más información.

Si el comportamiento que desea es una versión strcpy de strcpy que copia el prefijo inicial más largo de la cadena de origen en un búfer de tamaño conocido, hay varias opciones para usted:

  • Puedes escribir una función hecha a medida que haga el trabajo:

     char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { size_t i; for (i = 0; i < size - 1 && src[i]; i++) { dest[i] = src[i]; } dest[i] = '\0'; } return dest; } 

    La mayoría de los sistemas BSD tienen una función strlcpy(char *dest, const char *src, size_t n); Eso opera lo mismo. El orden de sus argumentos es confuso ya que n es el tamaño de la matriz dest , pero viene después del argumento src .

  • Puedes usar strncat() :

     char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { *dest = '\0'; strncat(dest, src, n - 1); } return dest; } 
  • Puede usar snprintf() o sprintf() , pero se siente como usar una prensa hidráulica para perforar un clavo:

     snprintf(dest, size, "%s", src); 

    Alternativamente:

     if (size > 0) { sprintf(dest, "%.*s", (int)(size - 1), src); } 
  • Puede usar strlen() y memcpy() , pero esto solo es posible si sabe que la fuente ptr apunta a una cadena terminada en nulo. También es menos eficiente que las dos soluciones anteriores si la cadena de origen es mucho más larga que la matriz de destino:

     char *safe_strcpy(char *dest, size_t size, char *src) { if (size > 0) { size_t len = strlen(src); if (len >= size) len = size - 1; memcpy(dest, src, len); dest[len] = '\0'; } return dest; } 
  • Podría usar strncpy() y forzar la terminación nula. Esto sería ineficiente si la matriz de destino es grande porque strncpy() también llena el rest de la matriz de destino. Anulará los bytes si la cadena de origen es más corta. La semántica de esta función es muy poco intuitiva, poco comprendida y propensa a errores. Incluso cuando se usan correctamente, las ocurrencias de strncpy() son errores que esperan que ocurran, ya que el siguiente progtwigdor, más audaz pero menos inteligente, puede alterar el código e introducirlos en un bash de optimizar el código que no entiende. Juega seguro: evita esta función.

Como ilustración de tener que usar una alternativa a strncpy() , considere Git 2.19 (Q3 2018) que encuentra que es demasiado fácil usar incorrectamente las funciones de la API del sistema como strcat (); strncpy() ; … estas funciones seleccionadas ahora están prohibidas en este código base y causarán una falla de comstackción.

Ese parche enumera varias alternativas, lo que lo hace relevante para esta pregunta.

Consulte commit e488b7a , commit cc8fdae , commit 1b11b64 (24 de julio de 2018), y commit c8af66a (26 de julio de 2018) por Jeff King ( peff ) .
(Combinado por Junio ​​C Hamano – gitster – in commit e28daf2 , 15 de agosto de 2018)

banned.h : marca strncpy() como prohibido

La función strncpy() es menos horrible que strcpy() , pero aún así es bastante fácil de usar mal debido a su divertida semántica de terminación.
Es decir, que si trunca omite el terminador NUL, y debe recordar agregarlo usted mismo. Incluso si lo usa correctamente, a veces es difícil para un lector verificar esto sin buscar el código.
Si estás pensando en usarlo, considera en su lugar:

  • strlcpy() si realmente necesita una cadena truncada pero terminada en NUL (proporcionamos una versión compatible, por lo que siempre está disponible)
  • xsnprintf() si está seguro de que lo que está copiando debería caber
  • strbuf o xstrfmt() si necesita manejar cadenas asignadas a heap de longitud arbitraria.

Tenga en cuenta que hay una instancia de strncpy en compat/regex/regcomp.c , que está bien (asigna una cadena suficientemente grande antes de copiar).
Pero esto no NO_REGEX=1 lista de prohibición incluso cuando se comstack con NO_REGEX=1 , porque:

  1. no usamos git-compat-util.h cuando lo comstackmos (en lugar de eso, dependemos de los archivos del sistema de la biblioteca ascendente); y
  2. Está en un #ifdef DEBUG#ifdef DEBUG

Ya que no banned.h código banned.h , es mejor dejarlo para evitar que nuestra divergencia sea mínima.

La función strcpy siempre termina en nulo. Por supuesto, debe incluir código para evitar desbordamientos de búfer, por ejemplo:

 char buf[50]; if (strlen(src) >= sizeof buf) { // do something else... } else strcpy(buf, src);