strdup () - что он делает в C? - PullRequest
288 голосов
/ 31 октября 2008

Какова цель функции strdup() в C?

Ответы [ 10 ]

351 голосов
/ 31 октября 2008

Точно так же, как это звучит, если вы привыкли к сокращенному способу, которым 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
}

Другими словами:

  1. Он пытается выделить достаточно памяти для хранения старой строки (плюс символ '\ 0', чтобы отметить конец строки).

  2. Если выделение не удалось, он устанавливает errno в ENOMEM и немедленно возвращает NULL. Установка errno в ENOMEM - это то, что malloc делает в POSIX, поэтому нам не нужно явно делать это в нашем strdup. Если вы не POSIX-совместимый, ISO C фактически не предписывает существование ENOMEM, поэтому я не включил это сюда (b) .

  3. В противном случае распределение сработало, поэтому мы копируем старую строку в новую строку (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
}
83 голосов
/ 27 ноября 2009
char * strdup(const char * s)
{
  size_t len = 1+strlen(s);
  char *p = malloc(len);

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

Возможно, код работает немного быстрее, чем с strcpy(), так как символ \0 не нужно искать снова (он уже был с strlen()).

50 голосов
/ 31 октября 2008

Нет смысла повторять другие ответы, но учтите, что strdup() может делать все, что захочет, с точки зрения C, поскольку это не является частью какого-либо стандарта C. Однако он определен POSIX.1-2001.

17 голосов
/ 31 октября 2008

С человек в строю :

Функция strdup() должна возвращать указатель на новую строку, которая является дубликатом строки, на которую указывает s1. Возвращенный указатель может быть передан в free(). Пустой указатель возвращается, если новая строка не может быть создана.

4 голосов
/ 27 июня 2013

strdup () выполняет динамическое выделение памяти для массива символов, включая конечный символ '\ 0', и возвращает адрес памяти кучи:

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
}

Итак, это дает нам еще одну строку, идентичную строке, заданной ее аргументом, без необходимости выделять память. Но мы все еще должны освободить его, позже.

3 голосов
/ 17 декабря 2014

strdup и strndup определены в POSIX-совместимых системах как:

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

Функция strdup () выделяет достаточно памяти для копирования Строка str, выполняет копирование и возвращает указатель на нее.

Указатель может впоследствии использоваться в качестве аргумента функции free.

Если недостаточно памяти, возвращается NULL и errno устанавливается на ENOMEM.

Функция strndup () копирует не более len символов из строки str всегда с нулем, заканчивая скопированную строку.

3 голосов
/ 31 октября 2008

Создает дублированную копию передаваемой строки, выполняя malloc и strcpy передаваемой строки. Буфер malloc возвращается вызывающей стороне, следовательно, необходимо запустить free для возвращаемого значения.

1 голос
/ 03 декабря 2015

утверждение:

strcpy(ptr2, ptr1);

эквивалентно (кроме факта, что это изменяет указатели):

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

Принимая во внимание:

ptr2 = strdup(ptr1);

эквивалентно:

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

Итак, если вы хотите, чтобы скопированная строка использовалась в другой функции (как она создается в разделе кучи), вы можете использовать strdup, иначе достаточно strcpy,

1 голос
/ 31 октября 2008

Самое ценное, что он делает, - это дает вам еще одну строку, идентичную первой, без необходимости выделять память (расположение и размер) самостоятельно. Но, как уже отмечалось, вам все равно нужно освободить его (но для этого также не требуется вычисление количества).

0 голосов
/ 13 июля 2014

Функция strdup () является сокращением для дубликата строки, она принимает параметр в виде строковой константы или строкового литерала и выделяет достаточно места для строки, записывает соответствующие символы в выделенном пространстве и в конце возвращает адрес выделенного пространства для вызывающей подпрограммы.

...