2009-01-27 73 views
15

Gần đây tôi đã nhận thức được rằng chức năng strdup() Tôi rất thích sử dụng rất nhiều trên OS X không phải là một phần của ANSI C, nhưng là một phần của POSIX. Tôi không muốn viết lại tất cả các mã của mình, vì vậy tôi nghĩ tôi sẽ viết hàm strdup() của riêng mình. Nó không phải là khó, thực sự, nó chỉ là một malloc() và một strcpy(). Dù sao, tôi có chức năng, nhưng tôi đang làm gì nếu tôi viết chức năng này và liên kết nó với mã của tôi, và nó đã tồn tại trong libc? Trình liên kết hoặc trình biên dịch của tôi có cho phép tôi về cơ bản xác định phiên bản chức năng của riêng tôi không, hoặc tôi có phải đặt tên khác cho nó không? Sẽ rất thuận tiện nếu có một cách để sử dụng lại cùng tên, để nếu có strcpy() tồn tại trong libc của người dùng, họ có thể sử dụng nó, nhưng nếu nó không tồn tại trong libc của họ, họ có thể sử dụng phiên bản của tôi. thay đổi mã càng tốt.strdup() chức năng

Phiên bản ngắn:

a) gì xảy ra khi tôi viết chức năng riêng của tôi với tên giống như một hàm built-in?

b) Tôi có thể làm gì để tránh những điều xấu xảy ra với tôi trên nền tảng không có strdup() mà không cần viết lại tất cả mã của mình để không sử dụng strdup(), điều này hơi tẻ nhạt?

Trả lời

20

Thông thường, bạn chỉ cần sử dụng #if để xác định hàm bạn muốn khi theo một trình biên dịch nhất định. Nếu thư viện được xây dựng trong không định nghĩa strdup, không có vấn đề trong việc xác định điều đó cho mình (trừ khi họ làm định nghĩa nó trong tương lai, bạn sẽ phải lấy nó ra.)

// Only define strdup for platforms that are missing it.. 
#if COMPILER_XYZ || COMPILER_ABC 
char *strdup(const char *) 
{ 
    // .... 
} 
#endif 
+8

Nếu bạn cảm thấy tham vọng, bạn thậm chí có thể thiết lập automake và autoconf để cấu hình macro cần thiết để kiểm tra xem bạn có cần tự xác định strdup hay không, thay vì liệt kê tất cả các trình biên dịch và môi trường bạn muốn hỗ trợ. –

4

a) Điều gì sẽ xảy ra khi tôi viết hàm của riêng mình có cùng tên với hàm được cài sẵn?

Bạn không thể xác định lại hàm đã tồn tại trong tệp tiêu đề bạn đang đưa vào. Điều này sẽ dẫn đến lỗi biên dịch.

b) Những gì tôi có thể làm gì để tránh những điều xấu xảy ra với tôi trên nền tảng mà không có strdup() mà không cần viết lại tất cả các mã của tôi không sử dụng strdup(), mà chỉ là một chút tẻ nhạt?

Tôi khuyên bạn nên tạo chức năng wrapper riêng của mình để strdup và thay thế tất cả các cuộc gọi của bạn để sử dụng chức năng trình bao bọc mới. Ví dụ:

char *StringDuplicate(const char *s1) 
{ 
#ifdef POSIX 
    return strdup(s1); 
#else 
    /* Insert your own code here */ 
#endif 
} 

Thay đổi tất cả các cuộc gọi từ strdup sang StringDuplicate() phải là thao tác tìm và thay thế đơn giản, làm cho nó trở thành phương pháp khả thi. Logic nền tảng cụ thể sau đó sẽ được giữ ở một vị trí duy nhất thay vì nằm rải rác trong toàn bộ hệ thống mã của bạn.

2

FYI: Tôi chưa từng thấy một môi trường nào không xác định strdup().

+1

tôi từng thích strdup - cho đến khi tôi nói với các bạn trong ## c tại irc.freenode.org. họ không thích nó, và lập luận rằng nếu có một hai dòng đơn giản, đảm bảo cách làm việc - tại sao một người nên phụ thuộc vào chức năng không di động? (strdup là không được chấp nhận trong các cửa sổ, và giành chiến thắng ce dường như không có nó) –

+0

msdn nói rằng một trong những nên sử dụng _strdup thay thế. nhưng tôi nghĩ rằng tôi đọc những cảnh báo không dùng nữa là vô nghĩa anyway :) nhưng hôm nay nó là một bất ngờ với tôi để xem strdup thực sự là như vậy được sử dụng rộng rãi. –

6

bạn chỉ có thể sử dụng macro như thế này, theo cách này bạn có thể sử dụng tên cũ, nhưng liên kết sẽ thấy tên khác;

char *my_strdup(const char *s) { 
    char *p = malloc(strlen(s) + 1); 
    if(p) { strcpy(p, s); } 
    return p; 
} 

/* this goes in whatever header defines my_strdup */ 
char *my_strdup(const char *s); 
#define strdup(x) my_strdup(x) 
+0

giải pháp tốt, nhưng bạn cần phải bọC#define strdup (x) bên trong một số cơ chế kiểm tra khác để xác định xem nó có cần được thay thế hay không. Nếu không bạn có thể muốn sử dụng libc strdup – Matt

+4

Không có nhiều tác hại trong * không * kiểm tra cho điều đó, ngoại trừ tối ưu có thể. BTW, ở trên có thể nhanh hơn với bộ nhớ đệm strlen và sau đó sử dụng memcpy, bởi vì nó là một nội tại trong nhiều trình biên dịch, mà kết thúc như một lệnh CPU đơn. –

3

Bạn cũng nên xem xét tránh tạo bất kỳ số nhận dạng nào (bao gồm hàm) bắt đầu bằng str [a-z]. Trong khi điều này không được bảo lưu, phần tiêu chuẩn C (ISO/IEC 9899: 1999) 7.26.11 (các hướng dẫn thư viện trong tương lai) nêu rõ "Các tên hàm bắt đầu bằng str, mem, hoặc wcs và một chữ thường có thể được thêm vào các khai báo trong tiêu đề."

6

Như Rob Kennedy lưu ý cách tốt nhất là kiểm tra bên trong các kịch bản xây dựng của bạn nếu chức năng này tồn tại hay không. Tôi biết rằng nó là khá dễ dàng với autoconfig, nhưng có lẽ với các công cụ xây dựng kịch bản nền tảng khác, quá.

Sau đó, bạn chỉ cần đặt trong tập tin tiêu đề của bạn:


#ifndef HAVE_STRDUP 
# ifdef HAVE__STRDUP 
# define strdup _strdup 
# else 
# define strdup my_strdup 
# endif 
#endif 

Nếu strdup đã tồn tại trên nền tảng mục tiêu phiên bản libc được sử dụng, nếu không hoạt động my_strdup tùy chỉnh của bạn sẽ được sử dụng.

CHỈNH SỬA: Tôi phải thêm giải thích tại sao nó tốt hơn.

Đầu tiên trình biên dịch không liên quan đến sự tồn tại của một hàm trong libc. Ví dụ lấy hàm strlcpy. Nó có mặt trên FreeBSD nhưng không có trên Linux (glibc), mặc dù cả hai đều sử dụng gcc theo mặc định. Hoặc điều gì xảy ra nếu ai đó sẽ biên dịch mã của bạn bằng tiếng kêu?

Thứ hai kiểm tra nền tảng (Tôi không biết nếu có một cách tiêu chuẩn) sẽ chỉ hoạt động nếu bạn thêm rõ ràng cho mỗi plattform bạn muốn hỗ trợ tiền xử lý chính xác có điều kiện. Vì vậy, giả sử bạn đã làm chủ để biên dịch ứng dụng của bạn trên OSX và Win32 và bạn muốn biên dịch nó ngay bây giờ trên Linux, bạn sẽ phải trải qua tất cả các điều kiện tiền xử lý để xem chúng có hoạt động với Linux hay không. Có lẽ bạn cũng muốn hỗ trợ FreeBSD, OpenBSD, v.v.? Cùng một công việc một lần nữa. Với một thử nghiệm trong các kịch bản xây dựng của bạn, nó có thể biên dịch mà không cần bất kỳ công việc bổ sung nào.

-2

Nếu có ai khác đọc: Không sử dụng strdup của nền tảng() ngay cả khi có sẵn, và không lãng phí thời gian/công sức với autoconf/automake chỉ để sử dụng nó. Nghiêm túc, khó khăn như thế nào:

char* mystrdup(const char* str) 
{ 
return strcpy(malloc(strlen(str) + 1),str); 
} 

Điều này thực sự đảm bảo #ifdefs? Trình kiểm tra biên dịch? HÔN.

+16

Điều gì xảy ra nếu malloc thất bại? –

Các vấn đề liên quan