2008-10-23 86 views
12

Các biến trong C++ được lưu ở đâu?Các biến trong C++ được lưu ở đâu?

Bên trong RAM hoặc bộ nhớ cache của bộ xử lý?

+0

Câu hỏi này làm cho không có ý nghĩa, vì (hầu hết) cache là minh bạch và thực sự chỉ một phần của hệ thống bộ nhớ. Nó cũng thiếu sót bởi vì nó thực sự phụ thuộc vào kiến ​​trúc và trình biên dịch nơi các biến C++ (hoặc bất kỳ ngôn ngữ biên dịch nào) được lưu trữ. – tgamblin

+0

Ngoài ra, tiêu đề câu hỏi có thể được cải thiện đáng kể – jonner

+1

@Tal, như những người khác đã nói, như đã nêu câu hỏi là loại mơ hồ. Có thể bạn muốn xem xét nhận xét của mọi người và xem liệu bạn có thể đặt câu hỏi cụ thể hơn một chút hay không. –

Trả lời

1

Biến có thể được tổ chức ở một số địa điểm khác nhau, đôi khi ở nhiều nơi. Hầu hết các biến được đặt trong RAM khi chương trình được tải; đôi khi các biến được khai báo là const được đặt trong ROM. Bất cứ khi nào một biến được truy cập, nếu nó không nằm trong bộ nhớ cache của bộ xử lý, một lỗi bộ nhớ cache sẽ gây ra, và bộ xử lý sẽ dừng khi biến được sao chép từ RAM/ROM vào bộ đệm.

Nếu bạn có bất kỳ trình biên dịch tối ưu hóa nửa chừng nào, các biến cục bộ thường sẽ được lưu trữ trong tệp đăng ký của bộ xử lý. Các biến sẽ di chuyển qua lại giữa RAM, bộ nhớ đệm và tệp sổ ghi khi chúng được đọc và ghi, nhưng chúng thường sẽ có bản sao trong RAM/ROM, trừ khi trình biên dịch quyết định điều đó là không cần thiết.

+0

Trình biên dịch cho các kiến ​​trúc thông thường, không được nhúng không đặt các biến trong "ROM". –

+0

ROM thông thường có nghĩa là bộ nhớ chỉ được ghi trong quá trình sản xuất - các biến const vẫn được lưu trữ trong RAM của máy tính, nhưng không được ghi vào chương trình thực thi –

+2

Stroustrup thường nói về các biến được lưu trữ trong ROM. Cũng như ủy ban tiêu chuẩn C++ (http://www.open-std.org/jtc1/sc22/wg21/docs/PDTR18015.pdf trang 75). Trong thực tế, nó không phải là ROM vật lý, mà thay vào đó là một phần của tệp thực thi cho dữ liệu (trong ELF nó là phần .text). –

6

Các biến trong C++ được lưu trữ trên ngăn xếp hoặc đống.

stack:

int x; 

đống:

int *p = new int; 

Nói như vậy, cả hai đều là công trình xây dựng trong bộ nhớ RAM.

Nếu mức sử dụng RAM của bạn cao mặc dù cửa sổ có thể hoán đổi ổ đĩa này ra đĩa.

Khi tính toán được thực hiện trên các biến, bộ nhớ sẽ được sao chép vào sổ đăng ký.

0

tùy thuộc vào cách thức chúng được tuyên bố, họ sẽ hoặc được lưu trữ trong "heap" hay "stack"

Heap là một cấu trúc dynamic dữ liệu mà ứng dụng có thể sử dụng.

Khi ứng dụng sử dụng dữ liệu, nó phải được chuyển đến sổ đăng ký của CPU ngay trước khi chúng được tiêu thụ, tuy nhiên điều này rất dễ bay hơi và lưu trữ tạm thời.

34

biến được lưu trữ:

  • trên stack, nếu họ đang auto chức năng địa phương -matic biến
  • trên heap, nếu họ đang giao new hoặc malloc, vv (chi tiết về những gì nó có nghĩa là để nói "một biến được lưu trữ trong các đống" trong các ý kiến)
  • trong một khu vực dữ liệu cho mỗi quá trình nếu họ là toàn cầu hoặc static

Đây là tất cả trong RAM, tất nhiên. Caching là minh bạch cho các quá trình không gian người dùng, mặc dù nó có thể ảnh hưởng đến hiệu năng.

Trình biên dịch có thể tối ưu hóa mã để lưu các biến trong thanh ghi. Điều này là cao trình biên dịch và mã phụ thuộc, nhưng trình biên dịch tốt sẽ làm như vậy tích cực.

+6

Thực ra, các biến không được lưu trữ trong heap. Bạn có thể có một biến trỏ đến một cái gì đó trong heap, nhưng biến tự nó sẽ có trong một thanh ghi, trên một chồng, hoặc được phân bổ tĩnh. –

+0

Kristopher, một điểm hợp lệ. Trong định nghĩa C++, biến là con trỏ, không phải mảng được trỏ tới, vì vậy bạn đã đúng. –

+0

Được thăng hạng nhưng bạn nên xóa điểm 2. – Joe

8

Các biến thường được lưu trữ trong RAM. Điều này hoặc là trên heap (ví dụ:tất cả các biến toàn cầu thường sẽ đến đó) hoặc trên ngăn xếp (tất cả các biến được khai báo trong một phương thức/hàm thường đến đó). Stack và Heap đều là RAM, chỉ là các vị trí khác nhau. Con trỏ có các quy tắc khác nhau. Con trỏ tới một thứ gì đó (khối bộ nhớ, đối tượng, v.v.) thường tuân theo các quy tắc ở trên (một con trỏ được khai báo bên trong một hàm được lưu trữ trên ngăn xếp), nhưng dữ liệu trỏ tới (chính khối bộ nhớ hoặc đối tượng) bạn đã tạo bằng mới) được lưu trữ trên heap. Bạn có thể tạo con trỏ trỏ tới ngăn xếp (ví dụ: "int a = 10; int * b = & a;", b trỏ đến a và a được lưu trữ trên ngăn xếp), nhưng cấp phát bộ nhớ bằng cách sử dụng malloc hoặc đếm mới đối với bộ nhớ heap.

Điều gì xảy ra trong bộ nhớ cache CPU vượt quá khả năng kiểm soát trình biên dịch, CPU tự quyết định bộ nhớ cache và cách lưu trữ bộ nhớ cache (tùy thuộc vào các yếu tố như "Dữ liệu này đã được sử dụng gần đây?" Hoặc " dữ liệu được sử dụng khá sớm một lần nữa? ”và dĩ nhiên kích thước của cache có ảnh hưởng lớn đến việc CPU sẽ giữ dữ liệu trong bao lâu - bộ nhớ cache của chúng càng nhiều thì càng có nhiều dữ liệu và bộ nhớ cache càng dài trước khi giải phóng dung lượng bộ nhớ cache cho dữ liệu mới).

Trình biên dịch chỉ có thể quyết định liệu dữ liệu có được đăng ký vào CPU hay không. Thông thường dữ liệu được giữ ở đó nếu nó được truy cập rất thường xuyên trong một hàng (vì truy cập đăng ký nhanh hơn bộ đệm và nhanh hơn nhiều so với RAM). Một số thao tác trên một số hệ thống có thể thực sự chỉ được thực hiện nếu dữ liệu nằm trong thanh ghi - tuy nhiên trình biên dịch sẽ quyết định sao chép dữ liệu vào RAM ngay sau khi thực hiện thao tác trên đó hay giữ nó ở đó để thực hiện nhiều thao tác hơn. trước khi ghi nó trở lại RAM. Nó sẽ luôn luôn cố gắng giữ dữ liệu thường xuyên truy cập nhất trong một thanh ghi nếu có thể và nếu nó hết sổ đăng ký (tùy thuộc vào số lượng đăng ký CPU của bạn), nó sẽ quyết định xem có nên ghi dữ liệu trở lại RAM hay không. nó từ đó khi cần thiết một lần nữa), hoặc chỉ tạm thời trao đổi dữ liệu lên stack và sau đó lấy lại dữ liệu từ đó (mặc dù stack là RAM, thường thì việc sử dụng stack nhanh hơn vì các CPU thường có bộ đệm ẩn, vì vậy việc đẩy và popping từ stack có thể thực sự chỉ ghi vào bộ nhớ cache và đọc lại từ đó, dữ liệu có thể không bao giờ đến bộ nhớ). Tuy nhiên, khi dòng mã nhảy từ một phương thức/hàm này sang phương thức khác, thường thì tất cả các thanh ghi được ghi lại vào bộ nhớ, vì trình biên dịch khó có thể nói chắc chắn rằng hàm/phương thức được gọi sẽ không truy cập vào bộ nhớ nơi dữ liệu đăng ký đến và khi nào không ghi dữ liệu trở lại, chức năng có thể thấy một giá trị cũ vẫn còn hiện diện trong bộ nhớ, vì giá trị mới chỉ nằm trong sổ đăng ký và chưa được viết lại.

15

Đối với C++ nói chung, câu trả lời đúng là "bất cứ nơi nào trình biên dịch của bạn quyết định đặt chúng". Bạn không nên giả định theo cách khác, trừ khi bạn bằng cách nào đó hướng trình biên dịch của bạn theo cách khác. Một số biến có thể được lưu trữ hoàn toàn trong sổ đăng ký và một số biến có thể được tối ưu hóa hoàn toàn và được thay thế bằng chữ ở đâu đó. Với một số trình biên dịch trên một số nền tảng, hằng số có thể thực sự kết thúc trong ROM.

Phần câu hỏi của bạn về "bộ nhớ cache của bộ xử lý" hơi bị nhầm lẫn. Có một số công cụ để chỉ thị cách bộ vi xử lý xử lý bộ nhớ cache của nó, nhưng nói chung đó là hoạt động kinh doanh của bộ vi xử lý và phải ẩn với bạn. Bạn có thể nghĩ về cache như cửa sổ CPU của bạn vào RAM. Khá nhiều sự truy cập bộ nhớ bất kỳ nào đi qua bộ nhớ cache.

Ở đầu kia của phương trình, RAM không sử dụng đôi khi sẽ được đổi chỗ ra đĩa trên hầu hết các hệ điều hành. Vì vậy, nó có thể (nhưng không chắc) rằng tại một số thời điểm các biến của bạn đang thực sự được lưu trữ trên đĩa. :-)

+0

Tôi hiểu rằng trình biên dịch có thể quyết định làm bất cứ điều gì nó muốn. Có trình biên dịch hiện đang làm điều gì đó rất khác so với thông thường (tự động = ngăn xếp hoặc sổ đăng ký, được phân bổ = trợ giúp v.v.) không? – user231536

3

Tôi nghĩ bạn đang trộn lẫn hai khái niệm. Một, ngôn ngữ C++ lưu trữ các biến trong bộ nhớ như thế nào. Hai, làm thế nào để máy tính và hệ điều hành quản lý bộ nhớ đó.

Trong C++, các biến có thể được cấp phát trên ngăn xếp, bộ nhớ được dành riêng cho việc sử dụng chương trình và được cố định kích thước khi bắt đầu luồng hoặc bộ nhớ động có thể được cấp phát khi đang sử dụng mới.Một trình biên dịch cũng có thể chọn lưu trữ các biến trên thanh ghi trong bộ xử lý nếu phân tích mã sẽ cho phép nó. Các biến đó sẽ không bao giờ thấy bộ nhớ hệ thống.

Nếu biến kết thúc trong bộ nhớ, hệ điều hành và bộ vi xử lý tiếp quản. Cả hai địa chỉ dựa trên stack và địa chỉ động đều là ảo. Điều đó có nghĩa là họ có thể hoặc không thể cư trú trong bộ nhớ hệ thống tại bất kỳ thời điểm nào. Biến bộ nhớ trong bộ nhớ có thể được lưu trữ trong bộ nhớ hệ thống, được phân trang lên đĩa hoặc có thể nằm trong bộ nhớ cache trên hoặc gần bộ xử lý. Vì vậy, thật khó để biết dữ liệu đó thực sự đang sống ở đâu. Nếu một chương trình không được nhàn rỗi trong một thời gian hoặc hai chương trình đang cạnh tranh cho tài nguyên bộ nhớ, giá trị có thể được lưu vào đĩa trong tệp trang và được khôi phục khi chương trình chuyển sang chạy. Nếu biến là cục bộ đối với một số công việc đang được thực hiện, nó có thể được sửa đổi trong bộ nhớ cache của bộ xử lý nhiều lần trước khi nó được đưa trở lại bộ nhớ hệ thống. Mã bạn viết sẽ không bao giờ biết điều này xảy ra. Tất cả nó biết là nó có một địa chỉ để hoạt động trên và tất cả các hệ thống khác chăm sóc phần còn lại.

+0

Trong hầu hết các hệ thống hiện đại, ngăn xếp không được cố định kích thước, nhưng được tự động mở rộng bởi hệ điều hành khi xảy ra lỗi trang (vì một ngăn xếp trống). –

+0

Trong câu trả lời của bạn, nó trở nên rất rõ ràng rằng hai điều khác nhau đang xảy ra: "mô hình đối tượng" của ngôn ngữ và hệ thống RAM/SwapFile/Caching. Câu trả lời hay! – xtofl

+0

Xin chào Paul. Cảm ơn bạn đã bình luận. Bạn nói đúng rằng ngăn xếp là bộ nhớ ảo và có thể được phân trang. Quan điểm của tôi là nó đã được cố định kích thước khi được phân bổ khi bắt đầu luồng. Điều này bị chi phối bởi mối liên kết. – Todd

5

C++ không biết bộ nhớ cache của bộ xử lý của bạn.

Khi bạn đang chạy một chương trình, được viết bằng ngôn ngữ C++ hoặc bất kỳ ngôn ngữ nào khác, CPU của bạn sẽ giữ một bản sao của các khối RAM "phổ biến" trong bộ nhớ cache. Điều đó được thực hiện ở cấp độ phần cứng.

Đừng nghĩ về bộ nhớ cache CPU là bộ nhớ "khác" hoặc "nhiều hơn" ... nó chỉ là một cơ chế để giữ một số phần RAM gần kề.

1

ngôn ngữ C++ hỗ trợ hai loại phân bổ bộ nhớ thông qua các biến trong chương trình C++:

phân bổ tĩnh là những gì sẽ xảy ra khi bạn khai báo một biến tĩnh hoặc toàn cầu. Mỗi biến tĩnh hoặc toàn cục xác định một khối không gian, có kích thước cố định. Không gian được cấp phát một lần, khi chương trình của bạn được bắt đầu (một phần của hoạt động exec), và không bao giờ được giải phóng. Phân bổ tự động xảy ra khi bạn khai báo một biến tự động, chẳng hạn như đối số hàm hoặc biến cục bộ. Không gian cho một biến tự động được phân bổ khi câu lệnh ghép chứa khai báo được nhập vào và được giải phóng khi câu lệnh ghép đó được thoát ra. Kích thước của bộ nhớ tự động có thể là một biểu thức khác nhau. Trong các triển khai CPP khác, nó phải là một hằng số. Loại phân bổ bộ nhớ quan trọng thứ ba, phân bổ động, không được hỗ trợ bởi biến C++ nhưng có sẵn chức năng Thư viện. Phân bổ bộ nhớ động

Phân bổ bộ nhớ động là kỹ thuật trong đó chương trình xác định khi chúng đang chạy nơi lưu trữ một số thông tin. Bạn cần phân bổ động khi lượng bộ nhớ bạn cần hoặc thời gian bạn tiếp tục cần đến, phụ thuộc vào các yếu tố không được biết trước khi chương trình chạy.

Ví dụ: bạn có thể cần một khối để lưu trữ một dòng được đọc từ tệp đầu vào; vì không có giới hạn về độ dài của một dòng, bạn phải phân bổ bộ nhớ động và làm cho nó tự động lớn hơn khi bạn đọc thêm dòng.

Hoặc, bạn có thể cần một khối cho mỗi bản ghi hoặc mỗi định nghĩa trong dữ liệu đầu vào; vì bạn không thể biết trước số lượng sẽ có, bạn phải phân bổ một khối mới cho mỗi bản ghi hoặc định nghĩa khi bạn đọc nó.

Khi bạn sử dụng phân bổ động, việc cấp phát một khối bộ nhớ là một hành động mà chương trình yêu cầu một cách rõ ràng. Bạn gọi hàm hoặc macro khi bạn muốn phân bổ không gian và chỉ định kích thước bằng đối số. Nếu bạn muốn giải phóng không gian, bạn làm như vậy bằng cách gọi một hàm hoặc macro khác. Bạn có thể làm những việc này bất cứ khi nào bạn muốn, thường xuyên như bạn muốn.

Phân bổ động không được hỗ trợ bởi biến CPP; không có lớp lưu trữ “động” và không bao giờ có thể là biến CPP có giá trị được lưu trữ trong không gian được phân bổ động. Cách duy nhất để có được bộ nhớ được cấp phát động là thông qua một cuộc gọi hệ thống, và cách duy nhất để tham chiếu đến không gian được cấp phát động là thông qua một con trỏ. Bởi vì nó ít thuận tiện hơn, và bởi vì quá trình phân bổ động thực tế đòi hỏi nhiều thời gian tính toán hơn, nên các lập trình viên thường chỉ sử dụng phân bổ động khi không phân bổ tĩnh và tự động.

Ví dụ: nếu bạn muốn phân bổ động một số không gian để giữ cấu trúc foobar, bạn không thể khai báo biến kiểu struct foobar có nội dung là không gian được phân bổ động. Nhưng bạn có thể khai báo một biến kiểu con trỏ struct foobar * và gán cho nó địa chỉ của không gian. Sau đó, bạn có thể sử dụng các toán tử '*' và '->' vào biến con trỏ này để tham khảo các nội dung của không gian:

{ 
    struct foobar *ptr 
     = (struct foobar *) malloc (sizeof (struct foobar)); 
    ptr->name = x; 
    ptr->next = current_foobar; 
    current_foobar = ptr; 
} 
Các vấn đề liên quan