Trong C, tại sao một số người bỏ con trỏ trước khi giải phóng nó?


167

Tôi đang làm việc trên một cơ sở mã cũ và hầu như mọi cách gọi free () đều sử dụng một tham số trên đối số của nó. Ví dụ,

free((float *)velocity);
free((float *)acceleration);
free((char *)label);

trong đó mỗi con trỏ thuộc loại tương ứng (và khớp). Tôi thấy không có điểm nào trong việc này cả. Đó là mã rất cũ, vì vậy tôi sẽ tự hỏi liệu đó có phải là một thứ K & R không. Nếu vậy, tôi thực sự muốn hỗ trợ các trình biên dịch cũ có thể đã yêu cầu điều này, vì vậy tôi không muốn xóa chúng.

Có một lý do kỹ thuật để sử dụng các phôi? Tôi thậm chí không thấy nhiều lý do thực tế để sử dụng chúng. Điểm nhắc nhở bản thân về kiểu dữ liệu ngay trước khi giải phóng nó là gì?

EDIT: Câu hỏi này không phải là một bản sao của câu hỏi khác. Câu hỏi khác là một trường hợp đặc biệt của câu hỏi này, mà tôi nghĩ là hiển nhiên nếu những cử tri thân thiết sẽ đọc tất cả các câu trả lời.

Colophon: Tôi đang đưa ra dấu "const answer" vì đây là lý do thực sự của bonafide tại sao điều này có thể cần phải được thực hiện; tuy nhiên, câu trả lời về việc nó là một tùy chỉnh trước ANSI C (ít nhất là trong số một số lập trình viên) dường như là lý do nó được sử dụng trong trường hợp của tôi. Rất nhiều điểm tốt của nhiều người ở đây. Cảm ơn vì sự đóng góp của bạn.


13
"Điểm nhắc nhở bản thân về kiểu dữ liệu ngay trước khi giải phóng nó là gì?" Có lẽ để biết bao nhiêu bộ nhớ sẽ được giải phóng?
m0skit0

12
@Codor Trình biên dịch không thực hiện thỏa thuận, hệ điều hành nào.
m0skit0

20
@ m0skit0 "Có lẽ để biết bao nhiêu bộ nhớ sẽ được giải phóng?" Loại không cần thiết để biết bao nhiêu miễn phí. Diễn viên chỉ vì lý do đó là mã hóa xấu.
dùng694733

9
@ m0skit0 Đúc vì khả năng đọc vì mã hóa luôn xấu, bởi vì việc truyền thay đổi cách thức các loại được diễn giải và nó có thể ẩn các lỗi nghiêm trọng. Khi cần đọc, bình luận tốt hơn.
dùng694733

66
Vào thời cổ đại khi khủng long đi trên trái đất và viết sách lập trình, tôi tin rằng không có chữ void*C chuẩn, mà chỉ có char*. Vì vậy, nếu kết quả khảo cổ học của bạn tiết lộ mã đúc tham số thành free (), tôi tin rằng nó phải xuất phát từ khoảng thời gian đó hoặc được viết bởi một sinh vật từ thời điểm đó. Tôi không thể tìm thấy bất kỳ nguồn nào cho việc này, vì vậy tôi sẽ không trả lời.
Lundin

Câu trả lời:


171

Việc truyền có thể được yêu cầu để giải quyết các cảnh báo của trình biên dịch nếu các con trỏ là const. Dưới đây là một ví dụ về mã gây ra cảnh báo mà không đưa ra đối số miễn phí:

const float* velocity = malloc(2*sizeof(float));
free(velocity);

Và trình biên dịch (gcc 4.8.3) nói:

main.c: In function main’:
main.c:9:5: warning: passing argument 1 of free discards const qualifier from pointer target type [enabled by default]
     free(velocity);
     ^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected void *’ but argument is of type const float *’
 extern void free (void *__ptr) __THROW;

Nếu bạn sử dụng free((float*) velocity);trình biên dịch ngừng phàn nàn.


2
@ m0skit0 không giải thích lý do tại sao một người nào đó sẽ sử dụng float*trước khi giải phóng. Tôi đã thử free((void *)velocity);với gcc 4.8.3. Tất nhiên, nó sẽ không hoạt động với một trình biên dịch cổ
Manos Nikolaidis

54
Nhưng tại sao bạn cần phân bổ động bộ nhớ không đổi? Bạn không bao giờ có thể sử dụng nó!
Nils_M

33
@Nils_M là một ví dụ đơn giản để tạo điểm nhấn. Những gì tôi đã làm trong mã thực tế trong một hàm là phân bổ bộ nhớ không const, gán giá trị, truyền cho một con trỏ const và trả về nó. Bây giờ, có một con trỏ để gán bộ nhớ const mà ai đó phải giải phóng.
Manos Nikolaidis

2
Ví dụ : Quảng cáo Các chương trình con này trả về chuỗi trong bộ nhớ vừa được đặt, được trỏ bởi * stringValueP, mà cuối cùng bạn phải giải phóng. Đôi khi, hàm OS mà bạn sử dụng để giải phóng bộ nhớ được khai báo để đưa một con trỏ tới một thứ không phải là hằng số làm đối số của nó, vì vậy * stringValueP là một con trỏ tới const.
Carsten S

3
Sai lầm, nếu một chức năng mất const char *pnhư một cuộc tranh cãi và sau đó giải phóng nó, những điều đúng để làm không phải là để cast pđể char*trước khi gọi miễn phí. Đó là không tuyên bố nó là const char *pở vị trí đầu tiên, vì nó sửa đổi *p và nên được khai báo cho phù hợp. (Và nếu cần một con trỏ const thay vì con trỏ thành const, int *const pbạn không cần phải sử dụng vì nó thực sự hợp pháp và do đó hoạt động tốt mà không cần diễn viên.)
Ray

59

C chuẩn trước không có void*nhưng chỉ có char*, vì vậy bạn phải truyền tất cả các tham số được thông qua. Nếu bạn bắt gặp mã C cổ đại, do đó bạn có thể tìm thấy các phôi như vậy.

Câu hỏi tương tự với tài liệu tham khảo .

Khi tiêu chuẩn C đầu tiên được phát hành, các nguyên mẫu cho malloc và miễn phí thay đổi từ việc char*cho void*rằng họ vẫn có ngày hôm nay.

Và tất nhiên trong tiêu chuẩn C, các phôi như vậy là thừa và chỉ gây hại cho khả năng đọc.


23
Nhưng tại sao bạn lại đưa đối số về freecùng loại mà nó đã có?
jwodder

4
@chux Vấn đề với tiêu chuẩn trước chỉ là: không có nghĩa vụ cho bất cứ điều gì. Mọi người chỉ vào cuốn sách K & R cho canon vì đó là thứ duy nhất họ có. Và như chúng ta có thể thấy từ một số ví dụ trong phiên bản 2 của K & R, bản thân K & R bối rối về cách các tham số freehoạt động trong tiêu chuẩn C (bạn không cần phải truyền). Tôi chưa đọc ấn bản đầu tiên vì vậy tôi không thể biết liệu họ có bị nhầm lẫn trong thời kỳ tiền chuẩn của thập niên 80 không.
Lundin

7
Tiêu chuẩn C không có void*, nhưng nó cũng không có nguyên mẫu hàm, do đó, việc đưa ra đối số freevẫn không cần thiết ngay cả trong K & R (giả sử tất cả các loại con trỏ dữ liệu được sử dụng cùng một biểu diễn).
Ian Abbott

6
Vì nhiều lý do đã nêu trong các bình luận, tôi không nghĩ câu trả lời này có ý nghĩa.
R .. GitHub DỪNG GIÚP ICE

4
Tôi không thấy câu trả lời này thực sự sẽ trả lời như thế nào. Câu hỏi ban đầu liên quan đến phôi từ các loại khác, không chỉ để char *. Nó có ý nghĩa gì trong các trình biên dịch cũ mà không có void? Những diễn viên như vậy sẽ đạt được những gì?
AnT

34

Đây là một ví dụ nơi miễn phí sẽ thất bại nếu không có diễn viên:

volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p);        // fail: warning C4090: 'function' : different 'volatile' qualifiers
free((int*)p);  // success :)
free((void*)p); // success :)

Trong C, bạn có thể nhận được cảnh báo (có một trong VS2012). Trong C ++, bạn sẽ gặp lỗi.

Các trường hợp hiếm hoi sang một bên, việc đúc chỉ làm nở mã ...

Chỉnh sửa: Tôi đúc để void*không int*demo thất bại. Nó sẽ hoạt động tương tự như int*sẽ được chuyển đổi thành void*ngầm. Đã thêm int*mã.


Lưu ý rằng trong mã được đăng trong câu hỏi, các diễn viên không phải void *, nhưng đến float *char *. Những diễn viên đó không chỉ là ngoại lai, họ đã sai.
Andrew Henle

1
Câu hỏi thực sự là về điều ngược lại.
m0skit0

1
Tôi không hiểu câu trả lời; Theo nghĩa nào sẽ free(p)thất bại? Nó sẽ cung cấp cho một lỗi biên dịch?
Codor

1
Đây là những điểm tốt. constRõ ràng là tương tự với con trỏ vòng loại, rõ ràng.
Lundin

2
volatileđã tồn tại từ khi C được chuẩn hóa nếu không còn. Nó không được thêm vào trong C99.
R .. GitHub DỪNG GIÚP ICE

30

Lý do cũ: 1. Bằng cách sử dụng free((sometype*) ptr), mã rõ ràng về loại con trỏ nên được coi là một phần của free()cuộc gọi. Dàn diễn viên rõ ràng rất hữu ích khi free()được thay thế bằng (tự làm) DIY_free().

#define free(ptr) DIY_free(ptr, sizeof (*ptr))

A DIY_free()là (là) một cách, đặc biệt là trong chế độ gỡ lỗi, để thực hiện phân tích thời gian chạy của con trỏ được giải phóng. Điều này thường được kết hợp với một DIY_malloc()để thêm các giá trị, số lượng sử dụng bộ nhớ toàn cầu, v.v ... Nhóm của tôi đã sử dụng kỹ thuật này trong nhiều năm trước khi các công cụ hiện đại hơn xuất hiện. Điều bắt buộc là vật phẩm được miễn phí được phân loại thành loại ban đầu được phân bổ.

  1. Với nhiều giờ dành cho việc theo dõi các vấn đề về bộ nhớ, v.v., các thủ thuật nhỏ như truyền kiểu miễn phí sẽ hỗ trợ tìm kiếm và thu hẹp gỡ lỗi.

Hiện đại: Tránh constvolatilecảnh báo theo địa chỉ của Manos Nikolaidis @@egur . Nghĩ rằng tôi sẽ lưu ý những ảnh hưởng của 3 vòng : const, volatile, và restrict.

[sửa] Đã thêm char * restrict *rp2mỗi @R .. bình luận

void free_test(const char *cp, volatile char *vp, char * restrict rp, 
    char * restrict *rp2) {
  free(cp);  // warning
  free(vp);  // warning
  free(rp);  // OK
  free(rp2);  // warning
}

int main(void) {
  free_test(0,0,0,0);
  return 0;
}

3
restrictchỉ là một vấn đề vì nó được đặt ở đâu - nó ảnh hưởng đến đối tượng rpkhông phải là loại trỏ. Nếu bạn thay vào đó char *restrict *rp, thì nó sẽ thành vấn đề.
R .. GitHub DỪNG GIÚP ICE

16

Đây là một giả thuyết khác.

Chúng tôi đang nói rằng chương trình đã được viết trước C89, có nghĩa là nó không thể được làm việc xung quanh một số loại không phù hợp với nguyên mẫu của free, bởi vì không chỉ không có những điều như constcũng không void *trước khi C89, không có những điều như một nguyên mẫu chức năng trước C89. stdlib.hchính nó là một phát minh của ủy ban. Nếu các tiêu đề hệ thống bận tâm khai báo free, họ sẽ làm như thế này:

extern free();  /* no `void` return type either! */

Bây giờ, điểm mấu chốt ở đây là sự vắng mặt của các nguyên mẫu hàm có nghĩa là trình biên dịch không kiểm tra kiểu đối số . Nó đã áp dụng các chương trình khuyến mãi đối số mặc định (cùng các chương trình khuyến mãi vẫn áp dụng cho các lệnh gọi hàm matrixdic) và đó là nó. Trách nhiệm thực hiện các đối số tại mỗi cuộc gọi phù hợp với kỳ vọng của callee hoàn toàn thuộc về lập trình viên.

Tuy nhiên, điều này vẫn không có nghĩa là cần thiết phải đưa ra đối số freetrên hầu hết các trình biên dịch K & R. Một chức năng như

free_stuff(a, b, c)
    float *a;
    char *b;
    int *c;
{
    free(a);
    free(b);
    free(c);
}

nên đã được biên dịch chính xác. Vì vậy, tôi nghĩ rằng những gì chúng ta có ở đây là một chương trình được viết để đối phó với trình biên dịch lỗi cho một môi trường khác thường: ví dụ, một môi trường trong đó sizeof(float *) > sizeof(int)và trình biên dịch sẽ không sử dụng quy ước gọi thích hợp cho các con trỏ trừ khi bạn đặt chúng tại điểm của cuộc gọi.

Tôi không biết về bất kỳ môi trường nào như vậy, nhưng điều đó không có nghĩa là không có môi trường. Các ứng cử viên có khả năng xuất hiện nhất trong tâm trí là các trình biên dịch "C nhỏ" đã cắt giảm cho micros 8 và 16 bit vào đầu những năm 1980. Tôi cũng sẽ không ngạc nhiên khi biết rằng Crays sớm có vấn đề như thế này.


1
Nửa đầu tôi hoàn toàn đồng ý với. Và nửa thứ hai là một phỏng đoán hấp dẫn và hợp lý.
chux - Tái lập Monica

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.