strdup () - nó làm gì trong C?


302

Mục đích của strdup()hàm trong C là gì?


44
cũng có strdupa () (trong thư viện GNU C), một hàm đẹp tương tự strdup (), nhưng phân bổ bộ nhớ trên ngăn xếp. Chương trình của bạn không cần giải phóng bộ nhớ một cách rõ ràng như trong trường hợp với strdup (), nó sẽ được giải phóng tự động khi bạn thoát khỏi chức năng nơi strdupa () được gọi
dmityugov

11
strdupalà nguy hiểm và không nên được sử dụng trừ khi bạn đã xác định strlenlà rất nhỏ. Nhưng sau đó bạn chỉ có thể sử dụng một mảng có kích thước cố định trên ngăn xếp.
R .. GitHub DỪNG GIÚP ICE

4
@slacker Google Translate không bị hữu ích ... gì strdup/ strdupabình ở Ba Lan?
haneefmubarak 7/03/2015

14
@haneefmubarak tại đây
anatolyg

Dưới đây là sự khác biệt giữa strdup và strcpy stackoverflow.com/questions/14020380/strcpy-vs-strdup
Siva Prakash

Câu trả lời:


372

Chính xác thì nó nghe như thế nào, giả sử bạn đã quen với cách viết tắt trong đó C và UNIX gán các từ, nó nhân đôi chuỗi :-)

Hãy nhớ rằng nó thực sự không phải là một phần của tiêu chuẩn ISO C (a) (đó là điều POSIX), nó thực sự hoạt động giống như mã sau:

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
}

Nói cách khác:

  1. Nó cố gắng phân bổ đủ bộ nhớ để giữ chuỗi cũ (cộng với ký tự '\ 0' để đánh dấu kết thúc chuỗi).

  2. Nếu phân bổ thất bại, nó đặt errnothành ENOMEMvà trả về NULLngay lập tức. Thiết của errnođể ENOMEMlà một cái gì đó malloclàm trong POSIX vì vậy chúng tôi không cần phải làm một cách rõ ràng trong của chúng tôi strdup. Nếu bạn không tuân thủ POSIX, ISO C thực sự không bắt buộc sự tồn tại của ENOMEMvì vậy tôi đã không đưa nó vào đây (b) .

  3. Mặt khác, phân bổ hoạt động để chúng tôi sao chép chuỗi cũ sang chuỗi mới (c) và trả lại địa chỉ mới (mà người gọi có trách nhiệm giải phóng tại một số điểm).

Hãy nhớ rằng đó là định nghĩa khái niệm. Bất kỳ nhà văn thư viện nào xứng đáng với mức lương của họ có thể đã cung cấp mã được tối ưu hóa mạnh mẽ nhắm mục tiêu vào bộ xử lý cụ thể đang được sử dụng.


(a) Tuy nhiên, các chức năng bắt đầu bằng strvà một chữ cái viết thường được bảo lưu theo tiêu chuẩn cho các hướng trong tương lai. Từ C11 7.1.3 Reserved identifiers:

Mỗi tiêu đề khai báo hoặc định nghĩa tất cả các định danh được liệt kê trong mệnh đề phụ được liên kết của nó và * tùy ý khai báo hoặc định nghĩa các định danh được liệt kê trong mệnh đề phụ thư viện hướng tương lai của thư viện. **

Các hướng tương lai cho string.hcó thể được tìm thấy trong C11 7.31.13 String handling <string.h>:

Tên hàm bắt đầu bằng str, memhoặc wcsvà một chữ cái viết thường có thể được thêm vào các khai báo trong <string.h>tiêu đề.

Vì vậy, bạn có thể nên gọi nó là một cái gì đó khác nếu bạn muốn được an toàn.


(b) Thay đổi về cơ bản sẽ được thay thế if (d == NULL) return NULL;bằng:

if (d == NULL) {
    errno = ENOMEM;
    return NULL;
}

(c) Lưu ý rằng tôi sử dụng strcpycho điều đó vì điều đó cho thấy rõ ý định. Trong một số triển khai, có thể nhanh hơn (vì bạn đã biết độ dài) để sử dụng memcpy, vì chúng có thể cho phép chuyển dữ liệu theo khối lớn hơn hoặc song song. Hoặc có thể không :-) Câu thần chú tối ưu hóa số 1: "đo lường, đừng đoán".

Trong mọi trường hợp, nếu bạn quyết định đi theo con đường đó, bạn sẽ làm một cái gì đó như:

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
Điều đáng chú ý là, như cách triển khai mẫu của Pax ngụ ý, strdup (NULL) không được xác định và không phải là thứ bạn có thể mong đợi để hành xử theo bất kỳ cách dự đoán nào.
thư giãn

2
Ngoài ra, tôi nghĩ malloc () sẽ đặt errno, vì vậy bạn không nên tự đặt nó. Tôi nghĩ.
Chris Lutz

5
@Alcot, strdupdành cho những tình huống mà bạn muốn bộ nhớ heap được phân bổ cho bản sao chuỗi. Nếu không bạn phải tự làm điều đó. Nếu bạn đã một bộ đệm đủ lớn (malloc'ed hoặc cách khác), có, sử dụng strcpy.
paxdiablo

2
@acgtyrant: nếu, theo tiêu chuẩn, bạn có nghĩa là tiêu chuẩn ISO (tiêu chuẩn C thực sự), không, nó không phải là một phần của nó. Nó một phần của tiêu chuẩn POSIX. Tuy nhiên, có rất nhiều triển khai C cung cấp nó, mặc dù không phải là một phần chính thức của ISO C. Tuy nhiên, ngay cả khi họ không làm như vậy, năm lớp trong câu trả lời này là quá đủ.
paxdiablo

2
Điểm hay, @chux, ISO chỉ { EDOM, EILSEQ, ERANGE }bắt buộc theo mã lỗi yêu cầu. Đã cập nhật câu trả lời cho tài khoản này.
paxdiablo

86
char * strdup(const char * s)
{
  size_t len = 1+strlen(s);
  char *p = malloc(len);

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

Có thể mã nhanh hơn một chút so với strcpy()\0char không cần tìm kiếm lại (Nó đã có sẵn strlen()).


Cảm ơn. Trong thực hiện cá nhân của tôi, tôi làm cho nó thậm chí còn "tệ hơn". return memcpy(malloc(len), s, len);vì tôi thích sự cố về phân bổ hơn là NULLthất bại trong phân bổ.
Patrick Schlüter

3
@tristopia dereferences NULLkhông phải sụp đổ; nó không được xác định. Nếu bạn muốn chắc chắn nó gặp sự cố, hãy viết một emalloclệnh gọi abortkhi thất bại.
Dave

Tôi biết điều đó, nhưng việc triển khai của tôi được đảm bảo chỉ chạy trên Solaris hoặc Linux (theo bản chất của ứng dụng).
Patrick Schlüter

@tristopia: Thật tốt khi có thói quen làm mọi thứ một cách tốt nhất. Tập thói quen sử dụng emallocngay cả khi không cần thiết trên Solaris hoặc Linux để bạn sẽ sử dụng nó trong tương lai khi bạn viết mã trên các nền tảng khác.
ArtOfWarfare

51

Không có điểm nào lặp lại các câu trả lời khác, nhưng xin lưu ý rằng strdup()có thể làm bất cứ điều gì nó muốn từ góc độ C, vì nó không phải là một phần của bất kỳ tiêu chuẩn C nào. Tuy nhiên, nó được định nghĩa bởi POSIX.1-2001.


4
Là hàng strdup()xách tay? Không, không khả dụng trong môi trường không phải POSIX (dù sao cũng có thể thực hiện được). Nhưng để nói rằng một chức năng POSIX có thể làm bất cứ điều gì khá là mô phạm. POSIX là một tiêu chuẩn khác tốt như C và thậm chí còn phổ biến hơn.
PP

2
@BlueMoon Tôi nghĩ rằng vấn đề là việc triển khai C tuyên bố không tuân thủ POSIX vẫn có thể cung cấp một strdupchức năng như một phần mở rộng. Khi triển khai như vậy, không có gì đảm bảo rằng nó strduphoạt động giống như chức năng POSIX. Tôi không biết về bất kỳ triển khai nào như vậy, nhưng việc triển khai không độc hại hợp pháp có thể cung cấp char *strdup(char *)các lý do lịch sử và từ chối các nỗ lực để vượt qua const char *.

Sự khác biệt giữa tiêu chuẩn C và POSIX là gì? Theo tiêu chuẩn C, ý bạn là nó không tồn tại trong các thư viện chuẩn C?
Koray Tugay

@KorayTugay Chúng là những tiêu chuẩn khác nhau. Tốt hơn là coi chúng là không liên quan trừ khi bạn biết rằng tiêu chuẩn cho một chức năng C cụ thể phù hợp với tiêu chuẩn POSIX và trình biên dịch / thư viện của bạn tuân thủ tiêu chuẩn cho chức năng đó.
Matthew Đọc

17

Từ người đàn ông strdup :

Các strdup()chức năng có trách nhiệm trả lại một con trỏ đến một chuỗi mới, đó là một bản sao của chuỗi được trỏ đến bởi s1. Con trỏ trả về có thể được truyền vào free(). Một con trỏ null được trả về nếu chuỗi mới không thể được tạo.


4

strdup () thực hiện cấp phát bộ nhớ động cho mảng ký tự bao gồm ký tự kết thúc '\ 0' và trả về địa chỉ của bộ nhớ heap:

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
}

Vì vậy, những gì nó làm là cung cấp cho chúng ta một chuỗi khác giống hệt với chuỗi được đưa ra bởi đối số của nó, mà không yêu cầu chúng ta phân bổ bộ nhớ. Nhưng chúng ta vẫn cần giải phóng nó, sau này.


3

Nó tạo một bản sao của chuỗi được truyền vào bằng cách chạy mallocstrcpy của chuỗi được truyền vào. Bộ đệm malloc'ed được trả về cho người gọi, do đó cần phải chạy miễn phí trên giá trị trả về.


3

strdupstrndupđược định nghĩa trong các hệ thống tuân thủ POSIX như:

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

Hàm strdup () phân bổ đủ bộ nhớ cho một bản sao của chuỗi str, thực hiện sao chép và trả về một con trỏ tới nó.

Con trỏ sau đó có thể được sử dụng làm đối số cho hàm free.

Nếu không đủ bộ nhớ, NULLđược trả về và errnođược đặt thành ENOMEM.

Hàm strndup () sao chép hầu hết các lenký tự từ chuỗi strluôn null kết thúc chuỗi đã sao chép.


1

Điều có giá trị nhất mà nó làm là cung cấp cho bạn một chuỗi khác giống hệt với chuỗi đầu tiên, mà không yêu cầu bạn phải tự phân bổ bộ nhớ (vị trí và kích thước). Nhưng, như đã lưu ý, bạn vẫn cần giải phóng nó (nhưng cũng không yêu cầu tính toán số lượng.)


1

Tuyên bố:

strcpy(ptr2, ptr1);

tương đương với (khác với thực tế điều này thay đổi con trỏ):

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

Trong khi:

ptr2 = strdup(ptr1);

tương đương với:

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

Vì vậy, nếu bạn muốn chuỗi mà bạn đã sao chép được sử dụng trong một hàm khác (vì nó được tạo trong phần heap), bạn có thể sử dụng strdup, khác strcpylà đủ,


0

Hàm strdup () là một tốc ký cho chuỗi trùng lặp, nó lấy tham số là một hằng chuỗi hoặc một chuỗi ký tự và phân bổ vừa đủ không gian cho chuỗi và ghi các ký tự tương ứng trong không gian được phân bổ và cuối cùng trả về địa chỉ của phân bổ không gian để các thói quen gọi.


1
Đối số strdupkhông cần phải là hằng chuỗi, nó phải là chuỗi C, tức là mảng kết thúc null char.
chqrlie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.