strdup () - что он делает в C?

302

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

Маной Сомнения
источник
44
есть также strdupa () (в библиотеке GNU C), хорошая функция, которая похожа на strdup (), но выделяет память в стеке. Ваша программа не должна явно освобождать память, как в случае с strdup (), она будет освобождена автоматически при выходе из функции, где была вызвана strdupa ()
dmityugov
11
strdupaявляется опасным и не должен использоваться, если вы уже определили, что strlenэто очень мало. Но тогда вы можете просто использовать массив фиксированного размера в стеке.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
4
@slacker google переводчик не помогает ... Что значит strdup/ strdupaозначает по-польски?
haneefmubarak
14
@haneefmubarak здесь
Анатолиг
Здесь есть разница между strdup и strcpy stackoverflow.com/ru/questions/14020380/strcpy-vs-strdup
Шива Пракаш

Ответы:

372

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

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

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

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

  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
}
paxdiablo
источник
8
Стоит отметить, что, как показывает пример реализации Pax, strdup (NULL) не определен, и вы не можете ожидать, что он будет вести себя любым предсказуемым образом.
расслабиться
2
Кроме того, я думаю, что malloc () установит errno, поэтому вам не нужно устанавливать его самостоятельно. Думаю.
Крис Латс
5
@Alcot, strdupдля тех ситуаций, когда вы хотите выделить кучу памяти для копирования строки. В противном случае вы должны сделать это самостоятельно. Если у вас уже есть достаточно большой буфер (malloc или другой), используйте strcpy.
paxdiablo
2
@acgtyrant: если под стандартом вы подразумеваете стандарт ISO (настоящий стандарт C), то нет, он не является его частью. Она является частью стандарта POSIX. Тем не менее, существует множество C реализаций , которые обеспечивают его, хотя и не официальной частью ISO C. Однако, даже если они этого не сделали, то пять-вкладыш в этом ответе должно быть более чем достаточно.
paxdiablo
2
Хороший вопрос, @chux, ISO требует только { EDOM, EILSEQ, ERANGE }кодов ошибок. Обновили ответ, чтобы учесть это.
paxdiablo
86
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()).

Патрик Шлютер
источник
Спасибо. В моей личной реализации я делаю это еще «хуже». return memcpy(malloc(len), s, len);поскольку я предпочитаю сбой при выделении, а не NULLсбой при выделении.
Патрик Шлютер
3
Разыменование @tristopia NULLне обязательно приводит к сбою; это не определено Если вы хотите быть уверенным, что он выйдет из строя, напишите сообщение, emallocкоторое вызывает abortпри неудаче.
Дейв
Я знаю это, но моя реализация гарантированно будет работать только в Solaris или Linux (по самой природе приложения).
Патрик Шлютер
@tristopia: Хорошо иметь привычку делать вещи наилучшим образом. Привыкайте использовать, emallocдаже если это не обязательно в Solaris или Linux, так что вы будете использовать его в будущем, когда будете писать код на других платформах.
ArtOfWarfare
51

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

Крис Янг
источник
4
Является ли strdup()портативный компьютер? Нет, недоступно в среде, отличной от POSIX (в любом случае, легко реализуемо). Но сказать, что функция POSIX может делать что угодно, довольно педантично. POSIX - это еще один стандарт, который так же хорош, как C, и еще более популярен.
ПП
2
@BlueMoon Я думаю, дело в том, что реализация C, не претендующая на соответствие POSIX, все же может предоставлять strdupфункцию в качестве расширения. В такой реализации нет гарантии, что она strdupведет себя так же, как и функция POSIX. Я не знаю ни о каких таких реализациях, но законная не злонамеренная реализация могла бы обеспечить char *strdup(char *)по историческим причинам и отклонить попытки передать в const char *.
В чем разница между стандартом C и POSIX? Под стандартом C вы подразумеваете, что он не существует в стандартных библиотеках C?
Корай Тугай
@KorayTugay Это разные стандарты. Лучше рассматривать их как несвязанные, если только вы не знаете, что стандарт для конкретной функции C соответствует стандарту POSIX и что ваш компилятор / библиотека соответствует стандарту для этой функции.
Мэтью Читал
17

От собеседника :

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

VonC
источник
4

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
}

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

Karshit
источник
3

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

jussij
источник
3

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всегда равной нулю, заканчивая скопированную строку.

Суджай Кумар
источник
1

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

dkretz
источник
1

Заявление:

strcpy(ptr2, ptr1);

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

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

В то время как:

ptr2 = strdup(ptr1);

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

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

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

Аль-Амин Бхуйян
источник
0

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

AnkitSablok
источник
1
Аргумент to strdupне обязательно должен быть строковой константой, он должен быть строкой C, то есть массивом с нулевым символом в конце char.
Chqrlie