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

7 minutos de lectura

avatar de usuario
dems98

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; 

  • Mirar strncpy_s

    – Yuri Ivaskevych

    26 de enero de 2017 a las 8:58

  • snprintf siempre termina en nulo a menos que el tamaño del búfer sea 0

    –MM

    26 de enero de 2017 a las 9:19

  • ¿Por qué importa si tiene que agregar la terminación nula usted mismo o si la función lo hace por usted? E incluso si tiene requisitos tan irracionales, simplemente puede envolver el código dentro de otra función.

    – Lundin

    26 de enero de 2017 a las 9:56

  • Nótese que el tercer argumento para strncpy es el tamaño del búfer de salida, no el número de caracteres que se van a copiar

    –MM

    26 de enero de 2017 a las 10:25

  • Sus requisitos son todavía vagos. Por ejemplo, explique si esta función debe manejar el caso de strlen(src) < n (y si es así, qué quieres que suceda en ese caso)

    –MM

    26 de enero de 2017 a las 10:26

avatar de usuario
chqrlie

Si el comportamiento que desea es una versión truncada 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:

  • Puede escribir una función 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); que opera igual. El orden de sus argumentos es confuso ya que n suele ser del tamaño de la dest matriz, pero viene después de la src argumento.

  • Puedes usar strncat():

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

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

    Alternativamente:

      if (size > 0) {
          sprintf(dest, "%.*s", (int)(size - 1), src);
      }
    
  • Puedes usar strlen() y memcpy(), pero esto solo es posible si sabe que el puntero de origen 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;
      }
    

    La ineficiencia se puede evitar con strnlen() si está disponible en el sistema de destino:

      char *safe_strcpy(char *dest, size_t size, char *src) {
          if (size > 0) {
              size_t len = strnlen(src, size - 1);
              memcpy(dest, src, len);
              dest[len] = '\0';
          }
          return dest;
      }
    
  • podrías usar strncpy() y fuerza la terminación nula. Esto sería ineficiente si la matriz de destino es grande porque strncpy() también llena el resto de la matriz de destino con bytes nulos si la cadena de origen es más corta. La semántica de esta función es muy contraria a la intuición, mal entendida y propensa a errores. Incluso cuando se usa correctamente, las ocurrencias de strncpy() son errores esperando para morder, ya que el próximo programador, más audaz pero menos inteligente, podría alterar el código e introducirlos en un intento de optimizar el código que no entiende completamente. Juega a lo seguro: evita esta función.

Otro aspecto de esta pregunta es la capacidad de la persona que llama para detectar truncamiento. Las implementaciones anteriores de safe_strcpy devolver el puntero de destino, como strcpy hace, por lo tanto, no proporciona ninguna información a la persona que llama. snprintf() devuelve un int que representa la cantidad de caracteres que se habrían copiado si la matriz de destino fuera lo suficientemente grande, en este caso, el valor devuelto es strlen(src) convertido a intque permite a la persona que llama detectar truncamientos y otros errores.

Aquí hay otra función más apropiada para componer una cadena de diferentes partes:

size_t strcpy_at(char *dest, size_t size, size_t pos, const char *src) {
    size_t len = strlen(src);
    if (pos < size) {
        size_t chunk = size - pos - 1;
        if (chunk > len)
            chunk = len;
        memcpy(dest + pos, src, chunk);
        dest[pos + chunk] = '\0';
    }
    return pos + len;
}

Esta función se puede utilizar en secuencias sin un comportamiento indefinido:

void say_hello(const char **names, size_t count) {
    char buf[BUFSIZ];
    char *p = buf;
    size_t size = sizeof buf;

    for (;;) {
        size_t pos = strcpy_at(p, size, 0, "Hello");
        for (size_t i = 0; i < count; i++) {
            pos = strcpy_at(p, size, pos, " ");
            pos = strcpy_at(p, size, pos, names[i]);
        }
        pos = strcpy_at(p, size, pos, "!");
        if (pos >= size && p == buf) {
            // allocate a larger buffer if required
            p = malloc(size = pos + 1);
            if (p != NULL)
                continue;
            p = buf;
        }
        printf("%s\n", p);
        if (p != buf)
            free(p);
        break;
    }
}

Un enfoque equivalente para snprintf también sería útil, pasando pos por dirección:

size_t snprintf_at(char *s, size_t n, size_t *ppos, const char *format, ...) {
    va_list arg;
    int ret;
    size_t pos = *ppos;

    if (pos < n) {
        s += pos;
        n -= pos;
    } else {
        s = NULL;
        n = 0;
    }
    va_start(arg, format);
    ret = snprintf(s, n, format, arg);
    va_end(arg);

    if (ret >= 0)
        *ppos += ret;

    return ret;
}

paso pos por dirección en lugar de por valor permite snprintf_at regresar snprintfel valor de retorno de , que puede ser -1 en caso de error de codificación.

Él strcpy la función 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);

  • No estoy seguro de por qué esto fue rechazado. Si conoce el tamaño del búfer, es una alternativa obvia. Si no conoce el tamaño del búfer, no tiene forma de hacer una copia segura de todos modos.

    –Andrew Henle

    30 sep 2019 a las 22:24

avatar de usuario
Comunidad

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 lo siguiente precisión:

“… el número máximo de bytes que se escribirán para s conversiones …” C11 §7.21.6.1 4

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

Tiene la sutil ventaja de que src no necesita ser un cuerdasolo una matriz de caracteres.

Otra herramienta para cuerdas.

  • No estoy seguro de por qué esto fue rechazado. Si conoce el tamaño del búfer, es una alternativa obvia. Si no conoce el tamaño del búfer, no tiene forma de hacer una copia segura de todos modos.

    –Andrew Henle

    30 sep 2019 a las 22:24

avatar de usuario
Roberto Caboni

Usar strlcpy() función.

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

  • no creo strlcpy es estándar y yo preferiría funciones estándar aquí.

    – cadaniluk

    26 de enero de 2017 a las 9:07

  • ahí está el strcpy_s función que es estándar según C11, pero los compiladores no necesitan admitirlo. Todavía es más estándar que strlcpy. Y no hay ninguna razón real para usar ninguno de ellos…

    – Lundin

    26 de enero de 2017 a las 9:53


  • Estoy bastante seguro de que te refieres strncpy_s. Pero strncpy_s es propenso a errores de uno en uno. Aparentemente, uno tiene que especificar el tamaño del búfer de destino menos uno. strlcpy es mucho más natural.

    – Sven

    26 de marzo de 2018 a las 10:59

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad