Xóa con trỏ trong C ++


92

Bối cảnh: Tôi đang cố gắng quấn lấy các con trỏ, chúng tôi vừa nhìn thấy chúng vài tuần trước ở trường và trong khi luyện tập hôm nay tôi đã gặp phải một điều ngớ ngẩn? vấn đề, nó có thể rất đơn giản với bạn nhưng tôi có rất ít hoặc không có kinh nghiệm lập trình.

Tôi đã thấy khá nhiều câu hỏi trong SO về việc xóa con trỏ nhưng chúng dường như đều liên quan đến việc xóa một lớp chứ không phải con trỏ 'đơn giản' (hoặc bất kỳ thuật ngữ thích hợp nào có thể là), đây là đoạn mã tôi đang cố gắng chạy:

#include <iostream>;

using namespace std;

int main() {
  int myVar,
      *myPointer;

  myVar = 8;
  myPointer = &myVar;

  cout << "delete-ing pointers " << endl;
  cout << "Memory address: " << myPointer << endl;

  // Seems I can't *just* delete it, as it triggers an error 
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // Error: a.out(14399) malloc: *** error for object 0x7fff61e537f4:
  // pointer being freed was not allocated
  // *** set a breakpoint in malloc_error_break to debug
  // Abort trap: 6

  // Using the new keyword befor deleting it works, but
  // does it really frees up the space? 
  myPointer = new int;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer continues to store a memory address.

  // Using NULL before deleting it, seems to work. 
  myPointer = NULL;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer returns 0.

}

Vì vậy, câu hỏi của tôi là:

  1. Tại sao trường hợp đầu tiên không hoạt động? Có vẻ như cách sử dụng đơn giản nhất để sử dụng và xóa một con trỏ? Lỗi cho biết bộ nhớ không được cấp phát nhưng 'cout' đã trả về một địa chỉ.
  2. Trong ví dụ thứ hai, lỗi không được kích hoạt nhưng thực hiện một cout giá trị của myPointer vẫn trả về địa chỉ bộ nhớ?
  3. # 3 có thực sự hoạt động không? Có vẻ như có tác dụng với tôi, con trỏ không còn lưu trữ địa chỉ, đây có phải là cách thích hợp để xóa một con trỏ không?

Xin lỗi vì câu hỏi dài, tôi muốn làm cho điều này rõ ràng nhất có thể, cũng cần nhắc lại, tôi có ít kinh nghiệm lập trình, vì vậy nếu ai đó có thể trả lời câu hỏi này bằng cách sử dụng thuật ngữ của giáo dân, nó sẽ được đánh giá rất cao!


16
Lý do bạn không nhìn thấy ví dụ đầu tiên là vì nó sai. Chỉ deletenhững gì bạn new. Nó cũng không bắt buộc con trỏ phải tự đặt thành NULL sau khi bạn xóa nó. Nếu bạn muốn an toàn ở đó, hãy sử dụng con trỏ thông minh, giúp giải phóng bộ nhớ cho bạn và đưa ra lỗi khi bạn cố truy cập khi chúng không giữ thứ gì đó.
chris

Được rồi, tôi không chắc con trỏ thông minh là gì, nhưng tôi sẽ xem xét nó, cảm ơn!
leopic

1
Tóm lại, họ làm những gì tôi đã mô tả. Để giữ một cái gì đó mới, bạn gọi resetvà nó giải phóng cái cũ. Để giải phóng nó mà không cần thay thế, bạn gọi release. Khi nó vượt ra khỏi phạm vi, nó sẽ bị phá hủy và có thể giải phóng bộ nhớ dựa trên loại nó là gì. std::unique_ptrchỉ dành cho một chủ sở hữu. std::shared_ptrgiải phóng nó khi chủ sở hữu cuối cùng ngừng sở hữu tài nguyên. Họ cũng ngoại lệ an toàn. Nếu bạn phân bổ tài nguyên với một tài nguyên và sau đó gặp phải ngoại lệ, tài nguyên sẽ được giải phóng đúng cách.
chris

Câu trả lời:


168

1 & 2

myVar = 8; //not dynamically allocated. Can't call delete on it.
myPointer = new int; //dynamically allocated, can call delete on it.

Biến đầu tiên được cấp phát trên ngăn xếp. Bạn chỉ có thể gọi xóa trên bộ nhớ mà bạn đã cấp phát động (trên heap) bằng cách sử dụng newtoán tử.

3.

  myPointer = NULL;
  delete myPointer;

Ở trên không làm gì cả . Bạn đã không giải phóng bất cứ thứ gì, vì con trỏ đã chỉ vào NULL.


Không nên làm những điều sau:

myPointer = new int;
myPointer = NULL; //leaked memory, no pointer to above int
delete myPointer; //no point at all

Bạn đã chỉ nó vào NULL, để lại bộ nhớ bị rò rỉ (int mới mà bạn đã cấp phát). Bạn nên giải phóng bộ nhớ mà bạn đã trỏ vào. Không có cách nào để truy cập nó được cấp phát new intnữa, do đó bộ nhớ bị rò rỉ.


Cách chính xác:

myPointer = new int;
delete myPointer; //freed memory
myPointer = NULL; //pointed dangling ptr to NULL

Cách tốt hơn:

Nếu bạn đang sử dụng C ++, không sử dụng con trỏ thô. Thay vào đó, hãy sử dụng các con trỏ thông minh có thể giải quyết những việc này cho bạn với chi phí thấp. C ++ 11 đi kèm với một số .


13
<pedantry> "Trên ngăn xếp" là một chi tiết triển khai - một chi tiết mà C ++ rõ ràng tránh đề cập. Thuật ngữ đúng hơn là "với thời lượng lưu trữ tự động". (C ++ 11, 3.7.3) </pedantry>
cHao

4
Cảm ơn bạn, tôi đã chọn câu trả lời của bạn để a) giải thích điều gì sai và b) đưa ra phương pháp hay nhất, cảm ơn nhiều!
leopic

6
@Tqn Điều đó không đúng. delete myPointergiao dịch *myPointer. Đúng rồi. Nhưng myPointertiếp tục chỉ vào vị trí bộ nhớ đã được giải phóng và không nên sử dụng vì nó là UB. Nó sẽ không thể truy cập được sau khi kết thúc phạm vi chỉ NẾU nó là một biến cục bộ ngay từ đầu.
Anirudh Ramanathan

2
@DarkCthulhu Cảm ơn! (Tôi theo nghĩa đen) học một cái gì đó newmỗi ngày. (Tôi sến
sẩm

1
@AmelSalibasic Bộ nhớ được liên kết với biến trên ngăn xếp sẽ chỉ được giải phóng khi nó vượt ra khỏi phạm vi. Việc gán nó để NULLtránh việc chúng ta sử dụng sai sau này.
Anirudh Ramanathan

23

Tôi tin rằng bạn không hoàn toàn hiểu cách hoạt động của con trỏ.
Khi bạn có một con trỏ trỏ đến một bộ nhớ nào đó, bạn phải hiểu ba điều khác nhau:
- có "cái được trỏ" bởi con trỏ (bộ nhớ)
- địa chỉ bộ nhớ này
- không phải tất cả các con trỏ đều cần xóa bộ nhớ của chúng: bạn chỉ cần xóa bộ nhớ đã được cấp phát động ( newtoán tử đã sử dụng ).

Hãy tưởng tượng:

int *ptr = new int; 
// ptr has the address of the memory.
// at this point, the actual memory doesn't have anything.
*ptr = 8;
// you're assigning the integer 8 into that memory.
delete ptr;
// you are only deleting the memory.
// at this point the pointer still has the same memory address (as you could
//   notice from your 2nd test) but what inside that memory is gone!

Khi bạn đã làm

ptr = NULL;
// you didn't delete the memory
// you're only saying that this pointer is now pointing to "nowhere".
// the memory that was pointed by this pointer is now lost.

C ++ cho phép bạn cố gắng tìm deletemột con trỏ trỏ đến nullnhưng nó không thực sự làm được gì, chỉ không đưa ra bất kỳ lỗi nào.


2
Cảm ơn, ĐIỀU NÀY cực kỳ hữu ích, tôi nghĩ tôi ĐÃ xóa tất cả các con trỏ, không biết rằng điều đó chỉ dành cho những con trỏ mới, cảm ơn.
leopic

13

Con trỏ tương tự như các biến bình thường ở chỗ bạn không cần phải xóa chúng. Chúng bị xóa khỏi bộ nhớ khi kết thúc quá trình thực thi chức năng và / hoặc khi kết thúc chương trình.

Tuy nhiên, bạn có thể sử dụng con trỏ để cấp phát 'khối' bộ nhớ, ví dụ như sau:

int *some_integers = new int[20000]

Điều này sẽ phân bổ không gian bộ nhớ cho 20000 số nguyên. Hữu ích, bởi vì ngăn xếp có kích thước hạn chế và bạn có thể muốn gây rối với một lượng lớn 'int' mà không có lỗi tràn ngăn xếp.

Bất cứ khi nào bạn gọi mới, sau đó bạn nên 'xóa' ở cuối chương trình của mình, vì nếu không, bạn sẽ bị rò rỉ bộ nhớ và một số không gian bộ nhớ được cấp sẽ không bao giờ được trả lại cho các chương trình khác sử dụng. Để làm điều này:

delete [] some_integers;

Hy vọng rằng sẽ giúp.


1
Tôi chỉ muốn nói thêm rằng bộ nhớ được cấp phát SẼ được trả lại cho các chương trình khác sử dụng, nhưng chỉ SAU KHI chương trình của bạn thực thi xong.
sk4l

7

Có một quy tắc trong C ++, cứ mỗi cái mới sẽ có một lần xóa .

  1. Tại sao trường hợp đầu tiên không hoạt động? Có vẻ như cách sử dụng đơn giản nhất để sử dụng và xóa một con trỏ? Lỗi cho biết bộ nhớ không được cấp phát nhưng 'cout' đã trả về một địa chỉ.

mới không bao giờ được gọi. Vì vậy, địa chỉ mà cout in ra là địa chỉ của vị trí bộ nhớ của myVar hoặc giá trị được gán cho myPointer trong trường hợp này. Bằng cách viết:

myPointer = &myVar;

bạn nói:

myPointer = Địa chỉ nơi dữ liệu trong myVar được lưu trữ

  1. Trong ví dụ thứ hai, lỗi không được kích hoạt nhưng thực hiện một cout giá trị của myPointer vẫn trả về địa chỉ bộ nhớ?

Nó trả về một địa chỉ trỏ đến vị trí bộ nhớ đã bị xóa. Bởi vì đầu tiên bạn tạo con trỏ và gán giá trị của nó cho myPointer, thứ hai bạn xóa nó, thứ ba bạn in nó. Vì vậy, trừ khi bạn gán một giá trị khác cho myPointer, địa chỉ đã xóa sẽ vẫn còn.

  1. # 3 có thực sự hoạt động không? Có vẻ như có tác dụng với tôi, con trỏ không còn lưu trữ địa chỉ, đây có phải là cách thích hợp để xóa một con trỏ không?

NULL bằng 0, bạn xóa 0, vì vậy bạn không xóa gì. Và logic là nó in 0 vì bạn đã làm:

myPointer = NULL;

bằng:

myPointer = 0;

4
  1. Bạn đang cố gắng xóa một biến được phân bổ trên ngăn xếp. Bạn không thể làm điều này
  2. Việc xóa một con trỏ không thực sự hủy một con trỏ, chỉ là bộ nhớ bị chiếm dụng được trả lại cho HĐH. Bạn có thể truy cập nó cho đến khi bộ nhớ được sử dụng cho một biến khác hoặc bị thao túng. Vì vậy, bạn nên đặt một con trỏ thành NULL (0) sau khi xóa.
  3. Xóa con trỏ NULL không xóa bất kỳ thứ gì.

2
int value, *ptr;

value = 8;
ptr = &value;
// ptr points to value, which lives on a stack frame.
// you are not responsible for managing its lifetime.

ptr = new int;
delete ptr;
// yes this is the normal way to manage the lifetime of
// dynamically allocated memory, you new'ed it, you delete it.

ptr = nullptr;
delete ptr;
// this is illogical, essentially you are saying delete nothing.

1
Ngoài ra, hãy xem bài giảng này về khung ngăn xếp youtube.com/watch?v=bjObm0hxIYYyoutube.com/watch?v=Rxvv9krECNw về con trỏ.
Casper Beyer
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.