Ngăn xếp, tĩnh và heap trong C ++


160

Tôi đã tìm kiếm, nhưng tôi không hiểu rõ ba khái niệm này. Khi nào tôi phải sử dụng phân bổ động (trong heap) và lợi thế thực sự của nó là gì? Các vấn đề của tĩnh và ngăn xếp là gì? Tôi có thể viết toàn bộ ứng dụng mà không cần phân bổ các biến trong heap không?

Tôi nghe nói rằng các ngôn ngữ khác kết hợp một "trình thu gom rác" để bạn không phải lo lắng về bộ nhớ. Người thu gom rác làm gì?

Bạn có thể tự mình điều khiển bộ nhớ mà bạn không thể sử dụng trình thu gom rác này?

Có lần ai đó nói với tôi rằng với tuyên bố này:

int * asafe=new int;

Tôi có một "con trỏ đến một con trỏ". Nó có nghĩa là gì? Nó khác với:

asafe=new int;

?


Có một câu hỏi rất giống nhau được hỏi cách đây một thời gian: stack và heap là gì và ở đâu? Có một vài câu trả lời thực sự hay cho câu hỏi đó sẽ làm sáng tỏ bạn.
Scott Saad

Bản sao có thể có của ngăn xếp và đống ở đâu?
Swati Garg

Câu trả lời:


223

Một câu hỏi tương tự đã được hỏi, nhưng nó không hỏi về thống kê.

Tóm tắt về bộ nhớ tĩnh, heap và stack là gì:

  • Một biến tĩnh về cơ bản là một biến toàn cục, ngay cả khi bạn không thể truy cập nó trên toàn cầu. Thông thường có một địa chỉ cho nó là trong chính thực thi. Chỉ có một bản sao cho toàn bộ chương trình. Bất kể bạn đi vào một hàm gọi (hoặc lớp) bao nhiêu lần (và trong bao nhiêu luồng!), Biến này được tham chiếu đến cùng một vị trí bộ nhớ.

  • Heap là một loạt các bộ nhớ có thể được sử dụng linh hoạt. Nếu bạn muốn 4kb cho một đối tượng thì bộ cấp phát động sẽ xem qua danh sách không gian trống của nó trong heap, chọn ra một đoạn 4kb và đưa nó cho bạn. Nói chung, bộ cấp phát bộ nhớ động (malloc, new, et c.) Bắt đầu ở cuối bộ nhớ và hoạt động ngược.

  • Giải thích làm thế nào một ngăn xếp phát triển và co lại là một chút nằm ngoài phạm vi của câu trả lời này, nhưng đủ để nói rằng bạn luôn luôn thêm và xóa từ cuối. Ngăn xếp thường bắt đầu cao và phát triển xuống địa chỉ thấp hơn. Bạn hết bộ nhớ khi ngăn xếp gặp bộ cấp phát động ở đâu đó ở giữa (nhưng tham khảo bộ nhớ vật lý và bộ nhớ ảo). Nhiều luồng sẽ yêu cầu nhiều ngăn xếp (quá trình thường dành một kích thước tối thiểu cho ngăn xếp).

Khi bạn muốn sử dụng từng cái:

  • Số liệu thống kê / toàn cầu rất hữu ích cho bộ nhớ mà bạn biết bạn sẽ luôn cần và bạn biết rằng bạn không bao giờ muốn giải quyết. (Nhân tiện, các môi trường nhúng có thể được coi là chỉ có bộ nhớ tĩnh ... ngăn xếp và đống là một phần của không gian địa chỉ đã biết được chia sẻ bởi loại bộ nhớ thứ ba: mã chương trình. Các chương trình thường sẽ phân bổ động ra khỏi chúng Bộ nhớ tĩnh khi chúng cần những thứ như danh sách được liên kết. Nhưng bất kể, chính bộ nhớ tĩnh (bộ đệm) không phải là "được phân bổ", mà là các đối tượng khác được phân bổ ra khỏi bộ nhớ được giữ bởi bộ đệm cho mục đích này. Bạn có thể làm điều này cũng không được nhúng và các trò chơi trên bàn điều khiển sẽ thường xuyên tránh các cơ chế bộ nhớ động tích hợp có lợi cho việc kiểm soát chặt chẽ quá trình phân bổ bằng cách sử dụng bộ đệm có kích thước đặt trước cho tất cả các phân bổ.)

  • Các biến ngăn xếp rất hữu ích khi bạn biết rằng miễn là hàm nằm trong phạm vi (trên ngăn xếp ở đâu đó), bạn sẽ muốn các biến vẫn còn. Ngăn xếp là tốt cho các biến mà bạn cần cho mã nơi chúng được đặt, nhưng không cần thiết ngoài mã đó. Chúng cũng thực sự tốt khi bạn truy cập tài nguyên, như tệp và muốn tài nguyên tự động biến mất khi bạn rời khỏi mã đó.

  • Phân bổ heap (bộ nhớ được phân bổ động) rất hữu ích khi bạn muốn linh hoạt hơn so với ở trên. Thông thường, một chức năng được gọi để phản hồi một sự kiện (người dùng nhấp vào nút "tạo hộp"). Phản hồi thích hợp có thể yêu cầu phân bổ một đối tượng mới (một đối tượng Box mới) sẽ tồn tại lâu sau khi thoát khỏi chức năng, vì vậy nó không thể nằm trên ngăn xếp. Nhưng bạn không biết bao nhiêu hộp bạn muốn khi bắt đầu chương trình, vì vậy nó không thể là tĩnh.

Thu gom rác thải

Gần đây tôi đã nghe rất nhiều về việc Người thu gom rác tuyệt vời như thế nào, vì vậy có lẽ một chút tiếng nói bất đồng sẽ hữu ích.

Bộ sưu tập rác là một cơ chế tuyệt vời khi hiệu suất không phải là vấn đề lớn. Tôi nghe nói các GC đang trở nên tốt hơn và tinh vi hơn, nhưng thực tế là, bạn có thể bị buộc phải chấp nhận một hình phạt hiệu suất (tùy thuộc vào trường hợp sử dụng). Và nếu bạn lười biếng, nó vẫn có thể không hoạt động đúng. Vào thời điểm tốt nhất, Bộ sưu tập rác nhận ra rằng bộ nhớ của bạn sẽ biến mất khi nhận ra rằng không còn tài liệu tham khảo nào nữa (xem phần tham khảo). Nhưng, nếu bạn có một đối tượng đề cập đến chính nó (có thể bằng cách tham chiếu đến một đối tượng khác tham chiếu lại), thì việc đếm tham chiếu một mình sẽ không chỉ ra rằng bộ nhớ có thể bị xóa. Trong trường hợp này, GC cần xem xét toàn bộ món súp tham khảo và tìm hiểu xem có hòn đảo nào chỉ được nhắc đến bởi chính họ không. Chính thức, tôi đoán rằng đó là một hoạt động O (n ^ 2), nhưng cho dù đó là gì, nó có thể trở nên tồi tệ nếu bạn hoàn toàn quan tâm đến hiệu suất. (Chỉnh sửa: Martin B chỉ ra rằng đó là O (n) cho các thuật toán hiệu quả hợp lý. Đó vẫn là O (n) quá nhiều nếu bạn quan tâm đến hiệu suất và có thể giải quyết trong thời gian liên tục mà không cần thu gom rác.)

Cá nhân, khi tôi nghe mọi người nói rằng C ++ không có bộ sưu tập rác, tôi nghĩ rằng đó là một tính năng của C ++, nhưng tôi có lẽ là thiểu số. Có lẽ điều khó nhất để mọi người tìm hiểu về lập trình trong C và C ++ là con trỏ và cách xử lý chính xác việc phân bổ bộ nhớ động của họ. Một số ngôn ngữ khác, như Python, sẽ rất kinh khủng nếu không có GC, vì vậy tôi nghĩ nó phù hợp với những gì bạn muốn từ một ngôn ngữ. Nếu bạn muốn hiệu năng đáng tin cậy, thì C ++ không có bộ sưu tập rác là điều duy nhất bên này của Fortran mà tôi có thể nghĩ đến. Nếu bạn muốn dễ dàng sử dụng và đào tạo bánh xe (để cứu bạn khỏi sự cố mà không yêu cầu bạn học quản lý bộ nhớ "đúng"), hãy chọn một cái gì đó với một GC. Ngay cả khi bạn biết cách quản lý bộ nhớ tốt, nó sẽ giúp bạn tiết kiệm thời gian mà bạn có thể dành để tối ưu hóa mã khác. Thực sự không còn nhiều hình phạt về hiệu suất nữa, nhưng nếu bạn thực sự cần hiệu suất đáng tin cậy (và khả năng biết chính xác những gì đang diễn ra, khi nào, dưới vỏ bọc) thì tôi sẽ gắn bó với C ++. Có một lý do mà mọi công cụ trò chơi lớn mà tôi từng nghe nói là ở C ++ (nếu không phải là C hoặc lắp ráp). Python, et al là tốt cho kịch bản, nhưng không phải là công cụ trò chơi chính.


Nó thực sự không liên quan đến câu hỏi ban đầu (hoặc thực sự là rất nhiều), nhưng bạn đã có được vị trí của ngăn xếp và chất đống. Thông thường , ngăn xếp phát triển xuống và đống lớn lên (mặc dù một đống không thực sự "phát triển", vì vậy đây là một sự đơn giản hóa quá lớn) ...
P Daddy

Tôi không nghĩ rằng câu hỏi này tương tự hoặc thậm chí trùng lặp với câu hỏi khác. đây là đặc biệt về C ++ và ý anh gần như chắc chắn là ba thời lượng lưu trữ hiện có trong C ++. Bạn có thể có một đối tượng động được phân bổ trên bộ nhớ tĩnh tốt, ví dụ, quá tải op mới.
Julian Schaub - litb

7
Xử lý xúc phạm của bạn về thu gom rác ít hơn một chút hữu ích.
P Daddy

9
Ngày nay, bộ sưu tập rác ngày nay tốt hơn bộ nhớ giải phóng thủ công vì nó xảy ra khi có rất ít việc phải làm, trái ngược với việc giải phóng bộ nhớ có thể xảy ra ngay khi hiệu suất có thể được sử dụng theo cách khác.
Georg Schölly

3
Chỉ cần một nhận xét nhỏ - bộ sưu tập rác không có độ phức tạp O (n ^ 2) (điều đó thực sự sẽ là thảm họa đối với hiệu suất). Thời gian dành cho một chu kỳ thu gom rác tỷ lệ thuận với kích thước của đống - xem hpl.hp.com/personal/Hans_Boehm/gc/complexity.html .
Martin B

54

Sau đây là tất nhiên không hoàn toàn chính xác. Mang nó với một hạt muối khi bạn đọc nó :)

Vâng, ba điều bạn đề cập đến là thời gian lưu trữ tự động, tĩnh và động , có liên quan đến thời gian các vật thể sống và khi chúng bắt đầu cuộc sống.


Thời gian lưu trữ tự động

Bạn sử dụng thời lượng lưu trữ tự động cho thời gian ngắn và dữ liệu nhỏ , chỉ cần cục bộ trong một số khối:

if(some condition) {
    int a[3]; // array a has automatic storage duration
    fill_it(a);
    print_it(a);
}

Thời gian tồn tại kết thúc ngay khi chúng ta thoát khỏi khối và nó bắt đầu ngay khi đối tượng được xác định. Chúng là loại thời lượng lưu trữ đơn giản nhất và nhanh hơn thời lượng lưu trữ động cụ thể.


Thời lượng lưu trữ tĩnh

Bạn sử dụng thời lượng lưu trữ tĩnh cho các biến miễn phí, có thể được truy cập bởi bất kỳ mã nào mọi lúc, nếu phạm vi của chúng cho phép sử dụng như vậy (phạm vi không gian tên) và cho các biến cục bộ cần kéo dài thời gian thoát khỏi phạm vi của chúng (phạm vi cục bộ) và cho các biến thành viên cần được chia sẻ bởi tất cả các đối tượng của lớp (phạm vi lớp). Tuổi thọ của chúng phụ thuộc vào phạm vi chúng nằm trong. Chúng có thể có phạm vi không gian tênphạm vi cục bộphạm vi lớp . Điều đúng về cả hai là, một khi cuộc sống của họ bắt đầu, cuộc đời kết thúc vào cuối chương trình . Đây là hai ví dụ:

// static storage duration. in global namespace scope
string globalA; 
int main() {
    foo();
    foo();
}

void foo() {
    // static storage duration. in local scope
    static string localA;
    localA += "ab"
    cout << localA;
}

Chương trình in ababab, vì localAkhông bị phá hủy khi thoát khỏi khối của nó. Bạn có thể nói rằng các đối tượng có phạm vi cục bộ bắt đầu trọn đời khi điều khiển đạt đến định nghĩa của chúng . Đối với localA, nó xảy ra khi cơ thể của chức năng được nhập vào. Đối với các đối tượng trong phạm vi không gian tên, thời gian bắt đầu khi khởi động chương trình . Điều này cũng đúng với các đối tượng tĩnh của phạm vi lớp:

class A {
    static string classScopeA;
};

string A::classScopeA;

A a, b; &a.classScopeA == &b.classScopeA == &A::classScopeA;

Như bạn thấy, classScopeAkhông bị ràng buộc với các đối tượng cụ thể của lớp, mà với chính lớp đó. Địa chỉ của cả ba tên trên đều giống nhau và tất cả đều biểu thị cùng một đối tượng. Có một quy tắc đặc biệt về thời điểm và cách thức các đối tượng tĩnh được khởi tạo, nhưng bây giờ chúng ta không quan tâm đến điều đó. Điều đó có nghĩa là bởi thuật ngữ khởi tạo tĩnh fiasco .


Thời lượng lưu trữ động

Thời gian lưu trữ cuối cùng là động. Bạn sử dụng nó nếu bạn muốn có các đối tượng sống trên một hòn đảo khác và bạn muốn đặt các con trỏ xung quanh tham chiếu chúng. Bạn cũng sử dụng chúng nếu các đối tượng của bạn lớn và nếu bạn muốn tạo các mảng có kích thước chỉ được biết khi chạy . Do tính linh hoạt này, các đối tượng có thời gian lưu trữ động rất phức tạp và chậm quản lý. Các đối tượng có thời lượng động đó bắt đầu trọn đời khi một lệnh gọi toán tử mới thích hợp xảy ra:

int main() {
    // the object that s points to has dynamic storage 
    // duration
    string *s = new string;
    // pass a pointer pointing to the object around. 
    // the object itself isn't touched
    foo(s);
    delete s;
}

void foo(string *s) {
    cout << s->size();
}

Cuộc đời của nó chỉ kết thúc khi bạn gọi xóa cho họ. Nếu bạn quên điều đó, những đối tượng đó không bao giờ kết thúc trọn đời. Và các đối tượng lớp xác định hàm tạo được khai báo của người dùng sẽ không có hàm hủy của chúng được gọi. Các đối tượng có thời lượng lưu trữ động yêu cầu xử lý thủ công thời gian sống và tài nguyên bộ nhớ liên quan. Thư viện tồn tại để dễ dàng sử dụng chúng. Bộ sưu tập rác rõ ràng cho các đối tượng cụ thể có thể được thiết lập bằng cách sử dụng một con trỏ thông minh:

int main() {
    shared_ptr<string> s(new string);
    foo(s);
}

void foo(shared_ptr<string> s) {
    cout << s->size();
}

Bạn không cần phải quan tâm đến việc gọi xóa: Ptr được chia sẻ sẽ giúp bạn, nếu con trỏ cuối cùng tham chiếu đến đối tượng nằm ngoài phạm vi. Bản thân ptr được chia sẻ có thời gian lưu trữ tự động. Vì vậy, thời gian tồn tại của nó được quản lý tự động, cho phép nó kiểm tra xem nó có nên xóa đối tượng được trỏ tới động trong hàm hủy của nó hay không. Để tham khảo shared_ptr, hãy xem tài liệu nâng cao: http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/ Shared_ptr.htmlm


39

Nó được nói một cách công phu, giống như "câu trả lời ngắn":

  • biến tĩnh (lớp)
    trọn đời = thời gian chạy chương trình (1)
    khả năng hiển thị = được xác định bởi các sửa đổi truy cập (riêng tư / được bảo vệ / công khai)

  • biến tĩnh (phạm vi toàn cầu)
    life = chương trình thời gian chạy (1)
    mức độ hiển thị = đơn vị biên dịch được khởi tạo trong (2)

  • biến heap life
    = được xác định bởi bạn (mới xóa)
    tầm nhìn = được xác định bởi bạn (bất cứ điều gì bạn gán con trỏ cho)


  • khả năng hiển thị biến stack = từ khai báo cho đến khi thoát phạm vi
    trọn đời = từ khai báo cho đến khi thoát phạm vi khai báo


(1) chính xác hơn: từ khi khởi tạo cho đến khi khử hóa đơn vị biên dịch (tức là tệp C / C ++). Thứ tự khởi tạo của các đơn vị biên dịch không được xác định bởi tiêu chuẩn.

(2) Cẩn thận: nếu bạn khởi tạo một biến tĩnh trong tiêu đề, mỗi đơn vị biên dịch sẽ có bản sao riêng.


5

Tôi chắc chắn rằng một trong những giáo viên sẽ sớm đưa ra câu trả lời tốt hơn, nhưng sự khác biệt chính là tốc độ và kích thước.

Cây rơm

Kịch tính nhanh hơn để phân bổ. Nó được thực hiện trong O (1) vì nó được phân bổ khi thiết lập khung ngăn xếp nên về cơ bản là miễn phí. Hạn chế là nếu bạn hết dung lượng ngăn xếp, bạn sẽ bị rút xương. Bạn có thể điều chỉnh kích thước ngăn xếp, nhưng IIRC bạn có ~ 2MB để chơi. Ngoài ra, ngay khi bạn thoát khỏi chức năng, mọi thứ trên ngăn xếp sẽ bị xóa. Vì vậy, nó có thể có vấn đề để tham khảo nó sau. (Con trỏ để xếp các đối tượng được phân bổ dẫn đến lỗi.)

Đống

Kịch tính chậm để phân bổ. Nhưng bạn có GB để chơi và trỏ đến.

Thu gom rác

Trình thu gom rác là một số mã chạy trong nền và giải phóng bộ nhớ. Khi bạn phân bổ bộ nhớ trên heap, rất dễ quên để giải phóng nó, được gọi là rò rỉ bộ nhớ. Theo thời gian, bộ nhớ mà ứng dụng của bạn tiêu thụ sẽ tăng lên và phát triển cho đến khi nó gặp sự cố. Có một trình thu gom rác định kỳ giải phóng bộ nhớ mà bạn không còn cần giúp loại bỏ lớp lỗi này. Tất nhiên điều này có giá, vì người thu gom rác làm mọi thứ chậm lại.


3

Các vấn đề của tĩnh và ngăn xếp là gì?

Vấn đề với phân bổ "tĩnh" là phân bổ được thực hiện vào thời gian biên dịch: bạn không thể sử dụng nó để phân bổ một số lượng dữ liệu khác nhau, số lượng dữ liệu không được biết cho đến thời gian chạy.

Vấn đề với việc phân bổ trên "ngăn xếp" là việc phân bổ bị hủy ngay khi chương trình con thực hiện phân bổ trả về.

Tôi có thể viết toàn bộ ứng dụng mà không cần phân bổ biến trong heap không?

Có lẽ nhưng không phải là một ứng dụng lớn, không bình thường (nhưng được gọi là chương trình "nhúng" có thể được viết mà không có heap, sử dụng tập hợp con của C ++).

Người thu gom rác làm gì?

Nó tiếp tục xem dữ liệu của bạn ("đánh dấu và quét") để phát hiện khi ứng dụng của bạn không còn tham chiếu đến nó nữa. Điều này thuận tiện cho ứng dụng, vì ứng dụng không cần phân bổ dữ liệu ... nhưng trình thu gom rác có thể tốn kém về mặt tính toán.

Trình thu gom rác không phải là một tính năng thông thường của lập trình C ++.

Bạn có thể tự mình điều khiển bộ nhớ mà bạn không thể sử dụng trình thu gom rác này?

Tìm hiểu các cơ chế C ++ để giải quyết bộ nhớ xác định:

  • 'tĩnh': không bao giờ bị hủy
  • 'stack': ngay khi biến "đi ra khỏi phạm vi"
  • 'heap': khi con trỏ bị xóa (bị xóa rõ ràng bởi ứng dụng hoặc bị xóa hoàn toàn trong chương trình con một hoặc khác)

1

Phân bổ bộ nhớ ngăn xếp (biến chức năng, biến cục bộ) có thể gặp vấn đề khi ngăn xếp của bạn quá "sâu" và bạn tràn bộ nhớ có sẵn để phân bổ ngăn xếp. Heap dành cho các đối tượng cần được truy cập từ nhiều luồng hoặc trong suốt vòng đời của chương trình. Bạn có thể viết toàn bộ chương trình mà không cần sử dụng heap.

Bạn có thể rò rỉ bộ nhớ khá dễ dàng mà không cần bộ thu gom rác, nhưng bạn cũng có thể ra lệnh khi các đối tượng và bộ nhớ được giải phóng. Tôi đã gặp phải các vấn đề với Java khi nó chạy GC và tôi có một quy trình thời gian thực, bởi vì GC là một luồng độc quyền (không có gì khác có thể chạy). Vì vậy, nếu hiệu suất là rất quan trọng và bạn có thể đảm bảo không có đối tượng bị rò rỉ, thì việc không sử dụng GC là rất hữu ích. Nếu không, nó chỉ khiến bạn ghét cuộc sống khi ứng dụng của bạn tiêu thụ bộ nhớ và bạn phải theo dõi nguồn rò rỉ.


1

Điều gì sẽ xảy ra nếu chương trình của bạn không biết trả trước bao nhiêu bộ nhớ để phân bổ (do đó bạn không thể sử dụng các biến ngăn xếp). Nói các danh sách được liên kết, các danh sách có thể phát triển mà không cần biết trước kích thước của nó là gì. Vì vậy, phân bổ trên một đống có ý nghĩa cho một danh sách được liên kết khi bạn không biết có bao nhiêu phần tử sẽ được chèn vào nó.


0

Một lợi thế của GC trong một số tình huống là sự khó chịu ở những người khác; sự phụ thuộc vào GC khuyến khích không nghĩ nhiều về nó. Về lý thuyết, đợi cho đến khoảng thời gian 'nhàn rỗi' hoặc cho đến khi hoàn toàn phải, khi nó sẽ đánh cắp băng thông và gây ra độ trễ phản hồi trong ứng dụng của bạn.

Nhưng bạn không cần phải 'không nghĩ về điều đó'. Giống như mọi thứ khác trong các ứng dụng đa luồng, khi bạn có thể mang lại, bạn có thể mang lại. Vì vậy, ví dụ, trong .Net, có thể yêu cầu một GC; bằng cách này, thay vì chạy GC ít thường xuyên hơn, bạn có thể có GC chạy ngắn hơn thường xuyên hơn và phân bổ độ trễ liên quan đến chi phí này.

Nhưng điều này đánh bại sự hấp dẫn chính của GC dường như "được khuyến khích không phải suy nghĩ nhiều về nó bởi vì nó là auto-mat-ic."

Nếu bạn lần đầu tiên tiếp xúc với lập trình trước khi GC trở nên phổ biến và cảm thấy thoải mái với malloc / miễn phí và mới / xóa, thì đó có thể là trường hợp bạn thấy GC hơi khó chịu và / hoặc không tin tưởng (vì người ta có thể không tin tưởng ' tối ưu hóa, 'đã có lịch sử rô.) Nhiều ứng dụng chấp nhận độ trễ ngẫu nhiên. Nhưng đối với các ứng dụng không có độ trễ ngẫu nhiên ít được chấp nhận, một phản ứng phổ biến là tránh môi trường GC và di chuyển theo hướng mã hoàn toàn không được quản lý (hoặc thần cấm, một nghệ thuật lắp ráp lâu đời, ngôn ngữ lắp ráp.)

Tôi đã có một sinh viên mùa hè ở đây một thời gian trước, một đứa trẻ thực tập, thông minh, được cai sữa trên GC; anh ấy rất kiên định về siêu năng lực của GC đến nỗi ngay cả khi lập trình trong C / C ++ không được quản lý, anh ấy đã từ chối theo mô hình malloc / free new / xóa bởi vì, trích dẫn, "bạn không cần phải làm điều này bằng ngôn ngữ lập trình hiện đại." Và bạn biết không? Đối với các ứng dụng nhỏ, chạy ngắn, bạn thực sự có thể thoát khỏi điều đó, nhưng không phải cho các ứng dụng hoạt động lâu dài.


0

Stack là bộ nhớ được cấp bởi trình biên dịch, khi chúng ta biên dịch chương trình, mặc định trình biên dịch sẽ cấp phát một số bộ nhớ từ HĐH (chúng ta có thể thay đổi cài đặt từ cài đặt trình biên dịch trong IDE của bạn) và OS là bộ nhớ cung cấp cho bạn bộ nhớ, tùy thuộc vào trên nhiều bộ nhớ khả dụng trên hệ thống và nhiều thứ khác, và sắp xếp bộ nhớ được phân bổ khi chúng ta khai báo một biến chúng sao chép (ref dưới dạng chính thức) các biến đó được đẩy lên để xếp chúng theo một số quy ước đặt tên theo mặc định CDECL trong Visual studio ví dụ: ký hiệu infix: c = a + b; việc đẩy stack được thực hiện từ phải sang trái PUSHING, b để stack, toán tử, a để stack và kết quả của các i, ec để stack. Trong ký hiệu sửa lỗi trước: = + cab Ở đây tất cả các biến được đẩy lên ngăn thứ 1 (phải sang trái) và sau đó thao tác được thực hiện. Bộ nhớ này được phân bổ bởi trình biên dịch là cố định. Vì vậy, giả sử 1 MB bộ nhớ được phân bổ cho ứng dụng của chúng tôi, giả sử các biến được sử dụng 700kb bộ nhớ (tất cả các biến cục bộ được đẩy lên ngăn xếp trừ khi chúng được phân bổ động) để bộ nhớ còn lại 324kb được phân bổ cho heap. Và ngăn xếp này có thời gian sống ít hơn, khi phạm vi của hàm kết thúc, các ngăn xếp này sẽ bị xóa.

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.