Точно так же, как это звучит, если вы привыкли к сокращенному способу, которым C и UNIX назначают слова, он дублирует строки : -)
Помня, что на самом деле он не является частью самого стандарта ISO C (a) (это POSIX), он фактически делает то же самое, что и следующий код:
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
}
Другими словами:
Он пытается выделить достаточно памяти для хранения старой строки (плюс символ '\ 0', чтобы отметить конец строки).
Если выделение не удалось, он устанавливает errno
в ENOMEM
и немедленно возвращает NULL
. Установка errno
в ENOMEM
- это то, что malloc
делает в POSIX, поэтому нам не нужно явно делать это в нашем strdup
. Если вы не POSIX-совместимый, ISO C фактически не предписывает существование ENOMEM
, поэтому я не включил это сюда (b) .
В противном случае распределение сработало, поэтому мы копируем старую строку в новую строку (c) и возвращаем новый адрес (который вызывающий отвечает за освобождение в некоторый момент).
Имейте в виду, что это концептуальное определение. Любой писатель библиотеки, достойный своей зарплаты, мог предоставить сильно оптимизированный код для конкретного используемого процессора.
(a) Однако функции, начинающиеся с str
и строчной буквы, зарезервированы стандартом для будущих направлений. От C11 7.1.3 Reserved identifiers
:
Каждый заголовок объявляет или определяет все идентификаторы, перечисленные в его соответствующем подпункте, и * необязательно объявляет или определяет идентификаторы, перечисленные в соответствующем подпункте будущих направлений библиотеки. **
Будущие направления для string.h
можно найти в C11 7.31.13 String handling <string.h>
:
Имена функций, начинающиеся с str
, mem
или wcs
и строчная буква, могут быть добавлены в объявления в заголовке <string.h>
.
Так что вы, вероятно, должны называть это как-то иначе, если хотите быть в безопасности.
(b) Изменение в основном заменяет if (d == NULL) return NULL;
на:
if (d == NULL) {
errno = ENOMEM;
return NULL;
}
(c) Обратите внимание, что для этого я использую strcpy
, поскольку это ясно показывает намерение. В некоторых реализациях может быть быстрее (так как вы уже знаете длину) использовать memcpy
, так как они могут позволить передавать данные большими кусками или параллельно. Или не может :-) Оптимизация мантра № 1: «мера, не угадать».
В любом случае, если вы решите пойти по этому пути, вы должны сделать что-то вроде:
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
}