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:
Próbuje przydzielić wystarczającą ilość pamięci, aby pomieścić stary ciąg (plus znak „\ 0”, aby zaznaczyć koniec łańcucha).
Jeśli przydział nie powiodła się, to ustawia errno
się ENOMEM
i powraca NULL
natychmiast. Ustawienie od errno
celu ENOMEM
jest coś malloc
nie w POSIX, więc nie trzeba jawnie robić w naszym strdup
. Jeśli jesteś nie POSIX zgodne, ISO C faktycznie nie wprowadzały istnienie ENOMEM
więc nie obejmowały że tutaj (b) .
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ę str
od 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.h
można znaleźć w C11 7.31.13 String handling <string.h>
:
Nazwy funkcji rozpoczynające się od str
, mem
lub wcs
małą 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 strcpy
do 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
}