Làm thế nào để lưu trữ const làm việc? (Mục 2, Scott Myers hiệu quả C ++)


8

Trong Mục 2 trên trang 16, (Thích các hằng số, enum và nội tuyến cho #defines), Scott nói:

Ngoài ra, mặc dù các trình biên dịch tốt sẽ không dành lưu trữ cho các đối tượng const của các kiểu số nguyên ...

Tôi không hiểu điều này. Nếu tôi xác định một đối tượng const, vd

const int myval = 5;

Sau đó, chắc chắn trình biên dịch phải dành một số bộ nhớ (có kích thước int) để lưu trữ giá trị 5?

Hoặc là dữ liệu const được lưu trữ theo một cách đặc biệt?

Đây là một câu hỏi về lưu trữ máy tính tôi cho rằng. Về cơ bản, làm thế nào để lưu trữ máy tính const các đối tượng để không lưu trữ được đặt sang một bên?


3
Bạn nên cung cấp một tiêu đề rõ ràng. ví dụ storage of const objectnhư nguồn câu hỏi của bạn có ít giá trị.
Simon Bergot

Câu trả lời:


7

Ngoài ra, mặc dù các trình biên dịch tốt sẽ không dành lưu trữ cho các đối tượng const của các kiểu số nguyên ...

Một tuyên bố chính xác hơn một chút là các trình biên dịch sẽ không đặt bộ nhớ dữ liệu cho các đối tượng const của kiểu số nguyên: họ sẽ trao đổi nó cho bộ nhớ chương trình . Không có sự khác biệt giữa hai kiến ​​trúc Von Neumann, nhưng trong các kiến ​​trúc khác, chẳng hạn như Harvard, sự khác biệt là khá quan trọng.

Để hiểu đầy đủ những gì đang diễn ra, bạn cần nhớ lại cách ngôn ngữ lắp ráp tải dữ liệu để xử lý. Có hai cách cơ bản để tải dữ liệu - đọc bộ nhớ từ một vị trí cụ thể (được gọi là chế độ địa chỉ trực tiếp ) hoặc đặt hằng số được chỉ định làm một phần của chính lệnh (được gọi là chế độ địa chỉ ngay lập tức ). Khi trình biên dịch thấy một const int x = 5khai báo theo sau int a = x+x, nó có hai tùy chọn:

  • Đặt 5 vào bộ nhớ dữ liệu, như thể đó là một biến và tạo các lệnh tải trực tiếp; coi việc ghi vào x là lỗi
  • Mỗi lần xđược tham chiếu, tạo một lệnh tải ngay lập tức của giá trị 5

Trong trường hợp đầu tiên, bạn sẽ thấy một lần đọc từ xvào thanh ghi tích lũy , thêm giá trị tại vị trí của bộ xtích lũy và lưu trữ vào vị trí của a. Trong trường hợp thứ hai, bạn sẽ thấy một tải ngay lập tức là năm, thêm ngay lập tức năm, sau đó là một cửa hàng vào vị trí của a. Một số trình biên dịch có thể hiểu rằng bạn đang thêm một hằng số cho chính nó, tối ưu hóa a = x+xvào a = 10, và tạo ra một chỉ dẫn duy nhất mà các cửa hàng mười tại vị trí của a.


1
+1 để đề cập đến việc gấp liên tục là tốt
jk.

10

Không cần thiết. Nó cũng có thể quyết định chỉ sử dụng giá trị thô 5 thay vì myvaltrong mã được biên dịch.

Sự khác biệt giữa #define MYVAL 5const int myval = 5trong trường hợp trước, trình biên dịch không có lựa chọn nào, vì bộ tiền xử lý đã thay thế tất cả các đề cập MYVALtrong mã nguồn 5theo thời gian trình biên dịch sẽ thấy mã nguồn. Trong trường hợp sau, mặc dù, có một sự lựa chọn. Đối với bản dựng gỡ lỗi không được tối ưu hóa, trình biên dịch có thể phân bổ rõ ràng a const int, vì vậy, trong trình gỡ lỗi, bạn sẽ có thể thấy hằng số myval, thay vì chỉ có giá trị thô 5.


Tôi hiểu lợi ích của const hơn là xác định. Nhưng ngay cả với ví dụ const, giá trị 5 phải được lưu trữ trong tệp thực thi ở đâu đó?
dùng619818

3
@ user619818, nó sẽ được lưu trữ trong mã dưới dạng tham số lệnh, không phải trong phân đoạn dữ liệu dưới dạng biến / hằng thông thường.
Péter Török

@ user619818: Hướng dẫn sẽ luôn có một tham số. Vì vậy, nếu lưu trữ riêng biệt được phân bổ, có giá trị địa chỉ của nó trong đối số ngay lập tức của hướng dẫn. Trên các CPU có kích thước lệnh cố định (như ARM), nó thậm chí còn tệ hơn, bởi vì các hằng số nhỏ như 5 phù hợp với chính lệnh 32 bit, nhưng địa chỉ thường không và khiến cho lệnh địa chỉ tải thêm được phát ra.
Jan Hudec

3

Những gì trích dẫn nói không hoàn toàn chính xác.

Một trình biên dịch tốt sẽ không dành lưu trữ cho các biến const tĩnh . Nếu biến const không tĩnh và nằm trong phạm vi tệp, nó phải đặt lưu trữ sang một bên vì biến có thể được tham chiếu từ một đơn vị biên dịch khác. Với tối ưu hóa thời gian liên kết, trình liên kết thể loại bỏ các hướng dẫn lưu trữ và viết lại tham chiếu biến nếu điều đó có thể chứng minh rằng chương trình không tạo ra một con trỏ tới biến đó.

Một lý do tốt hơn để sử dụng const intthay vì các #definetrình gỡ lỗi không "nhìn thấy" macro, vì vậy bạn không thể kiểm tra #definedgiá trị của trình gỡ lỗi.


3

Tôi sẽ ăn cắp câu đầu tiên từ câu trả lời của Péter Török nhưng giải thích khác đi: Không nhất thiết. Nó cũng có thể quyết định chỉ sử dụng giá trị thô 5 thay vì myvaltrong mã được biên dịch.

Đối xử myvalnhư một biến thông thường bằng cách phân bổ không gian cho nó trong bộ nhớ có thể có ý nghĩa về hiệu năng từ phạm vi rất nhỏ đến nghiêm trọng tùy thuộc vào kiến ​​trúc và cách nó xử lý bộ nhớ.

Làm việc theo cách đó, một trình biên dịch sẽ phát ra một lệnh chỉ ra một cái gì đó dọc theo dòng "tải thanh ghi R với bất cứ thứ gì ở vị trí bộ nhớ cho myval". Vị trí củamyvalnhư một toán hạng của lệnh, vì vậy nó đi ra khỏi cùng một khối dữ liệu như chính lệnh đó. Trên các CPU hiện đại, giá trị này sẽ có sẵn trên chip do tìm nạp trước lệnh. Với địa chỉ trong tay, CPU vẫn phải lấy giá trị ra khỏi bộ nhớ. Điều đó có thể diễn ra nhanh chóng nếu vị trí ở gần trong bộ đệm hoặc không quá nhanh nếu không. CPU không chỉ phải ra khỏi chip để có được giá trị, làm như vậy có thể buộc nó phải vượt qua các dữ liệu hữu ích khác từ bộ đệm sẽ được đưa trở lại sau. Khi chương trình đang chạy trong HĐH ảo hóa bộ nhớ, việc truy cập vào vị trí đó có thể gây ra lỗi trang, dẫn đến chương trình được đưa vào chế độ ngủ cho đến khi trang yêu cầu được đưa vào RAM thông qua ngoại vi (ví dụ: đĩa) I / O,

Bằng cách nối cứng giá trị không đổi vào mã đối tượng, trình biên dịch sẽ phát ra một lệnh như "tải thanh ghi R với giá trị 5". Giống như địa chỉ bộ nhớ được mô tả ở trên, 5sẽ là một toán hạng cho hướng dẫn và có sẵn theo cùng một cách (nghĩa là được tìm nạp trước). Đó là nơi tương tự kết thúc, bởi vì CPU hiện có mọi thứ cần thiết để 5đăng ký R và bắt tay vào công việc. Vì các địa chỉ và các thanh ghi thường có cùng kích thước, không có sự khác biệt về số byte mà lệnh chiếm giữ và việc thực thi thực tế diễn ra với khả năng không có lỗi bộ nhớ cache và lỗi trang có thể xảy ra khi bạn bỏ đi thứ gì đó trong bộ nhớ.

Trình biên dịch có thể, như Péter đã chỉ ra, phân bổ không gian và một biểu tượng cho myvalcác bản dựng gỡ lỗi. Sẽ không có bất kỳ tác hại nào khi làm điều này và vẫn cứng cáp giá trị của nó, vì giá trị vẫn giữ nguyên cho dù điều gì và biểu tượng thực sự chỉ có ở đó để con người chúng ta sử dụng để gỡ lỗi.

Lưu ý rằng điều này chỉ áp dụng cho các giá trị có thể được giữ trong các thanh ghi, bởi vì các thanh ghi là số nguyên theo bản chất. Các hằng số khác sẽ kết thúc trong bộ nhớ.


1

Trình biên dịch sẽ thay thế số năm bất cứ nơi nào sử dụng biến 'myval'.


0

Trình biên dịch có thể coi các giá trị const là toán hạng ngay lập tức. Toán hạng ngay lập tức không yêu cầu lưu trữ dữ liệu. Trình biên dịch có thể xử lý:

int foo = myVal;

giống như

int foo = 5;

Giá trị 5 không được lưu trong bộ nhớ dữ liệu, mà là một phần của chuỗi lệnh.

Trình biên dịch phải dự trữ lưu trữ dữ liệu trong trường hợp có thể lấy địa chỉ của giá trị. Ngay cả trong trường hợp đó, trình biên dịch vẫn sẽ sử dụng các hoạt động ngay lập tức khi giá trị của myVal được sử dụng.

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.