strdup () - що це робить у C?


302

Яке призначення strdup()функції в С?


44
є також strdupa () (у бібліотеці GNU C), приємна функція, схожа на strdup (), але виділяє пам'ять на стеці. Вашій програмі не потрібно звільнити пам'ять явно, як у випадку з strdup (), вона буде звільнена автоматично, коли ви вийдете з функції, де викликався strdupa ()
dmityugov

11
strdupaє небезпечним і не повинен використовуватися, якщо ви вже не визначили, що strlenце дуже мало. Але тоді ви могли просто використовувати масив фіксованого розміру на стеку.
R .. GitHub СТОП ДОПОМОГА ДВІ

4
Переклад @slacker google не допомагає ... Що означає strdup/ strdupaозначає польською?
haneefmubarak

14
@haneefmubarak тут
anatolyg

Ось різниця між strdup та strcpy stackoverflow.com/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відразу. Налаштування 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
}

8
Варто зауважити, що, як випливає реалізація зразка Pax, strdup (NULL) не визначений, і не те, що можна очікувати, щоб вести себе будь-яким передбачуваним способом.
розмотуємо

2
Крім того, я думаю, що malloc () встановив би помилку, тому вам не доведеться встановлювати це самостійно. Я думаю.
Кріс Лутц

5
@Alcot, strdupпризначений для тих ситуацій, коли ви хочете купувати пам'ять для копіювання рядка. Інакше вам доведеться це зробити самостійно. Якщо у вас вже є достатньо великий буфер (malloc'ed або іншим способом), так, використовуйте 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()оскільки \0char не потрібно шукати знову (це вже було з strlen()).


Дякую. В особистому виконанні я роблю це ще «гірше». return memcpy(malloc(len), s, len);тому що я віддаю перевагу краху при розподілі, а не NULLвідмові розподілу.
Патрік Шлютер

3
@tristopia перенаправлення NULLне повинно бути збоєм ; це не визначено. Якщо ви хочете бути впевнені, що вона виходить з ладу, напишіть повідомлення, emallocяке викликає abortпомилку.
Дейв

Я знаю це, але моя реалізація гарантовано працює лише на Solaris або Linux (за самою природою програми).
Патрік Шлютер

@tristopia: Добре мати звичку робити речі найкращим чином. Звикайте користуватися, emallocнавіть якщо це не потрібно для Solaris або Linux, щоб ви в майбутньому використовували його, коли ви пишете код на інших платформах.
ArtOfWarfare

51

Немає сенсу повторювати інші відповіді, але зауважте, що strdup()можна робити все, що завгодно, з точки зору С, оскільки це не є частиною жодного стандарту С. Однак це визначено POSIX.1-2001.


4
Є чи strdup()портативний комп'ютер? Ні, недоступний у не-POSIX середовищі (тривіально реалізований у будь-якому випадку). Але сказати, що функція POSIX може робити все, що завгодно, досить педантично. POSIX - це ще один стандарт, який настільки ж хороший, як і C, і тим більше популярний.
ПП

2
@BlueMoon Я думаю, що справа в тому, що реалізація C, яка не претендує на відповідність POSIX, все ще може надавати strdupфункцію як розширення. У такій реалізації немає гарантії, що вона strdupповодиться так само, як функція POSIX. Я не знаю жодної такої реалізації, але законна нешкідлива реалізація може передбачити char *strdup(char *)історичні причини та відхилити спроби передачі в const char *.

Яка різниця між стандартом C і POSIX? Під стандартом C ти маєш на увазі, що його немає у стандартних бібліотеках С?
Корай Тугай

@KorayTugay Вони різні стандарти. Краще ставитися до них як до споріднених, якщо ви не знаєте, що стандарт для певної функції C відповідає стандарту POSIX і що ваш компілятор / бібліотека відповідає стандарту для цієї функції.
Матвій

17

Від strdup man :

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


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
}

Отже, те, що він робить, це дати нам ще одну рядок, ідентичний рядку, заданому його аргументом, не вимагаючи від нас виділення пам'яті. Але нам все-таки потрібно звільнити її згодом.


3

Це робить дублікат копії переданої рядка, запустивши malloc та strcpy переданої рядка. Буфер malloc'ed повертається абоненту, отже, необхідність запускати вільне значення, що повертається.


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

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


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 () є скороченням для дублікату рядка, вона приймає в параметрі як константну строку або літеральний рядок і виділяє достатньо місця для рядка і записує відповідні символи у виділений простір і, нарешті, повертає адресу виділеного простір для звичайної виклику.


1
Аргумент strdupне повинен бути константним рядком, він повинен бути рядком C, тобто нульовим завершеним масивом char.
chqrlie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.