strdup () - que fait-il en C?


302

Quel est le but de la strdup()fonction en C?


44
il y a aussi strdupa () (dans la bibliothèque GNU C), une belle fonction similaire à strdup (), mais alloue de la mémoire sur la pile. Votre programme n'a pas besoin de libérer la mémoire explicitement comme dans le cas avec strdup (), il sera libéré automatiquement lorsque vous quitterez la fonction où strdupa () a été appelée
dmityugov

11
strdupaest dangereux et ne doit pas être utilisé sauf si vous avez déjà déterminé qu'il strlenest très petit. Mais alors vous pouvez simplement utiliser un tableau de taille fixe sur la pile.
R .. GitHub STOP HELPING ICE

4
@slacker google translate n'est pas utile ... Que signifie strdup/ strdupasignifie en polonais?
haneefmubarak

14
@haneefmubarak ici
anatolyg

Voici la différence entre strdup et strcpy stackoverflow.com/questions/14020380/strcpy-vs-strdup
Siva Prakash

Réponses:


372

Exactement ce à quoi cela ressemble, en supposant que vous êtes habitué à la façon abrégée dont C et UNIX attribuent les mots, il duplique les chaînes :-)

En gardant à l'esprit que cela ne fait pas partie de la norme ISO C elle-même (a) (c'est une chose POSIX), cela fait en fait la même chose que le code suivant:

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 d'autres termes:

  1. Il essaie d'allouer suffisamment de mémoire pour contenir l'ancienne chaîne (plus un caractère «\ 0» pour marquer la fin de la chaîne).

  2. Si l'allocation échoue, il met errnoà ENOMEMet retourne NULLimmédiatement. La définition de errnoto ENOMEMest quelque chose qui se mallocfait dans POSIX, nous n'avons donc pas besoin de le faire explicitement dans notre strdup. Si vous n'êtes pas conforme à POSIX, ISO C n'exige pas réellement l'existence de ENOMEMdonc je ne l'ai pas inclus ici (b) .

  3. Sinon, l'allocation a fonctionné, nous copions donc l'ancienne chaîne dans la nouvelle chaîne (c) et renvoyons la nouvelle adresse (que l'appelant est responsable de libérer à un moment donné).

Gardez à l'esprit que c'est la définition conceptuelle. Tout écrivain de bibliothèque digne de ce nom peut avoir fourni un code fortement optimisé ciblant le processeur particulier utilisé.


(a) Cependant, les fonctions commençant par stret une lettre minuscule sont réservées par la norme pour les directions futures. De C11 7.1.3 Reserved identifiers:

Chaque en-tête déclare ou définit tous les identificateurs répertoriés dans sa sous-clause associée, et * déclare ou définit facultativement les identificateurs répertoriés dans sa future sous-clause de directions de bibliothèque associées. **

Les orientations futures pour string.hse trouvent dans C11 7.31.13 String handling <string.h>:

Les noms de fonctions commençant par str, memou wcset une lettre minuscule peuvent être ajoutés aux déclarations dans l'en- <string.h>tête.

Donc, vous devriez probablement l'appeler autrement si vous voulez être en sécurité.


(b) Le changement serait essentiellement remplacé if (d == NULL) return NULL;par:

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

(c) Notez que j'utilise strcpypour cela car cela montre clairement l'intention. Dans certaines implémentations, il peut être plus rapide (car vous connaissez déjà la longueur) à utiliser memcpy, car ils peuvent permettre de transférer les données en plus gros morceaux, ou en parallèle. Ou ce n'est peut-être pas le cas :-) Mantra d'optimisation # 1: "mesurer, ne pas deviner".

Dans tous les cas, si vous décidez de suivre cette voie, vous feriez quelque chose comme:

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
}

8
Il convient de noter que, comme l'implémentation de l'exemple de Pax l'indique, strdup (NULL) n'est pas défini et n'est pas quelque chose que vous pouvez vous attendre à se comporter de manière prévisible.
détendez-vous

2
De plus, je pense que malloc () définirait errno, vous ne devriez donc pas avoir à le définir vous-même. Je pense.
Chris Lutz

5
@Alcot, strdupest pour les situations où vous souhaitez que la mémoire de tas soit allouée pour la copie de chaîne. Sinon, vous devez le faire vous-même. Si vous avez déjà avoir un tampon assez grand (malloc ou autrement), oui, utilisation strcpy.
paxdiablo

2
@acgtyrant: si, par norme, vous voulez dire la norme ISO (la vraie norme C), non, elle n'en fait pas partie. Il fait partie du standard POSIX. Cependant, il existe de nombreuses implémentations C qui le fournissent, bien qu'elles ne fassent pas officiellement partie de l'ISO C. Cependant, même si ce n'est pas le cas, les cinq lignes de cette réponse devraient être plus que suffisantes.
paxdiablo

2
Bon point, @chux, les mandats ISO ne sont { EDOM, EILSEQ, ERANGE }que des codes d'erreur requis. J'ai mis à jour la réponse pour en tenir compte.
paxdiablo

86
char * strdup(const char * s)
{
  size_t len = 1+strlen(s);
  char *p = malloc(len);

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

Peut-être que le code est un peu plus rapide qu'avec, strcpy()car le caractère \0n'a pas besoin d'être recherché à nouveau (il l'était déjà avec strlen()).


Merci. Dans ma mise en œuvre personnelle, je fais encore pire. return memcpy(malloc(len), s, len);car je préfère le crash sur l'allocation plutôt que l' NULLéchec de l'allocation.
Patrick Schlüter du

3
Le déréférencement de @tristopia NULLn'a pas à se bloquer; ce n'est pas défini. Si vous voulez être sûr qu'il se bloque, écrivez un emallocqui appelle abortfail.
Dave

Je le sais, mais mon implémentation est garantie de fonctionner uniquement sur Solaris ou Linux (par la nature même de l'application).
Patrick Schlüter

@tristopia: C'est bien d'avoir l'habitude de faire les choses de la meilleure façon. Prenez l'habitude d'utiliser emallocmême si ce n'est pas nécessaire sur Solaris ou Linux afin de pouvoir l'utiliser à l'avenir lorsque vous écrivez du code sur d'autres plates-formes.
ArtOfWarfare

51

Inutile de répéter les autres réponses, mais veuillez noter que cela strdup()peut faire tout ce qu'il veut d'un point de vue C, car il ne fait partie d'aucune norme C. Il est cependant défini par POSIX.1-2001.


4
Est strdup()portable? Non, non disponible dans un environnement non-POSIX (de toute façon implémentable de manière triviale). Mais dire qu'une fonction POSIX peut tout faire est assez pédant. POSIX est un autre standard aussi bon que les C et encore plus populaire.
PP

2
@BlueMoon Je pense que le fait est qu'une implémentation C qui ne revendique aucune conformité à POSIX peut toujours fournir une strdupfonction en tant qu'extension. Sur une telle implémentation, il n'y a aucune garantie que cela strdupse comporte de la même manière que la fonction POSIX. Je ne connais pas de telles implémentations, mais une implémentation légitime non malveillante pourrait fournir char *strdup(char *)des raisons historiques et rejeter les tentatives de passer a const char *.

Quelle est la différence entre la norme C et POSIX? Par standard C, vous voulez dire qu'il n'existe pas dans les bibliothèques standard C?
Koray Tugay

@KorayTugay Ce sont des normes différentes. Mieux vaut les traiter comme sans rapport, sauf si vous savez que la norme pour une fonction C particulière est conforme à la norme POSIX et que votre compilateur / bibliothèque est conforme à la norme pour cette fonction.
Matthew Read

17

De l' homme strdup :

La strdup()fonction doit renvoyer un pointeur vers une nouvelle chaîne, qui est un doublon de la chaîne pointée par s1. Le pointeur renvoyé peut être transmis à free(). Un pointeur nul est renvoyé si la nouvelle chaîne ne peut pas être créée.


4

strdup () effectue l'allocation de mémoire dynamique pour le tableau de caractères, y compris le caractère de fin «\ 0» et renvoie l'adresse de la mémoire de tas:

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
}

Donc, ce qu'il fait, c'est nous donner une autre chaîne identique à la chaîne donnée par son argument, sans nous obliger à allouer de la mémoire. Mais nous devons encore le libérer plus tard.


3

Il crée une copie en double de la chaîne transmise en exécutant un malloc et strcpy de la chaîne transmise. Le tampon mallocé est renvoyé à l'appelant, d'où la nécessité de s'exécuter librement sur la valeur de retour.


3

strdupet strndupsont définis dans les systèmes compatibles POSIX comme:

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

La fonction strdup () alloue suffisamment de mémoire pour une copie de la chaîne str, effectue la copie et lui renvoie un pointeur.

Le pointeur peut ensuite être utilisé comme argument de la fonction free.

Si la mémoire disponible est insuffisante, NULLest renvoyée et errnoest définie sur ENOMEM.

La fonction strndup () copie au maximum la plupart des lencaractères de la chaîne, strmettant ainsi fin à la chaîne copiée.


1

La chose la plus précieuse qu'il fait est de vous donner une autre chaîne identique à la première, sans vous obliger à allouer de la mémoire (emplacement et taille) vous-même. Mais, comme indiqué, vous devez toujours le libérer (mais qui ne nécessite pas non plus de calcul de quantité.)


1

La déclaration:

strcpy(ptr2, ptr1);

est équivalent à (à part le fait que cela change les pointeurs):

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

Tandis que:

ptr2 = strdup(ptr1);

est équivalent à:

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

Donc, si vous voulez que la chaîne que vous avez copiée soit utilisée dans une autre fonction (telle qu'elle est créée dans la section tas), vous pouvez utiliser strdup, sinon strcpyc'est assez,


0

La fonction strdup () est un raccourci pour la duplication de chaîne, elle prend un paramètre comme une constante de chaîne ou un littéral de chaîne et alloue juste assez d'espace pour la chaîne et écrit les caractères correspondants dans l'espace alloué et renvoie finalement l'adresse de l'alloué espace à la routine d'appel.


1
L'argument to strdupn'a pas besoin d'être une constante de chaîne, il doit être une chaîne C, c'est-à-dire un tableau terminé par null de char.
chqrlie
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.