Chức năng trả dây, phong cách tốt?


11

Trong các chương trình C của tôi, tôi thường cần một cách để thể hiện chuỗi ADT của mình. Ngay cả khi tôi không cần phải in chuỗi lên màn hình theo bất kỳ cách nào, thật gọn gàng khi có phương pháp như vậy để gỡ lỗi. Vì vậy, loại chức năng này thường xuất hiện.

char * mytype_to_string( const mytype_t *t );

Tôi thực sự nhận ra rằng tôi có (ít nhất) ba tùy chọn ở đây để xử lý bộ nhớ cho chuỗi trả về.

Thay thế 1: Lưu trữ chuỗi trả về trong một mảng char tĩnh trong hàm. Tôi không cần suy nghĩ nhiều, ngoại trừ chuỗi được ghi đè trong mỗi cuộc gọi. Mà có thể là một vấn đề trong một số trường hợp.

Phương án 2: Phân bổ chuỗi trên heap với malloc bên trong hàm. Thực sự gọn gàng vì sau đó tôi sẽ không cần phải nghĩ đến kích thước của bộ đệm hoặc ghi đè. Tuy nhiên, tôi phải nhớ để giải phóng () chuỗi khi hoàn thành, và sau đó tôi cũng cần gán cho một biến tạm thời để tôi có thể giải phóng. và sau đó phân bổ heap thực sự chậm hơn nhiều so với phân bổ stack, do đó, sẽ là một nút cổ chai nếu điều này được lặp lại trong một vòng lặp.

Phương án 3: Truyền con trỏ vào bộ đệm và để người gọi phân bổ bộ đệm đó. Giống:

char * mytype_to_string( const mytype_t *mt, char *buf, size_t buflen ); 

Điều này mang lại nhiều nỗ lực hơn cho người gọi. Tôi cũng nhận thấy rằng sự thay thế này mang lại cho tôi một lựa chọn khác theo thứ tự của các đối số. Tôi nên có đối số nào trước và cuối? (thực tế là sáu khả năng)

Vì vậy, tôi nên chọn cái nào? Bất kỳ tại sao? Có một số loại tiêu chuẩn bất thành văn trong số các nhà phát triển C?


3
Chỉ cần một ghi chú quan sát, hầu hết các hệ điều hành đều sử dụng tùy chọn 3 - người gọi phân bổ bộ đệm bằng mọi cách; cho biết con trỏ đệm và dung lượng; callee điền vào bộ đệm và cũng trả về độ dài thực của chuỗi nếu bộ đệm không đủ. Ví dụ: sysctlbynametrong OS X và iOS
rwong 8/2/2015

Câu trả lời:


11

Các phương pháp tôi đã thấy nhiều nhất là 2 và 3.

Bộ đệm do người dùng cung cấp thực sự khá đơn giản để sử dụng:

char[128] buffer;
mytype_to_string(mt, buffer, 128);

Mặc dù hầu hết các triển khai sẽ trả về số lượng bộ đệm được sử dụng.

Tùy chọn 2 sẽ chậm hơn và nguy hiểm khi sử dụng các thư viện được liên kết động nơi chúng có thể sử dụng các thời gian chạy khác nhau (và các đống khác nhau). Vì vậy, bạn không thể giải phóng những gì đã được đặt trong thư viện khác. Điều này sau đó yêu cầu một free_string(char*)chức năng để đối phó với nó.


Cảm ơn! Tôi nghĩ tôi cũng thích Giải pháp thay thế 3. Tuy nhiên tôi muốn có thể làm những việc như: printf("MyType: %s\n", mytype_to_string( mt, buf, sizeof(buf));và do đó tôi sẽ không muốn trả lại độ dài đã sử dụng mà là con trỏ cho chuỗi. Các bình luận thư viện năng động là thực sự quan trọng.
Øystein Schønning-Johansen 8/2/2015

Điều này không nên sizeof(buffer) - 1để thỏa mãn kẻ \0hủy diệt?
Michael-O

1
@ Michael-O không có thuật ngữ null được bao gồm trong kích thước bộ đệm có nghĩa là chuỗi tối đa có thể được đặt vào nhỏ hơn 1 so với kích thước được truyền. Đây là mẫu mà các chuỗi an toàn hoạt động trong thư viện chuẩn như snprintfsử dụng.
ratchet freak

@ratchetfreak Cảm ơn đã làm rõ. Sẽ tốt đẹp để mở rộng câu trả lời với sự khôn ngoan đó.
Michael-O

0

Ý tưởng thiết kế bổ sung cho # 3

Khi có thể, cũng cung cấp kích thước tối đa cần thiết cho mytypecùng một tệp .h như mytype_to_string().

#define MYTYPE_TO_STRING_SIZE 256

Bây giờ người dùng có thể mã cho phù hợp.

char buf[MYTYPE_TO_STRING_SIZE];
puts(mytype_to_string(mt, buf, sizeof buf));

Đặt hàng

Kích thước của mảng, khi đầu tiên, cho phép các loại VLA.

char * mytype_to_string( const mytype_t *mt, size_t bufsize, char *buf[bufsize]); 

Không quá quan trọng với kích thước đơn, nhưng hữu ích với 2 hoặc nhiều hơn.

void matrix(size_t row, size_t col, double matrix[row][col]);

Tôi nhớ rằng việc đọc có kích thước đầu tiên là một thành ngữ ưa thích trong phần tiếp theo C. Cần tìm tài liệu tham khảo đó ....


0

Là một bổ sung cho câu trả lời xuất sắc của @ ratchetfreak, tôi sẽ chỉ ra rằng phương án số 3 thay thế theo mô hình / mô hình tương tự như các hàm thư viện C tiêu chuẩn.

Ví dụ , strncpy.

char * strncpy ( char * destination, const char * source, size_t num );

Theo mô hình tương tự sẽ giúp giảm tải nhận thức cho các nhà phát triển mới (hoặc thậm chí là bản thân tương lai của bạn) khi họ cần sử dụng chức năng của bạn.

Sự khác biệt duy nhất với những gì bạn có trong bài viết của mình là destinationđối số trong các thư viện C có xu hướng được liệt kê đầu tiên trong danh sách đối số. Vì thế:

char * mytype_to_string( char *buf, const mytype_t *mt, size_t buflen ); 

-2

Bên cạnh thực tế là những gì bạn đề xuất làm là mùi mã xấu, thay thế 3 âm thanh tốt nhất với tôi. Tôi cũng nghĩ như @ gnasher729 rằng bạn đang sử dụng ngôn ngữ sai.


Chính xác những gì bạn xem xét một mùi mã? Xin hãy giải thích.
Hulk

-3

Thành thật mà nói, bạn có thể muốn chuyển sang một ngôn ngữ khác trong đó việc trả về một chuỗi không phải là một hoạt động phức tạp, tốn nhiều công sức và dễ bị lỗi.

Bạn có thể xem xét C ++ hoặc Objective-C nơi bạn có thể giữ nguyên 99% mã của mình.

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.