strdup () - cosa fa in C?


302

Qual è lo scopo della strdup()funzione in C?


44
c'è anche strdupa () (nella libreria GNU C), una bella funzione simile a strdup (), ma che alloca memoria nello stack. Il tuo programma non ha bisogno di liberare esplicitamente la memoria come nel caso di strdup (), verrà liberato automaticamente quando esci dalla funzione in cui è stato chiamato strdupa ()
dmityugov

11
strdupaè pericoloso e non dovrebbe essere usato a meno che tu non abbia già stabilito che strlenè molto piccolo. Ma poi potresti semplicemente usare un array di dimensioni fisse nello stack.
R .. GitHub FERMA AIUTANDO ICE il

4
@slacker google translate non è utile ... Cosa significa strdup/ strdupasignifica in polacco?
Haneefmubarak,

14
@haneefmubarak qui
anatolyg

Risposte:


372

Esattamente come sembra, supponendo che tu sia abituato al modo abbreviato in cui C e UNIX assegnano le parole, duplica le stringhe :-)

Tenendo presente che in realtà non fa parte dello standard ISO C stesso (a) (è una cosa POSIX), sta effettivamente facendo lo stesso del seguente codice:

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
}

In altre parole:

  1. Tenta di allocare memoria sufficiente per contenere la vecchia stringa (più un carattere '\ 0' per segnare la fine della stringa).

  2. Se l'assegnazione non è riuscita, imposta errnoper ENOMEMe ritorna NULLimmediatamente. L'impostazione di errnoto ENOMEMè qualcosa che mallocfa in POSIX, quindi non è necessario farlo esplicitamente nel nostro strdup. Se sei non conforme a POSIX, ISO C in realtà non mandato l'esistenza di ENOMEMcosì non ho incluso che qui (b) .

  3. Altrimenti l'allocazione ha funzionato, quindi copiamo la vecchia stringa nella nuova stringa (c) e restituiamo il nuovo indirizzo (che il chiamante è responsabile della liberazione a un certo punto).

Tieni presente che è la definizione concettuale. Qualsiasi scrittore degno del proprio stipendio può aver fornito un codice fortemente ottimizzato destinato al particolare processore utilizzato.


(a) Tuttavia, le funzioni che iniziano con stre una lettera minuscola sono riservate dallo standard per le direzioni future. Da C11 7.1.3 Reserved identifiers:

Ogni intestazione dichiara o definisce tutti gli identificatori elencati nella sua sotto-clausola associata e * facoltativamente dichiara o definisce gli identificatori elencati nella sua sotto-clausola delle direzioni della libreria future associate. **

Le direzioni future per string.hpossono essere trovate in C11 7.31.13 String handling <string.h>:

I nomi delle funzioni che iniziano con str, memo wcse una lettera minuscola possono essere aggiunti alle dichiarazioni del <string.h>intestazione.

Quindi dovresti probabilmente chiamarlo qualcos'altro se vuoi essere al sicuro.


(b) La modifica verrebbe sostanzialmente sostituita if (d == NULL) return NULL;da:

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

(c) Nota che lo uso strcpyper quello poiché questo mostra chiaramente l'intento. In alcune implementazioni, può essere più veloce (poiché si conosce già la lunghezza) da utilizzare memcpy, in quanto possono consentire il trasferimento dei dati in blocchi più grandi o in parallelo. Oppure potrebbe non :-) Mantra di ottimizzazione # 1: "misura, non indovinare".

In ogni caso, se decidessi di percorrere quella strada, faresti qualcosa del tipo:

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
Vale la pena notare che, come implica l'implementazione del campione di Pax, strdup (NULL) non è definito e non è qualcosa che ci si può aspettare di comportarsi in modo prevedibile.
Rilassati

2
Inoltre, penso che malloc () imposterà errno, quindi non dovresti impostarlo tu stesso. Penso.
Chris Lutz,

5
@Alcot, strdupè per quelle situazioni in cui si desidera allocare memoria heap per la copia di stringa. Altrimenti devi farlo da solo. Se già avete un tampone abbastanza grande (malloc'ed o altro), sì, l'uso strcpy.
paxdiablo,

2
@acgtyrant: se, per standard, intendi lo standard ISO (il vero standard C), no, non ne fa parte. Si è parte dello standard POSIX. Tuttavia, ci sono molte implementazioni C che lo forniscono, nonostante non facciano parte ufficiale dell'ISO C. Tuttavia, anche se non lo facessero, il quinto in questa risposta dovrebbe essere più che sufficiente.
paxdiablo,

2
Buon punto, @chux, mandati ISO solo { EDOM, EILSEQ, ERANGE }come codici di errore richiesti. Ho aggiornato la risposta per spiegare questo.
paxdiablo,

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

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

Forse il codice è un po 'più veloce di con strcpy()poiché \0non è necessario cercare nuovamente il carattere (lo era già con strlen()).


Grazie. Nella mia implementazione personale lo faccio anche "peggio". return memcpy(malloc(len), s, len);come preferisco il crash sull'allocazione piuttosto che il NULLfallimento dell'allocazione.
Patrick Schlüter,

3
La dereferenziazione di @tristopia NULLnon deve arrestarsi in modo anomalo; è indefinito. Se vuoi essere sicuro che si arresti in modo anomalo, scrivi uno emallocche chiama in abortcaso di errore.
Dave,

Lo so, ma la mia implementazione è garantita solo su Solaris o Linux (per la natura stessa dell'app).
Patrick Schlüter,

@tristopia: è bello avere l'abitudine di fare le cose nel modo migliore. Prendi l'abitudine di usarlo emallocanche se non è necessario su Solaris o Linux in modo da utilizzarlo in futuro quando scrivi codice su altre piattaforme.
ArtOfWarfare,

51

Inutile ripetere le altre risposte, ma tieni presente che strdup()può fare tutto ciò che vuole dal punto di vista C, poiché non fa parte di alcuno standard C. È tuttavia definito da POSIX.1-2001.


4
È strdup()portatile? No, non disponibile in ambiente non POSIX (comunque banalmente implementabile). Ma dire che una funzione POSIX può fare qualsiasi cosa è abbastanza pedante. POSIX è un altro standard buono come quello di C e ancora più popolare.
PP,

2
@BlueMoon Penso che il punto sia che un'implementazione C che non sostiene la conformità a POSIX possa comunque fornire una strdupfunzione come estensione. In una tale implementazione, non vi è alcuna garanzia che strdupsi comporti allo stesso modo della funzione POSIX. Non conosco nessuna di queste implementazioni, ma un'implementazione non malevola legittima potrebbe fornire char *strdup(char *)ragioni storiche e rifiutare i tentativi di passare a const char *.

Qual è la differenza tra standard C e POSIX? Con C standard intendi che non esiste nelle librerie C standard?
Koray Tugay,

@KorayTugay Sono standard diversi. Meglio trattarli come non correlati a meno che tu non sappia che lo standard per una particolare funzione C è conforme allo standard POSIX e che il tuo compilatore / libreria è conforme allo standard per quella funzione.
Matteo Leggi il

17

Da strdup man :

La strdup()funzione deve restituire un puntatore a una nuova stringa, che è un duplicato della stringa a cui punta s1. Il puntatore restituito può essere passato a free(). Viene restituito un puntatore null se non è possibile creare la nuova stringa.


4

strdup () esegue l'allocazione dinamica della memoria per l'array di caratteri incluso il carattere finale '\ 0' e restituisce l'indirizzo della memoria dell'heap:

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
}

Quindi, ciò che fa è darci un'altra stringa identica alla stringa fornita dal suo argomento, senza che ci venga richiesto di allocare memoria. Ma dobbiamo ancora liberarlo, più tardi.


3

Crea una copia duplicata della stringa passata eseguendo un malloc e strcpy della stringa passata. Il buffer di malloc viene restituito al chiamante, quindi la necessità di funzionare liberamente sul valore restituito.


3

strdupe strndupsono definiti nei sistemi POSIX compatibili come:

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

La funzione strdup () alloca memoria sufficiente per una copia della stringa str, esegue la copia e restituisce un puntatore ad essa.

Il puntatore può essere successivamente utilizzato come argomento per la funzione free.

Se la memoria disponibile è insufficiente, NULLviene restituita ed errnoè impostata su ENOMEM.

La funzione strndup () copia al massimo i lencaratteri dalla stringa strsempre null, terminando la stringa copiata.


1

La cosa più preziosa che fa è darti un'altra stringa identica alla prima, senza che tu debba allocare memoria (posizione e dimensione) da solo. Ma, come notato, è ancora necessario liberarlo (ma che non richiede nemmeno un calcolo della quantità.)


1

La dichiarazione:

strcpy(ptr2, ptr1);

è equivalente a (a parte il fatto che cambia i puntatori):

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

Mentre:

ptr2 = strdup(ptr1);

è equivalente a:

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

Pertanto, se desideri che la stringa che hai copiato sia utilizzata in un'altra funzione (poiché viene creata nella sezione heap), puoi utilizzare strdup, altrimenti strcpyè sufficiente,


0

La funzione strdup () è una scorciatoia per il duplicato di stringa, accetta un parametro come costante di stringa o valore letterale di stringa e alloca lo spazio sufficiente per la stringa e scrive i caratteri corrispondenti nello spazio allocato e infine restituisce l'indirizzo dell'allocato spazio alla routine chiamante.


1
L'argomento a strdupnon deve essere una costante di stringa, deve essere una stringa di tipo C, ovvero una matrice con terminazione null di char.
Chqrlie,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.