2010-03-18 53 views
23

Trong C và C++, Giải phóng con trỏ NULL sẽ không làm được gì.Giải phóng bộ nhớ hai lần

Tuy nhiên, tôi thấy mọi người nói rằng tham nhũng bộ nhớ có thể xảy ra nếu bạn "bộ nhớ miễn phí hai lần".

Điều này có đúng không? Điều gì đang xảy ra dưới mui xe khi bạn giải phóng bộ nhớ hai lần?

+3

Không có bộ nhớ nào được giải phóng khi bạn giải phóng con trỏ rỗng. Nếu bạn giải phóng con trỏ * không null * hai lần, bộ nhớ được giải phóng hai lần và đó là một vấn đề. – jalf

Trả lời

22
int *p = malloc(sizeof(int)); 
//value of p is now lets say 0x12345678 

*p = 2; 
free(p); //memory pointer is freed, but still value of p is 0x12345678 
     //now, if you free again, you get a crash or undefined behavior. 

Vì vậy, sau khi free ing lần đầu tiên, bạn nên làm p = NULL, vì vậy nếu (bởi bất kỳ cơ hội), free(p) được gọi là một lần nữa, không có gì sẽ xảy ra.

Đây là lý do tại sao giải phóng bộ nhớ gấp đôi là undefined: Why free crashes when called twice

+6

+1 cho tính chính xác của câu trả lời, ngay cả khi tôi không đồng ý thiết lập con trỏ sang NULL ngay sau đó, có thể ẩn một lỗi đang tồn tại (gọi là 'miễn phí' lần thứ hai) và tôi muốn nó thất bại và nhanh chóng lỗi có thể được xử lý hơn là ẩn. –

+0

@David: Tôi hoàn toàn đồng ý, nếu 'free' được gọi hai lần trên một con trỏ, nó là một (phần nào) thực hiện xấu. –

+1

@David: Đó là một chiến lược tốt miễn là bạn biết cách gỡ lỗi các tình huống 'tự do hai lần'. Nếu bạn không, tôi thay vì vô hiệu hóa con trỏ ngay sau khi giải phóng và kiểm tra nó cho nullness ngay trước mỗi câu lệnh 'free'. –

9

Đây là hành vi không xác định, có thể dẫn đến hỏng heap hoặc hậu quả nghiêm trọng khác.

free() cho con trỏ rỗng chỉ cần kiểm tra giá trị con trỏ bên trong và trả về. Việc kiểm tra đó sẽ không giúp giải phóng một khối hai lần.

Đây là những gì thường xảy ra. Việc triển khai heap lấy địa chỉ và cố gắng "chiếm quyền sở hữu" của khối tại địa chỉ đó bằng cách sửa đổi dữ liệu dịch vụ của riêng nó. Tùy thuộc vào việc thực hiện heap bất cứ điều gì có thể xảy ra. Có lẽ nó hoạt động và không có gì xảy ra, có thể dữ liệu dịch vụ bị hỏng và bạn đã bị hỏng heap.

Vì vậy, đừng làm điều đó. Đó là hành vi không xác định. Bất cứ điều gì xấu có thể xảy ra.

+0

Tôi cũng sẽ nói rõ ràng rằng 'free' ing một con trỏ không (neceaarily) thay đổi giá trị của nó và nói chung nó không null sau khi 'miễn phí' (đó là lý do thứ hai' miễn phí' gây tham nhũng). – Ari

4

Có, "hành vi không xác định" hầu như luôn dẫn đến sự cố. (trong khi "không xác định hành vi" theo định nghĩa có nghĩa là "bất cứ điều gì", các loại lỗi thường hành xử theo những cách khá có thể dự đoán được. Trong trường hợp miễn phí(), hành vi là luôn luôn segfault hoặc tương ứng "bảo vệ bộ nhớ lỗi" đặc trưng cho hệ điều hành.)

Tương tự nếu bạn giải phóng() con trỏ đến bất kỳ thứ gì khác ngoài NULL hoặc thứ bạn đã malloc'd.

char x; char* p=&x; free(p); // lỗi.

+0

+1 Crash 101 ở đó. – kenny

+3

Bạn quá lạc quan về kết quả của hành vi không xác định. –

+0

Tôi có Roger.Trong thực tế, giải phóng một cái gì đó hai lần gần như không bao giờ kết quả trong một vụ tai nạn tại thời điểm miễn phí diễn ra, mà làm cho nó một trong những lỗi khó khăn nhất để theo dõi. –

2

miễn phí() giải phóng không gian bộ nhớ chỉ đến bởi ptr, mà phải có được trả về bởi một cuộc gọi trước để malloc(), calloc() hoặc realloc(). Nếu không, hoặc nếu miễn phí (ptr) đã được được gọi trước, không xác định hành vi xảy ra. Nếu ptr là NULL, không có thao tác nào được thực hiện.

Vì vậy, bạn nhận được hành vi không xác định và mọi thứ có thể xảy ra.

1

1) Xử lý bộ nhớ động không được thực hiện bởi trình biên dịch. Có các thư viện thời gian chạy để giải quyết vấn đề này. Ví dụ: : glibc cung cấp các API như malloc và miễn phí, trong đó thực hiện các cuộc gọi hệ thống (sys_brk) để xử lý vùng heap.

2) Giải phóng bộ nhớ giống nhau hai lần đề cập đến một điều kiện như sau: Giả sử bạn có char * cptr;

Bạn cấp phát bộ nhớ bằng cách sử dụng: cptr = (char *) malloc (SIZE);

Bây giờ, khi bạn không còn cần bộ nhớ này nữa, bạn có thể giải phóng bộ nhớ bằng cách sử dụng: miễn phí (cptr);

Bây giờ ở đây điều gì xảy ra là bộ nhớ được trỏ tới bởi cptr là miễn phí để sử dụng.

Giả sử tại một thời điểm sau đó trong chương trình bạn lại gọi miễn phí (cptr), thì đây không phải là điều kiện hợp lệ. Kịch bản này, nơi bạn đang giải phóng cùng một bộ nhớ hai lần được biết là vấn đề "giải phóng bộ nhớ hai lần ".`

19

Bộ nhớ giải phóng không đặt con trỏ thành rỗng. Con trỏ vẫn trỏ đến bộ nhớ mà nó dùng để sở hữu, nhưng bây giờ đã có quyền sở hữu được chuyển trở lại trình quản lý heap.

Trình quản lý heap có thể có kể từ khi phân bổ lại bộ nhớ mà con trỏ cũ của bạn trỏ đến.

Giải phóng lại lần nữa không giống như nói free(NULL) và sẽ dẫn đến hành vi không xác định.

3

Khi bạn gọi miễn phí trên con trỏ, con trỏ của bạn sẽ không được đặt thành NULL. Không gian trống chỉ được đưa trở lại một hồ bơi để có thể phân bổ lại. Dưới đây là một ví dụ để kiểm tra:

đầu ra
#include <stdio.h> 
#include <stdlib.h> 

int main(){ 
    int* ptr = (int*)malloc(sizeof(int)); 
    printf("Address before free: %p\n", ptr); 
    free(ptr); 
    printf("Address after free: %p\n", ptr); 
    return 0; 
} 

Chương trình này cho tôi:

Address before free: 0x950a008 
Address after free: 0x950a008 

và bạn có thể thấy, rằng tự do đã không làm gì để con trỏ, nhưng chỉ nói với hệ thống bộ nhớ có sẵn cho tái sử dụng.

4

Để tránh miễn phí hai lần tôi nhất luôn sử dụng MACRO cho bộ nhớ miễn phí:

#ifdef FREEIF 
# undef FREEIF 
#endif 
#define FREEIF(_p) \ 
if(_p)    \ 
{      \ 
     free(_p); \ 
     _p = NULL; \ 
} 

bộ macro này p = NULL để tránh đung đưa con trỏ.

+2

Đây là một hack tốt đẹp nhưng tôi muốn cố gắng sửa chữa nguyên nhân thực tế. Nếu free() được gọi hai lần trên cùng một biến, đây rõ ràng là một lỗi. – user206268

0

Giải phóng bộ nhớ nhiều lần có thể gây hậu quả xấu. Bạn có thể chạy đoạn mã này để xem điều gì có thể xảy ra với máy tính của bạn.

#include <stdio.h>  /* printf, scanf, NULL */ 
#include <stdlib.h>  /* malloc, free, rand */ 

int main() 


    { 
    int i,n; 
    char * buffer; 

    printf ("How long do you want the string? "); 
    scanf ("%d", &i); 

    buffer = (char*) malloc (i+1); 
    if (buffer==NULL) exit (1); 

    for (n=0; n<i; n++) 
      buffer[n]=rand()%26+'a'; 
    buffer[i]='\0'; 

    printf ("Random string: %s\n",buffer); 
    free (buffer); 
    free (buffer); 

    return 0; 
} 

Nhiều thư viện chuẩn như CSparse sử dụng hàm bao bọc xử lý sự cố bộ nhớ. Tôi đã sao chép chức năng ở đây:

/* wrapper for free */ 
    void *cs_free (void *p) 
    { 
     if (p) free (p) ;  /* free p if it is not already NULL */ 
     return (NULL) ;   /* return NULL to simplify the use of  

    } 

Chức năng này có thể xử lý sự cố với bộ nhớ. Xin lưu ý rằng bạn phải chăm sóc điều kiện mà malloc trả về NULL trong một số trường hợp

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