Làm thế nào nó có thể được? Không phải bộ nhớ của một biến cục bộ không thể truy cập bên ngoài chức năng của nó?
Bạn thuê một phòng khách sạn. Bạn đặt một cuốn sách vào ngăn kéo trên cùng của bàn cạnh giường ngủ và đi ngủ. Bạn kiểm tra vào sáng hôm sau, nhưng "quên" để trả lại chìa khóa của bạn. Bạn ăn cắp chìa khóa!
Một tuần sau, bạn trở về khách sạn, không nhận phòng, lẻn vào phòng cũ bằng chìa khóa bị đánh cắp và tìm trong ngăn kéo. Cuốn sách của bạn vẫn còn đó. Kinh ngạc!
Làm thế nào mà có thể được? Không phải nội dung của ngăn kéo phòng khách sạn không thể truy cập nếu bạn chưa thuê phòng?
Chà, rõ ràng kịch bản đó có thể xảy ra trong thế giới thực không có vấn đề gì. Không có thế lực bí ẩn nào khiến cuốn sách của bạn biến mất khi bạn không còn được phép ở trong phòng. Cũng không có một thế lực bí ẩn nào ngăn bạn vào một căn phòng có chìa khóa bị đánh cắp.
Quản lý khách sạn không bắt buộc phải xóa sách của bạn. Bạn đã không ký hợp đồng với họ nói rằng nếu bạn để lại đồ đạc, họ sẽ hủy nó cho bạn. Nếu bạn vào lại phòng của bạn một cách bất hợp pháp với chìa khóa bị đánh cắp để lấy lại, nhân viên an ninh khách sạn không bắt buộc bạn phải lẻn vào. Bạn đã không ký hợp đồng với họ rằng "nếu tôi cố gắng lẻn vào phòng sau, bạn được yêu cầu ngăn chặn tôi. " Thay vào đó, bạn đã ký hợp đồng với họ với nội dung "Tôi hứa sẽ không lẻn vào phòng tôi sau", một hợp đồng mà bạn đã phá vỡ .
Trong tình huống này bất cứ điều gì cũng có thể xảy ra . Cuốn sách có thể ở đó - bạn đã gặp may mắn. Cuốn sách của người khác có thể ở đó và cuốn sách của bạn có thể ở trong lò của khách sạn. Ai đó có thể ở đó ngay khi bạn bước vào, xé sách của bạn thành từng mảnh. Khách sạn có thể đã loại bỏ bàn và đặt hoàn toàn và thay thế nó bằng một tủ quần áo. Toàn bộ khách sạn có thể sắp bị phá hủy và thay thế bằng một sân bóng đá, và bạn sẽ chết trong một vụ nổ trong khi bạn đang lén lút xung quanh.
Bạn không biết điều gì sẽ xảy ra; Khi bạn rời khỏi khách sạn và lấy trộm chìa khóa để sử dụng bất hợp pháp sau đó, bạn đã từ bỏ quyền sống trong một thế giới an toàn, có thể dự đoán được vì bạn đã chọn phá vỡ các quy tắc của hệ thống.
C ++ không phải là một ngôn ngữ an toàn . Nó sẽ vui vẻ cho phép bạn phá vỡ các quy tắc của hệ thống. Nếu bạn cố làm điều gì đó bất hợp pháp và dại dột như quay trở lại phòng mà bạn không được phép ở trong và lục lọi bàn làm việc thậm chí không còn ở đó nữa, C ++ sẽ không ngăn bạn. Các ngôn ngữ an toàn hơn C ++ giải quyết vấn đề này bằng cách hạn chế sức mạnh của bạn - bằng cách kiểm soát các phím chặt chẽ hơn nhiều, chẳng hạn.
CẬP NHẬT
Chúa ơi, câu trả lời này đang được chú ý rất nhiều. (Tôi không chắc tại sao - Tôi coi đó chỉ là một sự tương tự nhỏ "vui vẻ", nhưng bất cứ điều gì.)
Tôi nghĩ rằng nó có thể là nguyên bản để cập nhật điều này một chút với một vài suy nghĩ kỹ thuật.
Trình biên dịch đang kinh doanh trong việc tạo mã quản lý việc lưu trữ dữ liệu được thao tác bởi chương trình đó. Có rất nhiều cách khác nhau để tạo mã để quản lý bộ nhớ, nhưng theo thời gian, hai kỹ thuật cơ bản đã trở nên cố thủ.
Đầu tiên là có một vùng lưu trữ "tồn tại lâu" trong đó "tuổi thọ" của mỗi byte trong bộ lưu trữ - nghĩa là khoảng thời gian khi nó được liên kết hợp lệ với một số biến chương trình - không thể dự đoán dễ dàng trước của thời gian Trình biên dịch tạo các cuộc gọi vào một "trình quản lý heap" biết cách phân bổ động lưu trữ khi cần thiết và lấy lại khi không cần thiết nữa.
Phương pháp thứ hai là có một vùng lưu trữ thời gian ngắn có thời gian ngắn, trong đó tuổi thọ của mỗi byte được biết đến. Ở đây, các kiếp sống theo mô hình lồng nhau của hoàng tử. Thời gian tồn tại lâu nhất của các biến có thời gian tồn tại ngắn này sẽ được phân bổ trước bất kỳ biến có thời gian tồn tại ngắn nào khác và sẽ được giải phóng sau cùng. Các biến có thời gian tồn tại ngắn hơn sẽ được phân bổ sau các biến có thời gian tồn tại lâu nhất và sẽ được giải phóng trước chúng. Thời gian tồn tại của các biến có thời gian tồn tại ngắn hơn này là lồng nhau lồng nhau trong vòng đời của các biến có thời gian tồn tại lâu hơn.
Các biến cục bộ theo mô hình sau; khi một phương thức được nhập vào, các biến cục bộ của nó trở nên sống động. Khi phương thức đó gọi một phương thức khác, các biến cục bộ của phương thức mới trở nên sống động. Họ sẽ chết trước khi các biến cục bộ của phương thức đầu tiên bị chết. Thứ tự tương đối của sự khởi đầu và kết thúc của thời gian sống của kho lưu trữ liên quan đến các biến cục bộ có thể được thực hiện trước thời hạn.
Vì lý do này, các biến cục bộ thường được tạo dưới dạng lưu trữ trên cấu trúc dữ liệu "ngăn xếp", bởi vì một ngăn xếp có thuộc tính mà điều đầu tiên được đẩy vào nó sẽ là điều cuối cùng xuất hiện.
Giống như khách sạn quyết định chỉ thuê phòng theo tuần tự và bạn không thể trả phòng cho đến khi mọi người có số phòng cao hơn bạn đã trả phòng.
Vì vậy, hãy nghĩ về ngăn xếp. Trong nhiều hệ điều hành, bạn nhận được một ngăn xếp trên mỗi luồng và ngăn xếp được phân bổ theo một kích thước cố định nhất định. Khi bạn gọi một phương thức, công cụ sẽ được đẩy lên ngăn xếp. Nếu sau đó bạn chuyển một con trỏ tới ngăn xếp ra khỏi phương thức của bạn, như áp phích ban đầu thực hiện ở đây, thì đó chỉ là một con trỏ ở giữa một khối bộ nhớ triệu byte hoàn toàn hợp lệ. Tương tự như vậy, bạn trả phòng khách sạn; Khi bạn làm, bạn chỉ cần kiểm tra ra khỏi phòng chiếm số lượng cao nhất. Nếu không có ai khác đăng ký sau bạn và bạn quay trở lại phòng của bạn một cách bất hợp pháp, tất cả đồ đạc của bạn được đảm bảo vẫn còn ở đó trong khách sạn đặc biệt này .
Chúng tôi sử dụng ngăn xếp cho các cửa hàng tạm thời bởi vì chúng thực sự rẻ và dễ dàng. Không cần phải thực hiện C ++ để sử dụng ngăn xếp để lưu trữ cục bộ; nó có thể sử dụng đống. Nó không, bởi vì điều đó sẽ làm cho chương trình chậm hơn.
Việc triển khai C ++ là không bắt buộc để lại rác mà bạn để lại trên ngăn xếp không bị ảnh hưởng để bạn có thể quay lại để lấy nó bất hợp pháp sau này; việc trình biên dịch tạo mã trở về 0 mọi thứ trong "phòng" mà bạn vừa bỏ trống là hoàn toàn hợp pháp. Nó không phải bởi vì một lần nữa, đó sẽ là đắt tiền.
Việc triển khai C ++ là không bắt buộc để đảm bảo rằng khi ngăn xếp co lại một cách hợp lý, các địa chỉ được sử dụng là hợp lệ vẫn được ánh xạ vào bộ nhớ. Việc triển khai được phép nói với hệ điều hành "chúng ta đã hoàn thành việc sử dụng trang stack này ngay bây giờ. Cho đến khi tôi nói khác, hãy đưa ra một ngoại lệ phá hủy quy trình nếu có ai chạm vào trang stack hợp lệ trước đó". Một lần nữa, việc triển khai không thực sự làm điều đó bởi vì nó chậm và không cần thiết.
Thay vào đó, việc triển khai cho phép bạn phạm sai lầm và thoát khỏi nó. Hầu hết thời gian. Cho đến một ngày một cái gì đó thực sự khủng khiếp đi sai và quá trình bùng nổ.
Đây là vấn đề. Có rất nhiều quy tắc và rất dễ dàng để phá vỡ chúng một cách vô tình. Tôi chắc chắn có nhiều lần. Và tệ hơn, vấn đề thường chỉ xuất hiện khi bộ nhớ được phát hiện là hỏng hàng tỷ nano giây sau khi tham nhũng xảy ra, khi rất khó để tìm ra ai đã làm nó rối tung lên.
Nhiều ngôn ngữ an toàn bộ nhớ giải quyết vấn đề này bằng cách hạn chế sức mạnh của bạn. Trong C # "bình thường" đơn giản là không có cách nào để lấy địa chỉ của một địa phương và trả lại hoặc lưu trữ nó sau này. Bạn có thể lấy địa chỉ của một địa phương, nhưng ngôn ngữ được thiết kế khéo léo để không thể sử dụng nó sau khi vòng đời của địa phương kết thúc. Để lấy địa chỉ của một địa phương và chuyển lại, bạn phải đặt trình biên dịch ở chế độ "không an toàn" đặc biệt và đặt từ "không an toàn" trong chương trình của bạn, để chú ý đến thực tế rằng bạn có thể đang làm một cái gì đó nguy hiểm có thể phá vỡ các quy tắc.
Để đọc thêm:
address of local variable ‘a’ returned
; chương trình valgrindInvalid write of size 4 [...] Address 0xbefd7114 is just below the stack ptr