Tại sao bỏ giá trị trả lại của free thành void?


82

Tôi đang đọc một cuốn sách ( Lập trình với Chủ đề POSIX của Butenhof, 1997) sử dụng C, và tôi đã xem qua dòng sau:

(void)free(data);

Ở đây, datachỉ là một con trỏ tới một cấu trúc được phân bổ,

data = malloc(sizeof(my_struct_t));

Tại sao kết quả của freeviệc được chọn void?

Theo hiểu biết của tôi về C, điều này dường như không có ý nghĩa vì hai lý do:

  • Hàm miễn phí đã trả về void
  • Mã này không sử dụng giá trị trả về (thậm chí nó không được gán cho một biến)

Cuốn sách được viết vào năm 1997. Đây có phải là một thứ di sản?

Tác giả đề cập rằng các ví dụ đã được chạy trên Digital Unix 4.0d, nhưng tôi vẫn không thể tưởng tượng được lý do để đưa ra kết quả của hàm nếu bạn sẽ không sử dụng kết quả đó.


Một số giải thích có thể có thể được tìm thấy ở đây: stackoverflow.com/questions/689677/ cấp
Timbo

3
Vì tò mò, ngày xuất bản cuốn sách C của bạn là ngày nào? (Đó là cuốn sách nào?) Nếu trước đó vào khoảng năm 1995, có thể có một số biện minh cho nó - trình biên dịch C tiêu chuẩn không phổ biến trước đó. Nếu nó được xuất bản sau đó và vẫn chứa các diễn viên (và không có lời giải thích tại sao), hãy lo lắng về những thói quen xấu khác mà nó đang dạy cho bạn. Nhận một cuốn sách gần đây hơn!
Jonathan Leffler


3
@JonathanLeffler như đã đề cập trong bài viết gốc của tôi, cuốn sách được xuất bản năm 1997 và đang sử dụng UNIX 4.0d. Cuốn sách là "Lập trình với chủ đề POSIX" của David R. Butenhof. Cho đến nay nó đã được cung cấp rất nhiều thông tin và được viết bởi một trong những người đóng góp ban đầu cho tiêu chuẩn chủ đề POSIX.
Adam Johnston

6
Tôi đã sử dụng bản sao của tôi trong tuần trước - vâng, nó vẫn hữu ích. Nó được viết trên đỉnh của 'tiêu chuẩn phổ biến C' (tôi đã nói 'khoảng năm 1995'). 'UNIX 4.0d' nghe giống như UNIX kỹ thuật số - đó là nơi Butenhof hoạt động và lời nói đầu có đề cập đến nó. Hãy coi các diễn viên free()là một điều kỳ lạ trong cuốn sách mà bạn không cần phải mô phỏng. Nó đã từng có liên quan một thời gian dài trước đây, nhưng nó không còn phù hợp nữa.
Jonathan Leffler

Câu trả lời:


100

Nếu chúng ta đang nói về freechức năng tiêu chuẩn thì nguyên mẫu của nó là

void free(void *ptr);

Do đó dàn diễn viên hoàn toàn vô dụng.
Bây giờ một số suy đoán.

Tác giả có thể đã quên bao gồm stdlib.htiêu đề khai báo nguyên mẫu này, vì vậy trình biên dịch giả định kiểu trả về của nó là int. Bây giờ trong quá trình phân tích tĩnh mã này, trình biên dịch đã cảnh báo về giá trị trả về không được sử dụng của cái mà nó cho là không có voidchức năng. Một cảnh báo như vậy thường được giữ im lặng bằng cách thêm các diễn viên void.


50
Nhưng lưu ý rằng nếu các diễn viên được giới thiệu vì lý do suy đoán, thì việc sử dụng nó để bịt miệng cảnh báo là không chính xác . Trong trường hợp đó, trình biên dịch sẽ gán một loại khác freevới thực tế, với kết quả là cuộc gọi có hành vi không xác định (giả sử ngữ nghĩa C90, trong đó việc gọi một hàm không được khai báo vốn không thể hiện UB trong mọi trường hợp). Trong thực tế, điều hợp lý là điều đó sẽ dẫn đến sự sai trái của một số hệ thống. Giải pháp đúng là cung cấp một khai báo đúng cho hàm.
John Bollinger

11
Đáng chú ý, các ví dụ trong "Lập trình với Chủ đề POSIX" liên tục không bao gồm các tiêu đề tiêu chuẩn có liên quan. Có lẽ đây là một thực tế tồi của tác giả, họ có thể đã sử dụng một thiết lập trình biên dịch không chuẩn bao gồm tất cả các lib chuẩn theo mặc định.
Lundin

74

Nó sẽ là một điều di sản!

Trước khi có một tiêu chuẩn C, free()chức năng sẽ là (hoàn toàn) loại int- bởi vì vẫn chưa có loại đáng tin cậy voidđể trả về. Không có giá trị trả lại.

Khi mã lần đầu tiên được sửa đổi để hoạt động với trình biên dịch C tiêu chuẩn, có lẽ nó không bao gồm <stdlib.h>(vì nó không tồn tại trước tiêu chuẩn). Mã cũ sẽ viết extern char *malloc();(có thể không có extern) cho các hàm phân bổ (tương tự cho calloc()realloc()), và không cần phải khai báo free(). Và mã sau đó sẽ chuyển giá trị trả về đúng loại - bởi vì điều đó là cần thiết trên ít nhất một số hệ thống (bao gồm cả hệ thống tôi đã học C trên).

Một thời gian sau, các (void)diễn viên đã được thêm vào để nói với trình biên dịch (hoặc, nhiều khả năng hơn lint) rằng "giá trị trả về từ free()bị bỏ qua có chủ ý" để tránh khiếu nại. Nhưng nó sẽ tốt hơn để thêm <stdlib.h>và để khai báo của nó extern void free(void *vp);nói linthoặc trình biên dịch rằng không có giá trị để bỏ qua.

JFTR: Trở lại giữa thập niên 80, ICL Perq ban đầu nằm trên kiến ​​trúc hướng từ và char *địa chỉ cho một vị trí bộ nhớ là một số rất khác so với 'con trỏ bất cứ thứ gì' đến cùng một vị trí. Nó là rất quan trọng để tuyên bố char *malloc()bằng cách nào đó; điều quan trọng là truyền kết quả từ nó sang bất kỳ loại con trỏ nào khác. Các diễn viên thực sự đã thay đổi số lượng được sử dụng bởi CPU. (Cũng có nhiều niềm vui khi bộ nhớ chính trên các hệ thống của chúng tôi được nâng cấp từ 1 MiB lên 2 MiB - vì hạt nhân sử dụng khoảng 3/4 MiB, điều đó có nghĩa là các chương trình người dùng có thể sử dụng 1 1/4 MiB trước khi phân trang, v.v.)


9
Tôi vừa mở một bản sao của K & R, phiên bản 1, trong đó có triển khai free()trên p. 177 mà ngầm trả lại int.
ex nihilo

9
Tất nhiên - voidđã được thêm vào một số hệ thống (Unix System III, có thể) trước khi tiêu chuẩn được phát hành, nhưng đó không phải là một phần của C khi K & R 1st Edn được viết (1978). Hàm không trả về giá trị được khai báo mà không có kiểu trả về (có nghĩa là nó trả về int) và miễn là bạn không sử dụng giá trị không được trả về, không có vấn đề gì. Tiêu chuẩn C90 phải coi loại mã đó là hợp lệ - nó sẽ thất bại hoàn toàn vì một tiêu chuẩn không có. Nhưng C99 đã loại bỏ các intquy tắc 'ẩn ' và 'khai báo hàm ẩn'. Không phải tất cả các mã trên thế giới đã bắt kịp.
Jonathan Leffler

5
Op tuyên bố cuốn sách được viết vào năm 1997. Những gì bạn đang nói ở đây là "K & R C" tiêu chuẩn rất sớm và dường như không ai có thể viết một cuốn sách về điều đó cả. Cuốn sách duy nhất tồn tại theo hiểu biết của tôi thực sự là ấn bản đầu tiên của K & R.
Lundin

Đó là một thực tế thường xuyên (nếu quixotic) không bao gồm các tiêu đề nếu bạn nghĩ rằng bạn có thể thoát khỏi khai báo ngầm, bởi vì mọi người nghĩ rằng nó sẽ giảm thời gian xây dựng.
Spencer

Có ai sử dụng một (void)diễn viên cho printf()??
Luis Colorado

11

Diễn viên này là không cần thiết. Có lẽ nó đã không xảy ra vào thời điểm đó vì C đã được chuẩn hóa ở dạng C89.

Nếu nó đã được, nó sẽ là do tuyên bố ngầm . Điều này thường có nghĩa là người viết mã quên #include <stdlib.h>và máy phân tích tĩnh đang được sử dụng. Đây không phải là cách giải quyết tốt nhất và một ý tưởng tốt hơn sẽ chỉ là #include <stdlib.h>thay vào đó. Đây là một số từ ngữ từ C89 về tuyên bố ngầm:

Nếu biểu thức đứng trước danh sách đối số được ngoặc trong lệnh gọi hàm chỉ bao gồm một mã định danh và nếu không có khai báo nào cho mã định danh này, thì mã định danh được khai báo chính xác như thể, trong khối trong cùng có chứa lệnh gọi hàm, khai báo

extern int identifier();

đã xuất hiện.

Nhưng điều đó thật kỳ quặc bởi vì họ không truyền kết quả của mallocmột trong hai mallocfreetrong cùng một tệp tiêu đề.

Cũng có thể đây chỉ là một sai lầm hoặc một cách nào đó để nói với người đọc rằng freekhông trả lại kết quả.


5
Chỉ vì một ngôn ngữ được chuẩn hóa không có nghĩa là mọi người cập nhật ngay lập tức chuỗi công cụ và mã của họ để tuân thủ ngôn ngữ đó. Thật hợp lý khi "K & R" C kiểu cũ đã bị kẹt khoảng 8 năm nữa. Tuy nhiên, tôi đồng ý rằng thật kỳ lạ khi một công cụ phân tích tĩnh sẽ yêu cầu phân vai freenhưng không phải malloc.
dan04

4
@ dan04 Bạn thường sử dụng kết quả của malloc;) Tôi không phải là người thích viết những thứ như (void) printf (...) để ngăn chặn trình biên dịch nhổ cảnh báo nhưng "phải biên dịch mà không có bất kỳ cảnh báo nào, ngay cả những kẻ ngu ngốc" là điều mà xảy ra trong rất nhiều dự án.
richardb
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.