avatar of 发明者量化-小小梦 发明者量化-小小梦
tập trung vào tin nhắn riêng tư
4
tập trung vào
1271
Người theo dõi

Điểm kiến ​​thức viết chiến lược C++: Tái chế bộ nhớ C++

Được tạo ra trong: 2017-12-29 11:00:02, cập nhật trên: 2017-12-29 11:25:11
comments   0
hits   3111

Điểm kiến ​​thức viết chiến lược C++: Tái chế bộ nhớ C++

Có một số kiến thức cơ bản cần biết trước khi viết chiến lược C ++, ít nhất là biết các quy tắc này. Dưới đây là bản dịch:

  • #### C++ Memory Object General Strike

  Nếu một người tự cho mình là một lập trình viên giỏi nhưng không biết gì về bộ nhớ, thì tôi có thể nói với bạn rằng anh ta chắc chắn đang khoe khoang. Viết chương trình bằng C hoặc C++ cần chú ý nhiều hơn đến bộ nhớ, không chỉ vì việc phân bổ bộ nhớ một cách hợp lý có ảnh hưởng trực tiếp đến hiệu quả và hiệu suất của chương trình, mà còn quan trọng hơn, khi chúng ta vận hành bộ nhớ một cách vô tình sẽ có vấn đề xảy ra, và nhiều lần, những vấn đề này không dễ dàng phát hiện ra, chẳng hạn như rò rỉ bộ nhớ, chẳng hạn như ngón tay treo.

Chúng ta biết rằng C++ chia bộ nhớ thành ba vùng logic: Stack, Cluster và Static Storage Zone. Vì vậy, chúng ta gọi các đối tượng nằm trong đó là các đối tượng Stack, Cluster và Static. Vậy thì những đối tượng bộ nhớ khác nhau này khác nhau như thế nào?

  • 1 Khái niệm cơ bản

    Để bắt đầu, hãy nhìn vào . , thường được sử dụng để lưu trữ các biến hoặc đối tượng địa phương, như đối tượng mà chúng ta tuyên bố trong định nghĩa hàm bằng các câu tương tự như sau:

    Type stack_object ; 
    

    stack_object là một đối tượng stack, có tuổi thọ bắt đầu từ điểm định nghĩa và kết thúc khi hàm ở đó được trả về.

    Ngoài ra, hầu như tất cả các đối tượng tạm thời là các đối tượng đệm. Ví dụ, các hàm sau đây được định nghĩa:

    Type fun(Type object);
    

    Chức năng này tạo ra ít nhất hai đối tượng tạm thời, đầu tiên, các tham số được truyền theo giá trị, vì vậy sẽ được gọi hàm copy_construct để tạo ra một đối tượng tạm thời object_copy1, trong đó không phải là object mà là object_copy1, tự nhiên, object_copy1 là một đối tượng vỏ bọc, nó được giải phóng khi hàm trả về; và hàm này là giá trị trả về, khi hàm trả về, nếu chúng ta không xem xét tối ưu hóa giá trị trả về ((NRV), thì cũng sẽ tạo ra một đối tượng tạm thời object_copy2, đối tượng tạm thời này sẽ được giải phóng trong một khoảng thời gian sau khi hàm trả về. Ví dụ, một hàm có mã sau:

    Type tt ,result ; //生成两个栈对象
    
    tt = fun(tt); //函数返回时,生成的是一个临时对象object_copy2
    

    Thực hiện câu thứ hai ở trên là tạo một đối tượng tạm thời object_copy2 khi hàm fun trả về, và sau đó gọi toán tử định giá để thực hiện

    tt = object_copy2 ; //调用赋值运算符
    

    Bạn có thấy không? Các trình biên dịch đã tạo ra rất nhiều đối tượng tạm thời cho chúng ta mà chúng ta không nhận thức được, và chi phí thời gian và không gian để tạo ra các đối tượng tạm thời này có thể là rất lớn, vì vậy, bạn có thể hiểu tại sao đối với các đối tượng thạch cao thạch cao tốt nhất là truyền tham chiếu liên tục thay vì truyền tham số hàm theo giá trị.

    Tiếp theo, hãy nhìn vào stack. Stack, hay còn gọi là vùng lưu trữ tự do, nó được phân phối động trong quá trình thực thi chương trình, vì vậy đặc tính lớn nhất của nó là tính năng động. Trong C ++, tất cả các đối tượng stack phải được tạo và tiêu hủy bởi lập trình viên, vì vậy, nếu xử lý không tốt, sẽ có vấn đề về bộ nhớ. Nếu phân phối đối tượng stack, nhưng quên giải phóng, sẽ có rò rỉ bộ nhớ; và nếu đã giải phóng đối tượng, nhưng không đặt dấu chỉ số tương ứng là NULL, dấu chỉ số này được gọi là dấu chỉ số treo treo, khi sử dụng dấu chỉ số này một lần nữa, sẽ có truy cập trái phép, gây ra sự sụp đổ của chương trình.

    Vậy, cách phân bổ các đối tượng của stack trong C++ là gì? Cách duy nhất là sử dụng new ((đương nhiên, chỉ thị kiểu malloc cũng có thể lấy bộ nhớ của stack kiểu C), chỉ cần sử dụng new, một khối bộ nhớ sẽ được phân bổ trong stack và sẽ trả về một con trỏ chỉ vào đối tượng của stack đó.

    Trong thực tế, trước khi code hiển thị trong hàm main () được thực hiện, một hàm _main () được tạo bởi trình biên dịch sẽ được gọi, trong khi hàm _main () sẽ thực hiện việc cấu trúc và khởi tạo tất cả các đối tượng toàn cầu và trước khi hàm main () kết thúc, hàm exit được tạo bởi trình biên dịch sẽ được gọi để giải phóng tất cả các đối tượng toàn cầu. Ví dụ như mã sau:

    void main(void)
    {
        ... // 显式代码
    }
    
    
    // 实际上转化为这样:
    
    
    void main(void)
    {
        _main(); //隐式代码,由编译器产生,用以构造所有全局对象
        ...      // 显式代码
        ...
        exit() ; // 隐式代码,由编译器产生,用以释放所有全局对象
    }
    

    Vì vậy, khi biết được điều này, chúng ta có thể đưa ra một số thủ thuật, ví dụ, giả sử chúng ta muốn thực hiện một số công việc chuẩn bị trước khi main () được thực hiện, sau đó chúng ta có thể viết các công việc chuẩn bị này vào hàm cấu trúc của một đối tượng toàn cầu tùy chỉnh, do đó, trước khi mã hiển thị của main () được thực hiện, hàm cấu trúc của đối tượng toàn cầu này sẽ được gọi và thực hiện các hành động dự kiến, như vậy chúng ta sẽ đạt được mục đích của chúng ta.

    Một đối tượng tĩnh là một thành viên tĩnh của một lớp. Khi xem xét tình huống này, một số vấn đề phức tạp hơn đã được đưa ra.

    Vấn đề đầu tiên là tuổi thọ của các đối tượng thành viên tĩnh của lớp, các đối tượng thành viên tĩnh của lớp được tạo ra cùng với sự ra đời của đối tượng lớp đầu tiên, và biến mất khi kết thúc toàn bộ chương trình. Đó là trường hợp tồn tại, trong chương trình chúng ta định nghĩa một lớp, lớp có một đối tượng tĩnh là thành viên, nhưng trong quá trình thực hiện chương trình, nếu chúng ta không tạo ra bất kỳ đối tượng nào của lớp đó, thì sẽ không tạo ra đối tượng tĩnh mà lớp đó chứa.

    Vấn đề thứ hai xảy ra khi:

    class Base
    {
    public:
        static Type s_object ;
    }
    
    
    class Derived1 : public Base / / 公共继承
    {
    
    
        ... // other data 
    
    
    }
    
    
    class Derived2 : public Base / / 公共继承
    {
    
    
        ... // other data 
    
    
    }
    
    
    Base example ;
    
    
    Derivde1 example1 ;
    
    
    Derivde2 example2 ;
    
    
    example.s_object = …… ;
    
    
    example1.s_object = …… ; 
    
    
    example2.s_object = …… ; 
    

    Hãy chú ý đến ba câu lệnh được đánh dấu là blackbody ở trên, chúng truy cập vào s_object là cùng một đối tượng? Câu trả lời là chắc chắn, chúng thực sự chỉ đến cùng một đối tượng, điều này nghe không giống như là sự thật, phải không? Nhưng đó là sự thật, bạn có thể tự viết một đoạn mã đơn giản để xác minh.

    Hãy thử nghĩ xem, khi chúng ta truyền một đối tượng Derived1 đến một hàm chấp nhận các tham số không có tham chiếu Base, thì nó sẽ bị cắt như thế nào? Tôi tin rằng bây giờ bạn đã biết, đó là chỉ cần lấy subobject trong đối tượng Derived1 và bỏ qua tất cả các thành viên dữ liệu khác mà Derived1 tùy chỉnh, sau đó truyền subobject này cho hàm (thực tế, hàm sử dụng bản sao của subobject này).

    Tất cả các đối tượng của các lớp phái sinh kế thừa Base chứa một subobject của kiểu Base (đó là vị trí khóa của một đối tượng Derived1 có thể được chỉ định bằng chỉ số kiểu Base, và tự nhiên là khóa đa dạng), và tất cả các đối tượng của kiểu Base và tất cả các đối tượng của kiểu Base đều có cùng đối tượng s_object, tự nhiên, tất cả các trường hợp của các lớp trong toàn bộ hệ thống kế thừa của kiểu Base đều có cùng đối tượng s_object. Layout đối tượng của example, example1 và example2 được đề cập ở trên được thể hiện trong biểu đồ sau:

  • 2 So sánh ba đối tượng bộ nhớ

    đối tượng có lợi thế là tự động tạo khi thích hợp và tự động hủy khi thích hợp, không cần lập trình viên lo lắng; và các đối tượng tạo tốc độ thường nhanh hơn đối tượng đống, vì khi phân phối các đối tượng đống, sẽ gọi hoạt động mới của nhà điều hành, nhà điều hành mới sẽ sử dụng một số thuật toán tìm kiếm không gian bộ nhớ, và quá trình tìm kiếm có thể tốn nhiều thời gian, tạo ra các đối tượng không có nhiều rắc rối, nó chỉ cần di chuyển ngón trỏ . Tuy nhiên, cần lưu ý rằng dung lượng không gian thường là nhỏ, thường là 1MB 2MB, vì vậy đối tượng có kích thước lớn không phù hợp để phân phối trong .

    Đối tượng ngăn xếp, thời điểm tạo và thời điểm hủy của nó phải được xác định chính xác bởi lập trình viên, nghĩa là lập trình viên có quyền kiểm soát hoàn toàn đối với cuộc sống của đối tượng ngăn xếp. Chúng ta thường cần một đối tượng như vậy, ví dụ, chúng ta cần tạo ra một đối tượng có thể được truy cập bởi nhiều hàm, nhưng không muốn làm cho nó trở nên toàn cầu, thì lúc này tạo ra một đối tượng ngăn xếp chắc chắn là một lựa chọn tốt, sau đó truyền các con trỏ của đối tượng ngăn xếp này giữa các hàm, có thể thực hiện chia sẻ đối tượng đó. Ngoài ra, so với dung lượng không gian, dung lượng của ngăn xếp là lớn hơn nhiều.

    Tiếp theo là đối tượng tĩnh.

    Đầu tiên là đối tượng toàn cầu. Đối tượng toàn cầu cung cấp một cách đơn giản nhất cho giao tiếp giữa lớp và giao tiếp giữa các hàm, mặc dù cách này không thanh lịch. Nói chung, đối tượng toàn cầu không tồn tại trong các ngôn ngữ hướng đối tượng hoàn toàn, chẳng hạn như C#, vì đối tượng toàn cầu có nghĩa là không an toàn và cao độ kết hợp, sử dụng đối tượng toàn cầu quá nhiều trong chương trình sẽ làm giảm đáng kể sức mạnh, ổn định, khả năng bảo trì và khả năng sử dụng của chương trình.

    Tiếp theo là thành viên tĩnh của lớp, như đã đề cập ở trên, tất cả các đối tượng của lớp gốc và các class của nó đều chia sẻ đối tượng thành viên tĩnh này, vì vậy khi cần chia sẻ dữ liệu hoặc giao tiếp giữa các lớp hoặc giữa các class objects, các thành viên tĩnh như vậy chắc chắn là một lựa chọn tốt.

    Một trong những ví dụ nổi bật nhất là hàm luân phiên, chúng ta đều biết hàm luân phiên là hàm tự gọi chính nó. Nếu định nghĩa một đối tượng luân phiên không tĩnh trong hàm luân phiên, thì khi số lần luân phiên là khá lớn, chi phí cũng rất lớn. Điều này là do đối tượng luân phiên không tĩnh là đối tượng gạch, mỗi lần gọi luân phiên, sẽ tạo ra một đối tượng như vậy, mỗi lần quay trở lại, sẽ giải phóng đối tượng này, và, đối tượng như vậy chỉ giới hạn ở tầng gọi hiện tại, không thể nhìn thấy đối với các tầng nhúng sâu hơn và các tầng lộ diện hơn.

    Trong thiết kế hàm quy tụ, các đối tượng tĩnh có thể được sử dụng thay thế cho các đối tượng địa phương không tĩnh (tức là các đối tượng ván), điều này không chỉ làm giảm chi phí tạo ra và giải phóng các đối tượng không tĩnh mỗi khi gọi và trả về quy trình, mà các đối tượng tĩnh cũng có thể lưu giữ trạng thái trung gian của cuộc gọi quy tụ và có thể truy cập được cho các lớp gọi.

  • 3 Lợi ích bất ngờ của việc sử dụng vật liệu kim loại

    Như đã đề cập ở trên, các đối tượng ốc được tạo vào thời điểm thích hợp và tự động giải phóng vào thời điểm thích hợp, nghĩa là đối tượng ốc có chức năng quản lý tự động. Vậy đối tượng ốc sẽ tự động giải phóng ở đâu? Đầu tiên, khi nó kết thúc tuổi thọ; thứ hai, khi có sự bất thường trong hàm mà nó nằm. Bạn có thể nói, tất cả đều bình thường, không có gì to lớn.

    Nếu chúng ta đóng gói tài nguyên trong đối tượng và thực hiện hành động giải phóng tài nguyên trong hàm phân giải của đối tượng, thì khả năng rò rỉ tài nguyên sẽ giảm đáng kể, bởi vì đối tượng có thể tự động giải phóng tài nguyên ngay cả khi có bất thường xảy ra trong hàm. Quá trình thực tế là: khi hàm ném bất thường, cái gọi là stack_unwinding xảy ra, tức là ngăn xếp mở ra, vì đối tượng có trong ngăn xếp tự nhiên, trong quá trình quay trở lại của ngăn xếp, hàm phân giải đối tượng được thực hiện, do đó giải phóng tài nguyên bị đóng kín.

  • 4 Không được tạo ra các đối tượng đống

    Như đã đề cập ở trên, nếu bạn quyết định cấm tạo ra một loại đối tượng đống nhất định, bạn có thể tự tạo một lớp gói tài nguyên mà đối tượng đó chỉ có thể được tạo ra trong đống, để tự động giải phóng tài nguyên được gói trong trường hợp bất thường.

    Chúng ta đã biết rằng cách duy nhất để tạo ra các đối tượng ngăn xếp là sử dụng thao tác new, và nếu chúng ta cấm sử dụng new thì nó sẽ không hoạt động được nữa. Tiếp tục, khi thực hiện thao tác new, operator new sẽ được gọi, trong khi operator new có thể được tải lại.

    #include <stdlib.h> //需要用到C式内存分配函数
    
    
    class Resource ; //代表需要被封装的资源类
    
    
    class NoHashObject
    {
    private: 
        Resource* ptr ;//指向被封装的资源
    
    
        ... ... //其它数据成员
    
    
        void* operator new(size_t size) //非严格实现,仅作示意之用
        { 
            return malloc(size) ; 
        } 
    
    
        void operator delete(void* pp) //非严格实现,仅作示意之用
        { 
            free(pp) ; 
        } 
    
    
    public: 
        NoHashObject() 
        { 
            //此处可以获得需要封装的资源,并让ptr指针指向该资源
    
    
            ptr = new Resource() ; 
        } 
    
    
        ~NoHashObject() 
        { 
    
    
            delete ptr ; //释放封装的资源
        } 
    }; 
    

    NoHashObject bây giờ là một lớp cấm các đối tượng ngăn xếp, nếu bạn viết mã như sau:

    NoHashObject* fp = new NoHashObject (() ; // lỗi biên dịch!

    delete fp ;

    Có một cách để làm điều này, tôi gọi là giải mã mã mạnh mẽ. C++ mạnh mẽ đến mức bạn có thể sử dụng nó để làm bất cứ điều gì bạn muốn. Những kỹ thuật được sử dụng ở đây chủ yếu là chuyển đổi cưỡng chế kiểu chỉ số.

    void main(void)
    {
        char* temp = new char[sizeof(NoHashObject)] ; 
    
    
        //强制类型转换,现在ptr是一个指向NoHashObject对象的指针
    
    
        NoHashObject* obj_ptr = (NoHashObject*)temp ; 
    
    
        temp = NULL ; //防止通过temp指针修改NoHashObject对象
    
    
        //再一次强制类型转换,让rp指针指向堆中NoHashObject对象的ptr成员
    
    
        Resource* rp = (Resource*)obj_ptr ; 
    
    
        //初始化obj_ptr指向的NoHashObject对象的ptr成员
    
    
        rp = new Resource() ; 
    
    
        //现在可以通过使用obj_ptr指针使用堆中的NoHashObject对象成员了
    
    
        ... ... 
    
    
        delete rp ;//释放资源
    
    
        temp = (char*)obj_ptr ; 
    
    
        obj_ptr = NULL ;//防止悬挂指针产生
    
    
        delete [] temp ;//释放NoHashObject对象所占的堆空间。
    
    
        } 
    

    Việc thực hiện trên là một công việc khó khăn, và cách thực hiện này hầu như không được sử dụng trong thực tế, nhưng tôi đã viết ra một cách, bởi vì hiểu nó, chúng ta sẽ hiểu được đối tượng bộ nhớ C ++. Đối với rất nhiều chuyển đổi kiểu bắt buộc trên, điều cơ bản là gì? Chúng ta có thể hiểu như sau:

    Dữ liệu trong một khối bộ nhớ là không thay đổi, và loại là kính mà chúng ta đeo, khi chúng ta đeo một loại kính, chúng ta sẽ giải thích dữ liệu trong bộ nhớ bằng loại tương ứng, vì vậy giải thích khác nhau sẽ nhận được thông tin khác nhau.

    Những gì được gọi là chuyển đổi kiểu bắt buộc thực sự là thay đổi một cặp kính khác và xem lại cùng một dữ liệu trong bộ nhớ.

    Cũng cần lưu ý rằng các trình biên dịch khác nhau có thể bố trí dữ liệu thành viên đối tượng khác nhau, ví dụ, hầu hết các trình biên dịch đặt thành viên chỉ số ptr của NoHashObject ở 4 byte đầu tiên của không gian đối tượng, điều này đảm bảo chuyển đổi của câu nói sau đây được thực hiện như chúng ta mong đợi:

    Resource* rp = (Resource*)obj_ptr ;

    Tuy nhiên, không phải tất cả các trình biên dịch đều như vậy.

    Nếu chúng ta có thể cấm tạo ra một loại vật thể cụ thể, thì chúng ta có thể thiết kế một lớp để nó không thể tạo ra các đối tượng gạch không?

  • 5 Cấm tạo ra vật thể

    Như đã đề cập ở trên, khi tạo một đối tượng nén, bạn sẽ di chuyển con trỏ nén để lấy một khoảng không gian kích thước thích hợp của nén, sau đó gọi hàm cấu trúc tương ứng trực tiếp trên không gian này để tạo ra một đối tượng nén, và khi hàm quay trở lại, bạn sẽ gọi hàm phân tích của nó để giải phóng đối tượng này, sau đó điều chỉnh con trỏ nén để lấy lại bộ nhớ nén. Trong quá trình này, không cần vận hành new / delete, vì vậy thiết lập new / delete là riêng tư sẽ không đạt được mục đích.

    Nhưng trước khi làm vậy, có một điều cần xem xét, đó là nếu chúng ta đặt hàm cấu trúc là riêng tư, thì chúng ta cũng không thể sử dụng new để tạo trực tiếp các đối tượng đống, bởi vì new sẽ gọi hàm cấu trúc của nó sau khi phân bổ không gian cho đối tượng. Vì vậy, tôi chỉ định đặt hàm phân tích là riêng tư. Tiếp tục phân tích, đặt hàm cấu trúc là riêng tư ngoài việc sẽ hạn chế việc tạo đối tượng, có ảnh hưởng khác không?

    Nếu một lớp không được định làm lớp gốc, một giải pháp thường được sử dụng là tuyên bố hàm phân tích của nó là private.

    Để hạn chế các đối tượng đệm mà không hạn chế kế thừa, chúng ta có thể tuyên bố hàm phân tích là protected, và điều này là tốt nhất cho cả hai. Như mã sau:

    class NoStackObject
    {
    protected: 
    
    
        ~NoStackObject() { } 
    
    
    public: 
    
    
        void destroy() 
    
    
        { 
    
    
            delete this ;//调用保护析构函数
    
    
        } 
    }; 
    

    Tiếp theo, bạn có thể sử dụng lớp NoStackObject như sau:

    NoStackObject* hash_ptr = new NoStackObject() ;

    … … // thực hiện hành động với đối tượng hash_ptr

    hash_ptr->destroy() ; Ồ, có vẻ hơi lạ khi chúng ta tạo một đối tượng bằng new, nhưng thay vì xóa nó bằng delete, chúng ta sử dụng phương thức destroy. Dĩ nhiên là người dùng không quen với cách sử dụng kỳ lạ này. Vì vậy, tôi quyết định đặt hàm cấu trúc là private hoặc protected.

    class NoStackObject
    {
    protected: 
    
    
        NoStackObject() { } 
    
    
        ~NoStackObject() { } 
    
    
    public: 
    
    
        static NoStackObject* creatInstance() 
        { 
            return new NoStackObject() ;//调用保护的构造函数
        } 
    
    
        void destroy() 
        { 
    
    
            delete this ;//调用保护的析构函数
    
    
        } 
    };
    

    Bây giờ chúng ta có thể sử dụng lớp NoStackObject như sau:

    NoStackObject* hash_ptr = NoStackObject::creatInstance() ;

    … … // thực hiện hành động với đối tượng hash_ptr

    hash_ptr->destroy() ;

    hash_ptr = NULL; // ngăn chặn sử dụng con trỏ treo

    Bây giờ có cảm giác tốt hơn không, việc tạo ra đối tượng và giải phóng đối tượng đã được thực hiện một cách nhất quán.

  • Phương pháp tái chế rác trong C++

Nhiều lập trình viên C hoặc C++ coi thường việc thu hồi rác, cho rằng việc thu hồi rác chắc chắn kém hiệu quả hơn việc tự quản lý bộ nhớ động, và khi thu hồi sẽ khiến chương trình dừng lại ở đó, trong khi nếu bạn kiểm soát quản lý bộ nhớ, thời gian phân bổ và giải phóng đều ổn định, không dẫn đến việc chương trình ngừng hoạt động. Cuối cùng, nhiều lập trình viên C / C ++ tin rằng không thể thực hiện cơ chế thu hồi rác trong C / C ++. Những quan điểm sai lầm này được đưa ra do không hiểu biết về thuật toán thu hồi rác.

Trên thực tế, cơ chế thu hồi rác không chậm, thậm chí còn hiệu quả hơn so với phân phối bộ nhớ động. Vì chúng ta chỉ có thể phân phối mà không giải phóng, thì khi phân phối bộ nhớ chỉ cần lấy bộ nhớ mới từ đống, di chuyển đầu đống là đủ; và quá trình giải phóng đã bị bỏ qua, tự nhiên cũng tăng tốc. Các thuật toán thu hồi rác hiện đại đã phát triển rất nhiều, các thuật toán thu thập gia tăng đã có thể cho phép quá trình thu hồi rác được thực hiện theo từng phần, tránh gián đoạn chương trình.

Các thuật toán thu hồi rác thường dựa trên việc quét và đánh dấu tất cả các khối bộ nhớ hiện có thể được sử dụng, thu hồi bộ nhớ không được đánh dấu từ tất cả các bộ nhớ đã được phân bổ. Quan điểm về việc không thể thực hiện thu hồi rác trong C / C ++ thường dựa trên việc không thể quét chính xác tất cả các khối bộ nhớ có thể được sử dụng, nhưng điều dường như không thể thực hiện trên thực tế là không phức tạp. Đầu tiên, bằng cách quét dữ liệu trong bộ nhớ, chỉ số được phân bổ động trên bộ nhớ được dễ dàng nhận ra, và nếu có lỗi nhận dạng, chỉ có một số dữ liệu không chỉ số có thể được chỉ số, chứ không phải chỉ số đó là dữ liệu không chỉ số.

Khi thu hồi rác, chỉ cần quét đoạn bss, đoạn data và không gian rác hiện đang được sử dụng, tìm ra số lượng có thể là chỉ số bộ nhớ động, quét lặp lại bộ nhớ được tham khảo để có được tất cả bộ nhớ động hiện đang được sử dụng.

Nếu bạn thực hiện một bộ xử lý rác tốt cho dự án của bạn, bạn có thể cải thiện tốc độ quản lý bộ nhớ và thậm chí giảm tổng lượng bộ nhớ tiêu thụ. Nếu bạn quan tâm, bạn có thể tìm kiếm các bài báo và thư viện về việc xử lý rác trên mạng, mở rộng tầm nhìn đặc biệt quan trọng đối với một lập trình viên.

Tác giả:HK Zhang

  • #### Tại sao một chỉ số có thể tồn tại cho đến khi kết thúc toàn bộ chương trình khi được đặt địa chỉ cho một biến địa phương?
  #include<stdio.h>
  int*fun(){
      int k = 12;
      return &k;
  }
  int main(){
      int *p = fun();    
      printf("%d\n", *p);

      getchar();
      return 0;
  }

Không chỉ có thể truy cập, mà còn có thể sửa đổi, nhưng truy cập đó là không chắc chắn. Địa chỉ của biến cục bộ đều nằm trong stack của chính chương trình, sau khi biến cục bộ của chính quyền kết thúc, giá trị của nó vẫn tồn tại miễn là không cho địa chỉ bộ nhớ của biến cục bộ đó cho một biến khác. Nhưng nếu sửa đổi, thì nguy hiểm hơn, bởi vì địa chỉ bộ nhớ này có thể được cung cấp cho các biến khác của chương trình, có thể gây ra sự sụp đổ của chương trình nếu sửa đổi bằng chỉ số.

csdn bbs