strdup () - co robi w C?


302

Jaki jest cel strdup()funkcji w C?


44
istnieje także strdupa () (w bibliotece GNU C), przyjemna funkcja podobna do strdup (), ale przydziela pamięć na stosie. Twój program nie musi jawnie zwalniać pamięci, tak jak w przypadku strdup (), zostanie ona zwolniona automatycznie po wyjściu z funkcji, w której wywołano strdupa ()
dmityugov 31.10.08

11
strdupajest niebezpieczny i nie należy go używać, chyba że już ustaliłeś, że strlenjest bardzo mały. Ale wtedy możesz po prostu użyć tablicy o stałym rozmiarze na stosie.
R .. GitHub ZATRZYMAJ LÓD

4
@slacker google translate nie jest pomocny ... Co oznacza strdup/ strdupaznaczy po polsku?
haneefmubarak

14
@haneefmubarak tutaj
anatolyg

Odpowiedzi:


372

Dokładnie tak, jak to brzmi, zakładając, że przyzwyczaiłeś się do skróconego sposobu, w jaki C i UNIX przypisują słowa, powiela ciągi :-)

Pamiętając, że tak naprawdę nie jest częścią samego standardu ISO C (a) (jest to kwestia POSIX), skutecznie robi to samo, co następujący kod:

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
}

Innymi słowy:

  1. Próbuje przydzielić wystarczającą ilość pamięci, aby pomieścić stary ciąg (plus znak „\ 0”, aby zaznaczyć koniec łańcucha).

  2. Jeśli przydział nie powiodła się, to ustawia errnosię ENOMEMi powraca NULLnatychmiast. Ustawienie od errnocelu ENOMEMjest coś mallocnie w POSIX, więc nie trzeba jawnie robić w naszym strdup. Jeśli jesteś nie POSIX zgodne, ISO C faktycznie nie wprowadzały istnienie ENOMEMwięc nie obejmowały że tutaj (b) .

  3. W przeciwnym razie przydział zadziałał, więc kopiujemy stary ciąg do nowego ciągu (c) i zwracamy nowy adres (który w pewnym momencie odpowiedzialny jest za zwolnienie).

Pamiętaj, że to jest definicja koncepcyjna. Każdy pisarz biblioteki warty swojej pensji mógł dostarczyć wysoce zoptymalizowany kod skierowany do konkretnego używanego procesora.


(a) Jednak funkcje rozpoczynające się strod małej litery są rezerwowane przez standard dla przyszłych kierunków. Od C11 7.1.3 Reserved identifiers:

Każdy nagłówek deklaruje lub definiuje wszystkie identyfikatory wymienione w powiązanej podpunkcie, a * opcjonalnie deklaruje lub definiuje identyfikatory wymienione w powiązanej przyszłej podpunkcie kierunków bibliotek. **

Przyszłe kierunki dla string.hmożna znaleźć w C11 7.31.13 String handling <string.h>:

Nazwy funkcji rozpoczynające się od str, memlub wcsmałą literą mogą być dodawane do deklaracji w <string.h>nagłówku.

Prawdopodobnie powinieneś to nazwać czymś innym, jeśli chcesz być bezpieczny.


(b) Zmiana polegałaby zasadniczo na if (d == NULL) return NULL;:

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

(c) Zauważ, że używam strcpydo tego, ponieważ wyraźnie pokazuje to zamiar. W niektórych implementacjach użycie może być szybsze (ponieważ znasz już długość) memcpy, ponieważ mogą one umożliwiać przesyłanie danych w większych porcjach lub równolegle. A może nie :-) Mantra optymalizacji nr 1: „mierz, nie zgaduj”.

W każdym razie, jeśli zdecydujesz się wybrać tę trasę, zrobiłbyś coś takiego:

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
Warto zauważyć, że jak sugeruje przykładowa implementacja Pax, strdup (NULL) jest niezdefiniowany i nie jest czymś, czego można się spodziewać w jakikolwiek przewidywalny sposób.
odpręż się

2
Myślę też, że malloc () ustawiłoby errno, więc nie powinieneś ustawiać go sam. Myślę.
Chris Lutz

5
@Alcot, strdupjest w tych sytuacjach, w których chcesz przydzielić pamięć sterty dla kopii ciągu. W przeciwnym razie musisz to zrobić sam. Jeśli już masz wystarczająco duży bufor (malloc'ed lub w inny sposób), tak, użycie strcpy.
paxdiablo

2
@acgtyrant: jeśli przez standard rozumiesz standard ISO (prawdziwy standard C), nie, nie jest on częścią tego. Jest to część standardu POSIX. Istnieje jednak wiele implementacji języka C, które ją zapewniają, mimo że nie są one oficjalną częścią ISO C. Jednak nawet jeśli nie, pięciowarstwowa odpowiedź w tej odpowiedzi powinna być więcej niż wystarczająca.
paxdiablo

2
Dobra uwaga, @chux, ISO nakazuje tylko { EDOM, EILSEQ, ERANGE }wymagane kody błędów. Zaktualizowałem odpowiedź na to konto.
paxdiablo

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

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

Może kod jest nieco szybszy niż z, strcpy()ponieważ \0znak nie musi być ponownie przeszukiwany (już był z strlen()).


Dzięki. W mojej osobistej realizacji robię to jeszcze „gorszym”. return memcpy(malloc(len), s, len);ponieważ wolę awarię przydziału niż NULLbłąd przydziału.
Patrick Schlüter,

3
Dereferencing @tristopia NULLnie musi się zawieszać; jest niezdefiniowany. Jeśli chcesz mieć pewność, że się zawiesi, napisz, emallocktóry wywołuje abortawarię.
Dave

Wiem o tym, ale moja implementacja gwarantuje, że będzie działała tylko w systemie Solaris lub Linux (z samej natury aplikacji).
Patrick Schlüter,

@tristopia: Dobrze jest mieć nawyk robienia rzeczy w najlepszy sposób. Nabierz nawyku używania, emallocnawet jeśli nie jest to konieczne w systemie Solaris lub Linux, dzięki czemu będziesz go używać w przyszłości, gdy będziesz pisać kod na innych platformach.
ArtOfWarfare

51

Nie ma sensu powtarzać innych odpowiedzi, ale pamiętaj, że strdup()może zrobić wszystko, co chce z perspektywy C, ponieważ nie jest częścią żadnego standardu C. Jest jednak zdefiniowany w POSIX.1-2001.


4
Jest strdup()przenośny? Nie, niedostępne w środowisku innym niż POSIX (w każdym razie możliwe do wdrożenia). Ale powiedzenie, że funkcja POSIX może zrobić wszystko, jest dość pedantyczne. POSIX to kolejny standard równie dobry jak C i jeszcze bardziej popularny.
PP

2
@BlueMoon Myślę, że chodzi o to, że implementacja języka C, która twierdzi, że nie jest zgodna z POSIX, może nadal zapewniać strdupfunkcję rozszerzenia. W przypadku takiej implementacji nie ma gwarancji, że strdupzachowuje się tak samo jak funkcja POSIX. Nie znam takich implementacji, ale zgodna z prawem nie-złośliwa implementacja może zapewnić char *strdup(char *)z przyczyn historycznych i odrzucić próby przekazania const char *.

Jaka jest różnica między standardem C a POSIX? Przez standard C rozumiesz, że nie istnieje w standardowych bibliotekach C?
Koray Tugay

@KorayTugay Są to różne standardy. Lepiej traktować je jako niepowiązane, chyba że wiesz, że standard dla określonej funkcji C jest zgodny ze standardem POSIX i że twój kompilator / biblioteka jest zgodny ze standardem dla tej funkcji.
Mateusz

17

Od strdup man :

strdup()Funkcja zwraca wskaźnik do nowego łańcucha, który stanowi kopię łańcucha wskazywanego przez s1. Zwrócony wskaźnik można przekazać do free(). Wskaźnik zerowy jest zwracany, jeśli nie można utworzyć nowego ciągu.


4

Funkcja strdup () wykonuje dynamiczny przydział pamięci dla tablicy znaków, w tym znaku końcowego „\ 0”, i zwraca adres pamięci sterty:

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
}

Tak więc daje nam kolejny ciąg identyczny z ciągiem podanym przez jego argument, bez konieczności przydzielania pamięci. Ale nadal musimy go uwolnić później.


3

Tworzy zduplikowaną kopię ciągu przekazanego przez uruchomienie malloc i przekazanie ciągu strcpy ciągu. Malloc'ed bufor jest zwracany do wywołującego, stąd potrzeba zwolnienia wartości zwracanej.


3

strdupi strndupsą zdefiniowane w systemach zgodnych z POSIX jako:

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

Funkcja strdup () przydziela wystarczającą ilość pamięci na kopię ciągu str, wykonuje kopię i zwraca do niej wskaźnik.

Wskaźnik może następnie zostać użyty jako argument funkcji free.

Jeśli dostępna jest niewystarczająca ilość pamięci, NULLjest zwracana i errnoustawiana na ENOMEM.

Funkcja strndup () kopiuje co najwyżej lenznaki z ciągu, strzawsze kończąc kopiowany ciąg.


1

Najcenniejszą rzeczą jest to, że daje ci kolejny ciąg identyczny z pierwszym, bez konieczności samodzielnego przydzielania pamięci (lokalizacji i rozmiaru). Ale, jak wspomniano, nadal musisz go zwolnić (ale to również nie wymaga obliczenia ilości).


1

Twierdzenie:

strcpy(ptr2, ptr1);

jest równoważne (poza tym, że zmienia to wskaźniki):

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

Natomiast:

ptr2 = strdup(ptr1);

jest równa:

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

Jeśli więc chcesz, aby ciąg, który skopiowałeś, był używany w innej funkcji (ponieważ jest tworzony w sekcji sterty), możesz użyć strdup, w przeciwnym razie strcpywystarczy,


0

Funkcja strdup () jest skrótem dla duplikatu ciągu, przyjmuje parametr jako stałą ciągu lub literał ciągu i przydziela wystarczającą ilość miejsca na ciąg i zapisuje odpowiednie znaki w przydzielonym miejscu, a na koniec zwraca adres przydzielonego miejsca miejsce na rutynę wywoływania.


1
Argument do strdupnie musi być ciągiem ciągłym, musi być ciągiem C, tzn. Tablicą zakończoną zerem char.
chqrlie,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.