Tài nguyên đang được tải lên... tải...

C++ chiến lược viết kiến thức C++ recoveries

Tác giả:Những nhà phát minh định lượng - những giấc mơ nhỏ, Tạo: 2017-12-29 11:00:02, Cập nhật: 2017-12-29 11:25:11

C++ chiến lược viết kiến thức C++ recoveries

Có một số kiến thức cơ bản cần phải biết trước khi viết các chính sách C ++, ít nhất là biết các quy tắc này. Dưới đây là tài liệu chuyển tiếp:

  • Chiến tranh đối tượng bộ nhớ C++

Nếu một người tự xưng 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 phải quan tâm nhiều hơn đến bộ nhớ, không chỉ vì việc phân bổ bộ nhớ 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 thao tác bộ nhớ, những vấn đề sẽ xuất hiện một cách không cẩn thận, và nhiều lần, những vấn đề này là không dễ phát hiện, chẳng hạn như rò rỉ bộ nhớ, chẳng hạn như treo con trỏ.

Chúng ta biết rằng C++ phân chia bộ nhớ thành ba khu vực logic: ngăn xếp, nồi và kho lưu trữ tĩnh. Vì vậy, tôi gọi các đối tượng nằm trong chúng là các đối tượng ngăn xếp, nồi và tĩnh. Vậy những đối tượng khác nhau này khác nhau như thế nào?

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

    Trước tiên, hãy nhìn vào ──, thường được sử dụng để lưu trữ các biến hoặc đối tượng cục bộ, ví dụ như đối tượng mà chúng ta tuyên bố trong định nghĩa chức năng bằng câu nói tương tự như sau:

    Type stack_object ; 
    

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

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

    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ị, do đó sẽ gọi chức năng tạo bản sao để tạo ra một đối tượng tạm thời object_copy1, được sử dụng trong hàm không phải là object, mà là object_copy1, tự nhiên, object_copy1 là một đối tượng lồng, nó được giải phóng khi hàm trả về; và chức năng này là giá trị trả về, khi hàm trả về, nó cũng tạo ra một đối tượng tạm thời object_copy2, nếu chúng ta không xem xét tối ưu hóa giá trị trả về. Ví dụ, một hàm có mã như sau:

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

    Việc thực hiện của 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ử gán để thực hiện.

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

    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à việc tạo ra những đối tượng tạm thời này có thể là một chi phí lớn về thời gian và không gian, vì vậy, bạn có thể hiểu tại sao đối với các đối tượng convex tốt hơn là chuyển qua các tham số hàm theo giá trị thay vì chuyển qua các tham số hàm theo giá trị.

    Tiếp theo, hãy nhìn vào ngăn xếp. Các ngăn xếp, còn được gọi là khu vực lưu trữ tự do, được phân bổ năng động trong quá trình thực hiện chương trình, vì vậy đặc điểm lớn nhất của nó là tính năng động. Trong C ++, tất cả các đối tượng ngăn xếp đều chịu trách nhiệm tạo và phá hủy bởi lập trình viên, vì vậy, nếu xử lý không tốt, vấn đề bộ nhớ sẽ xảy ra.

    Vậy làm thế nào để phân bổ các đối tượng trong C++? Cách duy nhất là sử dụng new (dĩ nhiên, các lệnh của loại malloc cũng có thể lấy bộ nhớ cột C), chỉ cần sử dụng new, nó sẽ phân bổ một khối bộ nhớ trong cột và trả lại trỏ cho đối tượng đó.

    Hãy xem lại vùng lưu trữ tĩnh. Tất cả các đối tượng tĩnh, toàn cầu đều được phân bổ vào vùng lưu trữ tĩnh. Đối với các đối tượng toàn cầu, nó được phân bổ trước khi thực hiện hàm main. Trong thực tế, trước khi thực hiện mã hiển thị trong hàm main, một hàm main được tạo ra bởi trình biên dịch được gọi trước khi thực hiện, và hàm main sẽ thực hiện công việc xây dựng và khởi tạo tất cả các đối tượng toàn cầu.

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

    Vì vậy, khi biết điều này, chúng ta có thể rút ra một số thủ thuật, ví dụ như giả sử chúng ta phải làm một số công việc chuẩn bị trước khi thực hiện hàm main(), thì chúng ta có thể viết những 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 được định nghĩa, do đó, trước khi thực hiện mã hiển thị của hàm main(, 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, và như vậy đạt được mục đích của chúng ta.

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

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

    Câu hỏi thứ hai là, khi những tình huống sau đây xảy ra:

    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ú ý rằng ba câu nói trên được đánh dấu là blackbody, và các s_object mà chúng truy cập là cùng một đối tượng? Câu trả lời là có, chúng thực sự trỏ đến cùng một đối tượng, điều này nghe có vẻ không đúng, 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. Tôi sẽ giải thích tại sao điều này xảy ra.

    Hãy nghĩ rằng khi chúng ta truyền một đối tượng kiểu Derived1 cho một hàm mà không tham khảo các tham số kiểu Base, thì sẽ có một sự cắt giảm. Vậy làm thế nào để cắt giảm?

    Tất cả các đối tượng của các lớp phái sinh của lớp BASE đều chứa một subobject kiểu BASE (đó là chìa khóa có thể dùng trỏ kiểu BASE để trỏ đến một đối tượng Derived1, và tự nhiên cũng là chìa khóa đa dạng), trong khi tất cả các subobject và tất cả các đối tượng kiểu BASE đều chia sẻ cùng một s_object, tự nhiên, tất cả các trường hợp của lớp trong toàn bộ hệ thống kế thừa từ lớp BASE sẽ chia sẻ cùng một s_object.

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

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

    Các đối tượng ngăn xếp, lúc tạo và lúc phá hủy đều được lập trình viên xác định, nghĩa là lập trình viên có quyền kiểm soát hoàn toàn cuộc sống của các đối tượng ngăn xếp. Chúng ta thường cần các đối tượng như vậy, ví dụ, chúng ta cần tạo một đối tượng có thể truy cập được bởi nhiều chức năng, nhưng không muốn nó trở nên toàn cầu, thì lúc này tạo 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 chỉ số đối tượng ngăn xếp này giữa các chức năng để có thể chia sẻ đối tượng đó.

    Sau đó, hãy xem các đối tượng tĩnh.

    Đầu tiên là các đối tượng toàn cầu. Các đối tượng toàn cầu cung cấp một cách đơn giản nhất để giao tiếp giữa các 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, trong ngôn ngữ hướng đối tượng hoàn toàn, không có các đối tượng toàn cầu, chẳng hạn như C#, vì các đối tượng toàn cầu có nghĩa là không an toàn và có độ kết nối cao, và việc sử dụng quá nhiều đối tượng toàn cầu trong chương trình sẽ làm giảm đáng kể độ mạnh mẽ, ổn định, bảo trì và khả năng lặp lại của chương trình.

    Sau đó là các 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 cơ bản và các lớp phái sinh 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 các đối tượng lớp, các thành viên tĩnh như vậy chắc chắn là một lựa chọn tốt.

    Sau đó là các đối tượng địa phương tĩnh, chủ yếu được sử dụng để lưu giữ trạng thái trung gian trong thời gian hàm trong đó đối tượng đó được gọi lặp đi lặp lại, một trong những ví dụ nổi bật nhất là các chức năng hồi quy, chúng ta đều biết rằng các chức năng hồi quy là các chức năng tự gọi, nếu xác định một đối tượng địa phương phi tĩnh trong các chức năng hồi quy, thì khi số lần hồi quy là khá lớn, thì chi phí cũng rất lớn.

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

  • 3 Vận dụng vật liệu xăng để thu hoạch ngẫu nhiên

    Như đã đề cập ở trên, các đối tượng được tạo ra tại thời điểm thích hợp và sau đó được tự động phát hành tại thời điểm thích hợp, nghĩa là các đối tượng có chức năng quản lý tự động. Vậy các đối tượng sẽ tự động phát hành ở đâu?

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

  • 4 Ngăn chặn tạo các đối tượng hàng loạt

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

    Vậy làm thế nào để cấm tạo ra các đối tượng ngăn xếp? 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 new, nếu chúng ta cấm sử dụng new thì sẽ không hiệu quả. Hơn nữa, new sẽ gọi operator new khi thực hiện, trong khi operator new có thể được tải lại. Một cách khác là đặt new operator là private, để đối xứng, tốt nhất bạn cũng nên đặt operator là private. Bây giờ, bạn có thể có một câu hỏi nữa, liệu tạo ra các đối tượng ngăn xếp không cần gọi new?

    #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 trong ngăn xếp nếu bạn viết mã như sau:

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

    xóa fp;

    Mã trên sẽ tạo ra lỗi biên dịch. Bây giờ bạn đã biết cách thiết kế một lớp cấm các đối tượng chồng, bạn có thể có câu hỏi như tôi, không phải bạn không thể tạo ra các đối tượng chồng của loại mà không thể thay đổi định nghĩa của lớp NoHashObject? Không, có một cách, tôi gọi đó là giải pháp phá vỡ bạo lực. C ++ rất mạnh mẽ, mạnh mẽ đến nỗi bạn có thể làm bất cứ điều gì bạn muốn với nó.

    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à 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 vẫn viết đường đi, bởi vì hiểu nó là rất hữu ích cho việc hiểu các đối tượng bộ nhớ C++.

    Dữ liệu trong một bộ nhớ là không thay đổi, và kiểu là những chiếc kính mà chúng ta đeo, và khi chúng ta đeo một chiếc kính, chúng ta sẽ giải thích dữ liệu trong bộ nhớ bằng kiểu tương ứng, do đó những giải thích khác nhau sẽ đưa ra thông tin khác nhau.

    Việc 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 bộ 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ể sắp xếp bố cục dữ liệu thành viên của đối tượng khác nhau, ví dụ, hầu hết các trình biên dịch đều sắp xếp thành viên chỉ số ptr của NoHashObject ở 4 byte đầu tiên của không gian đối tượng để đảm bảo các hành động chuyển đổi của câu nói dưới đây được thực hiện như chúng ta mong đợi:

    Tài nguyên* 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.

    Vì chúng ta có thể cấm tạo ra các đối tượng hàng loạt của một loại nhất định, thì liệu chúng ta có thể thiết kế một lớp mà nó không thể tạo ra các đối tượng hàng loạt?

  • 5 cấm tạo ra các đối tượng silicon

    Như đã đề cập trước đây, khi tạo một đối tượng chuông, bạn sẽ di chuyển con trỏ của chuông để lấy một không gian có kích thước chuông phù hợp, và sau đó trực tiếp gọi vào không gian đó một hàm cấu trúc tương ứng để tạo ra một đối tượng chuông, và khi hàm trở lại, bạn sẽ gọi hàm phân tích của nó để giải phóng đối tượng, sau đó điều chỉnh con trỏ của chuông để lấy lại bộ nhớ chuông.

    Có thể, và tôi cũng dự định sử dụng phương pháp này. Nhưng trước đó, một điều cần phải 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 hàng loạt, 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ỉ dự định đặt hàm phân tích là riêng tư.

    Nếu một lớp không được dự định là một lớp cơ bản, 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à riêng tư.

    Để hạn chế các đối tượng của hàm mà không hạn chế sự kế thừa, chúng ta có thể tuyên bố hàm phân tích là protected, vì vậy cả hai đều tốt.

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

    Sau đó, bạn có thể sử dụng lớp NoStackObject như thế này:

    NoStackObject* hash_ptr = new NoStackObject() ;

    ...... // Hoạt động đối với đối tượng hash_ptr

    hash_ptr->destroy (); Không. Ồ, có vẻ hơi kỳ lạ không, chúng ta tạo một đối tượng bằng new, nhưng không sử dụng delete để xóa nó, mà sử dụng phương pháp destroy. Rõ ràng, 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 vào private hoặc protected. Điều này trở lại vấn đề mà tôi đã cố gắng tránh ở trên, đó là tạo một đối tượng bằng cách nào mà không sử dụng new?

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

    Bây giờ bạn có thể sử dụng lớp NoStackObject như sau:

    NoStackObject* hash_ptr = NoStackObject::creatInstance() ;

    ...... // Hoạt động đối với đối tượng hash_ptr

    hash_ptr->destroy() ;

    hash_ptr = NULL; // Ngăn chặn sử dụng trục tròn

    Bây giờ cảm giác không tốt hơn, việc tạo và giải phóng đối tượng hoạt động giống nhau.

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

    Nhiều lập trình viên C hoặc C++ không quan tâm đến việc tái chế rác, cho rằng việc tái chế rác chắc chắn sẽ kém hiệu quả hơn so với việc quản lý bộ nhớ động của mình, và sẽ khiến chương trình dừng lại ở đó khi tái chế, trong khi nếu họ kiểm soát quản lý bộ nhớ, thời gian phân bổ và giải phóng đều ổn định và không gây ra sự dừng lại của chương trình. 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ế tái chế rác trong C/C++. Những quan điểm sai lầm này được đưa ra bởi sự thiếu hiểu biết về các thuật toán tái chế rác.

    Trong thực tế, các cơ chế tái chế rác không chậm và thậm chí hiệu quả hơn so với phân bổ bộ nhớ động. Vì chúng ta chỉ có thể phân bổ không giải phóng, thì việc phân bổ bộ nhớ chỉ cần lấy bộ nhớ mới từ ngăn xếp liên tục, chỉ số của ngăn xếp di động là đủ; và quá trình giải phóng đã bị loại bỏ và tự nhiên tăng tốc. Các thuật toán tái chế rác hiện đại đã phát triển rất nhiều, và các thuật toán thu thập gia tăng đã cho phép quá trình tái chế rác diễn ra từng giai đoạn, tránh việc 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ớ có thể được sử dụng hiện tại, và 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ể thu hồi rác trong C/C++ thường dựa trên việc không thể quét đúng tất cả các khối bộ nhớ có thể còn được sử dụng, tuy nhiên, những điều dường như không thể thực hiện được thực sự không phức tạp. Thứ nhất, chỉ dẫn các dấu chấm được phân bổ năng động vào bộ nhớ trên chồng dễ dàng được xác định bằng cách quét dữ liệu trong bộ nhớ, và nếu có lỗi nhận dạng, chỉ có thể chỉ định một số dữ liệu không chỉ định là dấu chấm, chứ không chỉ định các dấu chấm là dữ liệu không chỉ định.

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

    Nếu bạn muốn thực hiện một máy thu rác tốt cho dự án của mình, bạn có thể tăng tốc độ quản lý bộ nhớ hoặc thậm chí giảm tổng lượng bộ nhớ sử dụng. 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 thực hiện đượ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.

    Được chuyển từHK Zhang

  • Tại sao một con trục có thể kéo dài vòng đời của nó cho đến khi toàn bộ chương trình kết thúc khi nó đượ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 việc truy cập đó là không chắc chắn. Các địa chỉ của biến địa phương đều nằm trong ngăn xếp của chương trình, và sau khi biến quyền kết thúc, giá trị của nó vẫn tồn tại miễn là không trao địa chỉ bộ nhớ của biến địa phương cho biến khác. Tuy nhiên, nếu được sửa đổi, thì nguy hiểm hơn, vì địa chỉ bộ nhớ này có thể được trao 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 bị buộc thay đổi bằng con trỏ.

    csdn bbs


Thêm nữa