strdup (): ¿qué hace en C?

302

¿Cuál es el propósito de la strdup()función en C?

Dudas de Manoj
fuente
44
También hay strdupa () (en la biblioteca GNU C), una buena función que es similar a strdup (), pero asigna memoria en la pila. Su programa no necesita liberar la memoria explícitamente, ya que en el caso de strdup (), se liberará automáticamente cuando salga de la función donde se llamó strdupa ()
dmityugov
11
strdupaes peligroso y no debe usarse a menos que ya haya determinado que strlenes muy pequeño. Pero luego podría usar una matriz de tamaño fijo en la pila.
R .. GitHub DEJA DE AYUDAR AL HIELO
44
@slacker google translate no es útil ... ¿Qué significa strdup/ strdupasignifica en polaco?
haneefmubarak
14
@haneefmubarak aquí
anatolyg
Aquí está la diferencia entre strdup y strcpy stackoverflow.com/questions/14020380/strcpy-vs-strdup
Siva Prakash

Respuestas:

372

Exactamente cómo suena, suponiendo que esté acostumbrado a la forma abreviada en que C y UNIX asignan palabras, duplica las cadenas :-)

Teniendo en cuenta que en realidad no es parte del estándar ISO C en sí (a) (es una cosa POSIX), efectivamente está haciendo lo mismo que el siguiente código:

char *strdup(const char *src) {
    char *dst = malloc(strlen (src) + 1);  // Space for length plus nul
    if (dst == NULL) return NULL;          // No memory
    strcpy(dst, src);                      // Copy the characters
    return dst;                            // Return the new string
}

En otras palabras:

  1. Intenta asignar suficiente memoria para contener la cadena anterior (más un carácter '\ 0' para marcar el final de la cadena).

  2. Si la asignación no, se establece errnopara ENOMEMy vuelve NULLinmediatamente. La configuración de errnoto ENOMEMes algo que mallochace en POSIX, por lo que no necesitamos hacerlo explícitamente en nuestro strdup. Si no cumple con POSIX, ISO C en realidad no exige la existencia de ENOMEMlo que no he incluido aquí (b) .

  3. De lo contrario, la asignación funcionó, por lo que copiamos la cadena anterior a la nueva cadena (c) y devolvemos la nueva dirección (que la persona que llama es responsable de liberar en algún momento).

Tenga en cuenta que esa es la definición conceptual. Cualquier escritor de biblioteca que valga su salario puede haber proporcionado un código altamente optimizado dirigido al procesador particular que se está utilizando.


(a) Sin embargo, las funciones que comienzan con struna letra minúscula están reservadas por el estándar para futuras instrucciones. De C11 7.1.3 Reserved identifiers:

Cada encabezado declara o define todos los identificadores enumerados en su subcláusula asociada, y * opcionalmente declara o define los identificadores enumerados en su subcláusula de direcciones de biblioteca futura asociada. **

Las direcciones futuras para string.hse pueden encontrar en C11 7.31.13 String handling <string.h>:

Los nombres de funciones que comienzan con str, memo wcsy una letra minúscula se pueden agregar a las declaraciones en el <string.h>encabezado.

Por lo tanto, probablemente debería llamarlo de otra manera si desea estar seguro.


(b) El cambio básicamente se reemplazaría if (d == NULL) return NULL;con:

if (d == NULL) {
    errno = ENOMEM;
    return NULL;
}

(c) Tenga en cuenta que lo uso strcpypara eso, ya que eso muestra claramente la intención. En algunas implementaciones, puede ser más rápido (ya que ya conoce la longitud) de usar memcpy, ya que pueden permitir la transferencia de datos en fragmentos más grandes o en paralelo. O puede que no :-) Mantra de optimización # 1: "medir, no adivinar".

En cualquier caso, si decide ir por esa ruta, haría algo como:

char *strdup(const char *src) {
    size_t len = strlen(src) + 1;       // String plus '\0'
    char *dst = malloc(len);            // Allocate space
    if (dst == NULL) return NULL;       // No memory
    memcpy (dst, src, len);             // Copy the block
    return dst;                         // Return the new string
}
paxdiablo
fuente
8
Vale la pena señalar que, como implica la implementación de muestra de Pax, strdup (NULL) no está definido y no es algo que se pueda esperar que se comporte de ninguna manera predecible.
Descanse
2
Además, creo que malloc () establecería errno, por lo que no debería tener que configurarlo usted mismo. Yo creo que.
Chris Lutz
55
@Alcot, strdupes para aquellas situaciones en las que desea asignar memoria de montón para la copia de cadena. De lo contrario, tienes que hacerlo tú mismo. Si ya tiene un búfer lo suficientemente grande (mal ubicado o no), sí, úselo strcpy.
paxdiablo
2
@acgtyrant: si, por estándar, te refieres al estándar ISO (el verdadero estándar C), no, no es parte de él. Que es parte del estándar POSIX. Sin embargo, hay muchas implementaciones de C que lo proporcionan, a pesar de no ser una parte oficial de ISO C. Sin embargo, incluso si no lo hicieran, el trazo de cinco en esta respuesta debería ser más que suficiente.
paxdiablo
2
Buen punto, @chux, ISO exige solo { EDOM, EILSEQ, ERANGE }como códigos de error requeridos. Han actualizado la respuesta para dar cuenta de esto.
paxdiablo
86
char * strdup(const char * s)
{
  size_t len = 1+strlen(s);
  char *p = malloc(len);

  return p ? memcpy(p, s, len) : NULL;
}

Tal vez el código sea un poco más rápido que con strcpy()ya que \0no es necesario volver a buscar el carácter (ya estaba con strlen()).

Patrick Schlüter
fuente
Gracias. En mi implementación personal, lo hago aún "peor". return memcpy(malloc(len), s, len);como prefiero el bloqueo en la asignación en lugar de la NULLfalla en la asignación.
Patrick Schlüter
3
La desreferenciación de @tristopia NULLno tiene que bloquearse; Es indefinido. Si quieres asegurarte de que se bloquea, escribe una emallocllamada abortque falla.
Dave
Lo sé, pero mi implementación está garantizada para ejecutarse solo en Solaris o Linux (por la naturaleza misma de la aplicación).
Patrick Schlüter
@tristopia: Es bueno tener la costumbre de hacer las cosas de la mejor manera. Acostúmbrese a usarlo emallocincluso si no es necesario en Solaris o Linux, de modo que lo usará en el futuro cuando escriba código en otras plataformas.
ArtOfWarfare
51

No tiene sentido repetir las otras respuestas, pero tenga en cuenta que strdup()puede hacer lo que quiera desde una perspectiva C, ya que no forma parte de ningún estándar C. Sin embargo, está definido por POSIX.1-2001.

Chris Young
fuente
44
Es strdup()portátil? No, no está disponible en un entorno que no sea POSIX (implementable trivialmente de todos modos). Pero decir que una función POSIX puede hacer cualquier cosa es bastante pedante. POSIX es otro estándar que es tan bueno como el de C e incluso más popular.
PP
2
@BlueMoon Creo que el punto es que una implementación de C que afirma que no cumple con POSIX aún puede proporcionar una strdupfunción como una extensión. En tal implementación, no hay garantía de que se strdupcomporte de la misma manera que la función POSIX. No conozco ninguna de esas implementaciones, pero una implementación legítima no maliciosa podría proporcionar char *strdup(char *)por razones históricas y rechazar los intentos de pasar a const char *.
¿Cuál es la diferencia entre C standard y POSIX? Por estándar C, ¿quiere decir que no existe en las bibliotecas estándar C?
Koray Tugay
@KorayTugay Son estándares diferentes. Es mejor tratarlos como no relacionados, a menos que sepa que el estándar para una función C particular se ajusta al estándar POSIX, y que su compilador / biblioteca se ajusta al estándar para esa función.
Matthew leyó el
17

De strdup man :

La strdup()función devolverá un puntero a una nueva cadena, que es un duplicado de la cadena a la que apunta s1. El puntero devuelto se puede pasar a free(). Se devuelve un puntero nulo si no se puede crear la nueva cadena.

VonC
fuente
4

strdup () realiza una asignación de memoria dinámica para la matriz de caracteres, incluido el carácter final '\ 0' y devuelve la dirección de la memoria de almacenamiento dinámico:

char *strdup (const char *s)
{
    char *p = malloc (strlen (s) + 1);   // allocate memory
    if (p != NULL)
        strcpy (p,s);                    // copy string
    return p;                            // return the memory
}

Entonces, lo que hace es darnos otra cadena idéntica a la cadena dada por su argumento, sin requerir que asignemos memoria. Pero aún necesitamos liberarlo, más tarde.

Karshit
fuente
3

Realiza una copia duplicada de la cadena que se pasa ejecutando un malloc y strcpy de la cadena que se pasa. El búfer mallocado se devuelve a la persona que llama, de ahí la necesidad de ejecutar libremente el valor de retorno.

jussij
fuente
3

strdupy strndupse definen en sistemas compatibles con POSIX como:

char *strdup(const char *str);
char *strndup(const char *str, size_t len);

La función strdup () asigna suficiente memoria para una copia de la cadena str, hace la copia y le devuelve un puntero.

El puntero puede usarse posteriormente como argumento para la función free.

Si no hay suficiente memoria disponible, NULLse devuelve y errnose establece en ENOMEM.

La función strndup () copia en la mayoría de los lencaracteres de la cadena strsiempre nulo terminando la cadena copiada.

Sujay Kumar
fuente
1

Lo más valioso que hace es darle otra cadena idéntica a la primera, sin requerir que usted asigne memoria (ubicación y tamaño). Pero, como se señaló, aún necesita liberarlo (pero que tampoco requiere un cálculo de cantidad).

dkretz
fuente
1

La declaración:

strcpy(ptr2, ptr1);

es equivalente a (aparte del hecho de que esto cambia los punteros):

while(*ptr2++ = *ptr1++);

Mientras:

ptr2 = strdup(ptr1);

es equivalente a:

ptr2 = malloc(strlen(ptr1) + 1);
if (ptr2 != NULL) strcpy(ptr2, ptr1);

Entonces, si desea que la cadena que ha copiado se use en otra función (como se crea en la sección de montón), puede usar strdup, de lo contrario strcpyes suficiente,

MD Al Amin Bhuiyan
fuente
0

La función strdup () es una abreviatura de duplicado de cadena, toma un parámetro como una constante de cadena o un literal de cadena y asigna el espacio suficiente para la cadena y escribe los caracteres correspondientes en el espacio asignado y finalmente devuelve la dirección del asignado espacio para la rutina de llamadas.

AnkitSablok
fuente
1
El argumento a strdupno necesita ser una cadena constante, debe ser una cadena C, es decir, una matriz terminada en nulo char.
chqrlie