strdup () - มันทำอะไรใน C


302

จุดประสงค์ของstrdup()ฟังก์ชั่นใน C คืออะไร?


44
นอกจากนี้ยังมี strdupa () (ในไลบรารี GNU C), ฟังก์ชั่นที่ดีที่คล้ายกับ strdup () แต่จัดสรรหน่วยความจำบนสแต็ก โปรแกรมของคุณไม่จำเป็นต้องเพิ่มหน่วยความจำอย่างชัดเจนเหมือนกับกรณีที่มี strdup () มันจะถูกปลดปล่อยโดยอัตโนมัติเมื่อคุณออกจากฟังก์ชั่นที่เรียกว่า strdupa ()
dmityugov

11
strdupaเป็นอันตรายและไม่ควรใช้เว้นแต่คุณจะเห็นว่าstrlenมีขนาดเล็กมาก แต่คุณสามารถใช้อาร์เรย์ขนาดคงที่บนสแต็กได้
. GitHub หยุดช่วยน้ำแข็ง

4
@slacker google translate ไม่เป็นประโยชน์ ... strdup/ strdupaหมายความว่าอะไรในโปแลนด์?
haneefmubarak

14
@haneefmubarak ที่นี่
anatolyg

นี่คือความแตกต่างระหว่าง strdup และ strcpy stackoverflow.com/questions/14020380/strcpy-vs-strdup
Siva Prakash

คำตอบ:


372

ดูเหมือนว่าหากคุณคุ้นเคยกับวิธีการย่อที่ 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ผมจึงไม่ได้รวมที่นี่(ข)

  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 () จะตั้งค่า errno ดังนั้นคุณไม่ควรตั้งค่าด้วยตนเอง ฉันคิด.
Chris Lutz

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()เป็น\0ถ่านไม่จำเป็นที่จะค้นหาอีกครั้ง (แล้วมันก็มีstrlen())


ขอบคุณ ในการใช้งานส่วนบุคคลของฉันฉันทำให้มัน "แย่ลง" return memcpy(malloc(len), s, len);ตามที่ฉันต้องการความล้มเหลวในการจัดสรรมากกว่า NULLความล้มเหลวในการจัดสรร
Patrick Schlüter

3
@tristopia dereferencing NULLไม่ต้องชนกัน มันไม่ได้กำหนด หากคุณต้องการแน่ใจว่ามันขัดข้องให้เขียนการemallocเรียกที่abortล้มเหลว
เดฟ

ฉันรู้ว่า แต่การใช้งานของฉันรับประกันว่าจะทำงานบน Solaris หรือ Linux เท่านั้น (โดยธรรมชาติของแอป)
Patrick Schlüter

@Tristopia: เป็นเรื่องที่ดีที่จะต้องทำสิ่งที่ดีที่สุด ใช้นิสัยเป็นประจำemallocแม้ว่าจะไม่จำเป็นบน Solaris หรือ Linux ดังนั้นคุณจะใช้มันในอนาคตเมื่อคุณเขียนโค้ดบนแพลตฟอร์มอื่น ๆ
ArtOfWarfare

51

ไม่มีจุดที่จะตอบคำตอบอื่น ๆ อีกต่อไป แต่โปรดทราบว่าstrdup()สามารถทำอะไรก็ได้ที่ต้องการจากมุมมอง C เนื่องจากไม่ใช่ส่วนหนึ่งของมาตรฐาน C ใด ๆ อย่างไรก็ตามมันถูกกำหนดโดย POSIX.1-2001


4
คือstrdup()แบบพกพา? ไม่ไม่สามารถใช้ได้ในสภาพแวดล้อมที่ไม่ใช่ POSIX (สามารถนำไปใช้งานได้เล็กน้อย) แต่ถ้าจะบอกว่าฟังก์ชั่น POSIX นั้นสามารถทำได้ทุกอย่างเลยทีเดียว POSIX เป็นอีกมาตรฐานที่ดีเท่า C และเป็นที่นิยมมากขึ้น
PP

2
@ BlueMoon ฉันคิดว่าประเด็นคือการใช้งาน C ที่อ้างว่าไม่สอดคล้องกับ POSIX อาจยังคงให้strdupฟังก์ชั่นเป็นส่วนขยาย ในการใช้งานดังกล่าวไม่มีการรับประกันว่าstrdupจะทำงานในลักษณะเดียวกันกับฟังก์ชั่น POSIX ผมไม่ทราบว่าการใช้งานดังกล่าวใด ๆ แต่การดำเนินการที่ไม่ถูกต้องตามกฎหมายที่เป็นอันตรายอาจให้ด้วยเหตุผลทางประวัติศาสตร์และปฏิเสธความพยายามในการที่จะผ่านในchar *strdup(char *) const char *

ความแตกต่างระหว่าง C มาตรฐานและ POSIX คืออะไร? ตามมาตรฐาน C คุณหมายถึงไม่มีในไลบรารีมาตรฐาน C หรือไม่
Koray Tugay

@KorayTugay พวกเขามีมาตรฐานที่แตกต่างกัน ดีกว่าที่จะถือว่าพวกเขาไม่เกี่ยวข้องเว้นแต่คุณจะรู้ว่ามาตรฐานสำหรับฟังก์ชั่น C เฉพาะสอดคล้องกับมาตรฐาน POSIX และคอมไพเลอร์ / ห้องสมุดของคุณเป็นไปตามมาตรฐานสำหรับฟังก์ชั่นนั้น
Matthew อ่าน

17

จากผู้ชาย strdup :

ฟังก์ชั่นจะกลับมาเป็นตัวชี้ไปยังสายใหม่ซึ่งเป็นซ้ำของสตริงที่ชี้ไปตามstrdup() ชี้ส่งกลับสามารถส่งผ่านไปs1 free()ตัวชี้ null ถูกส่งคืนหากไม่สามารถสร้างสตริงใหม่


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เสมอ null ยุติสตริงคัดลอก


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 char
chqrlie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.