Саме так це звучить, якщо припустити, що ви звикли до скороченого способу, коли C і UNIX призначає слова, він дублює рядки :-)
Маючи на увазі, що насправді не є частиною самого стандарту ISO C (а) (це 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
}