2011-01-04 53 views
61

Nếu chúng ta khai báo một biến như volatile mỗi khi giá trị tươi được cập nhật
Nếu chúng ta khai báo một biến như const thì giá trị của biến đó sẽ không được thay đổiSự khác nhau giữa const & const dễ bay hơi

Sau đó const volatile int temp;
Việc sử dụng khai báo biến số temp như trên là gì?
Điều gì xảy ra nếu chúng tôi khai báo là const int temp?

+0

Bạn sẽ không sử dụng 'const dễ bay hơi int temp; 'tại phạm vi khối (tức là bên trong' {} '), nó không sử dụng ở đó. –

Trả lời

21

Không phải vì biến là const mà nó có thể không thay đổi giữa hai điểm chuỗi.

Constness là lời hứa bạn không thay đổi giá trị, không phải là giá trị sẽ không bị thay đổi.

+5

Cộng với một để chỉ ra rằng dữ liệu 'const' không phải là" hằng số ". –

24
  • sẽ yêu cầu trình biên dịch không tối ưu hóa mã liên quan đến biến, thường khi chúng tôi biết nó có thể được thay đổi từ "bên ngoài", ví dụ: bởi một chủ đề khác.
  • const sẽ thông báo cho trình biên dịch rằng nó bị cấm cho chương trình sửa đổi giá trị của biến.
  • const volatile là một điều rất đặc biệt bạn có thể sẽ thấy được sử dụng chính xác 0 lần trong cuộc sống của bạn (tm). Như được mong đợi, nó có nghĩa là chương trình không thể sửa đổi giá trị của biến, nhưng giá trị có thể được sửa đổi từ bên ngoài, do đó không có tối ưu hóa nào được thực hiện trên biến.
+8

Tôi đã nghĩ rằng các biến 'biến động 'thường là những gì xảy ra khi bạn bắt đầu rối tung với phần cứng, không phải với các chủ đề khác. Nơi tôi đã nhìn thấy 'const volatile' được sử dụng là trong những thứ như đăng ký trạng thái ánh xạ bộ nhớ hoặc tương tự. –

+2

Tất nhiên, bạn hoàn toàn đúng, đa luồng chỉ là một ví dụ, nhưng không phải là một ví dụ duy nhất :). – mingos

+13

Nếu bạn làm việc với các hệ thống nhúng, bạn sẽ thấy điều này rất thường xuyên. –

95

Một đối tượng được đánh dấu là const volatile sẽ không được phép thay đổi bởi mã (lỗi sẽ được tăng lên do vòng loại const) - ít nhất thông qua tên/con trỏ cụ thể đó.

Phần volatile của vòng loại có nghĩa là trình biên dịch không thể tối ưu hóa hoặc sắp xếp lại quyền truy cập vào đối tượng.

Trong hệ thống nhúng, điều này thường được sử dụng để truy cập sổ đăng ký phần cứng có thể đọc và được phần cứng cập nhật, nhưng không có ý nghĩa gì khi ghi vào (hoặc có thể là lỗi để ghi vào).

Ví dụ có thể là thanh ghi trạng thái cho cổng nối tiếp. Các bit khác nhau sẽ chỉ ra nếu một ký tự đang chờ đọc hoặc nếu thanh ghi truyền sẵn sàng chấp nhận một ký tự mới (ví dụ, - nó trống). Mỗi lần đọc thanh ghi trạng thái này có thể dẫn đến một giá trị khác nhau tùy thuộc vào những gì khác đã xảy ra trong phần cứng cổng nối tiếp. Nó không có ý nghĩa gì khi ghi vào thanh ghi trạng thái (tùy thuộc vào thông số phần cứng cụ thể), nhưng bạn cần đảm bảo rằng mỗi lần đọc kết quả đăng ký đọc một phần cứng thực tế - sử dụng giá trị được lưu trong bộ nhớ cache từ đọc trước sẽ không cho bạn biết về những thay đổi trong trạng thái phần cứng.

Một ví dụ nhanh:

unsigned int const volatile *status_reg; // assume these are assigned to point to the 
unsigned char const volatile *recv_reg; // correct hardware addresses 


#define UART_CHAR_READY 0x00000001 

int get_next_char() 
{ 
    while ((*status_reg & UART_CHAR_READY) == 0) { 
     // do nothing but spin 
    } 

    return *recv_reg; 
} 

Nếu những gợi ý không được đánh dấu như là volatile, một vài vấn đề có thể xảy ra:

  • trong khi kiểm tra vòng lặp có thể đọc thanh ghi trạng chỉ một lần, kể từ trình biên dịch có thể giả định rằng bất cứ điều gì nó chỉ đến sẽ không bao giờ thay đổi (không có gì trong vòng lặp kiểm tra trong khi hoặc vòng lặp chính nó có thể thay đổi nó).Nếu bạn đã nhập hàm khi không có ký tự nào đang chờ trong phần cứng UART, bạn có thể kết thúc bằng một vòng lặp vô hạn không bao giờ dừng lại ngay cả khi nhận được một ký tự.
  • đọc của thanh ghi nhận có thể được di chuyển bởi trình biên dịch trước vòng lặp while - một lần nữa vì không có gì trong hàm chỉ ra rằng *recv_reg bị thay đổi bởi vòng lặp, không có lý do gì không đọc được trước khi nhập vòng lặp.

Vòng loại volatile đảm bảo rằng các tối ưu hóa này không được trình biên dịch thực hiện.

+0

+1 để được giải thích. Và tôi có một câu hỏi: những gì về const phương pháp dễ bay hơi? Nếu tôi có một lớp, được truy cập bởi nhiều luồng (mặc dù truy cập được đồng bộ với mutex) thì các phương thức const của tôi cũng có thể biến động hay không (vì một số biến có thể được thay đổi bởi luồng khác) – Sasa

6

Tôi cần sử dụng tính năng này trong ứng dụng nhúng nơi một số biến cấu hình nằm trong vùng bộ nhớ flash có thể được cập nhật bởi bộ nạp khởi động. Các biến cấu hình là 'liên tục' trong thời gian chạy, nhưng không có vòng loại dễ bay hơi các trình biên dịch sẽ tối ưu hóa một cái gì đó như thế này ...

cantx.id = 0x10<<24 | CANID<<12 | 0; 

... bởi precomputing giá trị liên tục và sử dụng một hướng dẫn lắp ráp ngay lập tức, hoặc tải liên tục từ một vị trí lân cận, sao cho bất kỳ cập nhật nào đối với giá trị CANID ban đầu trong vùng flash cấu hình sẽ bị bỏ qua. CANID phải là const dễ bay hơi.

3

const có nghĩa là biến không thể được sửa đổi bởi mã c, không phải biến không thể thay đổi. Nó có nghĩa là không có lệnh nào có thể ghi vào biến, nhưng giá trị của nó có thể vẫn thay đổi.

volatile có nghĩa là biến có thể thay đổi bất kỳ lúc nào và do đó không có giá trị được lưu trong bộ nhớ cache nào có thể được sử dụng; mỗi truy cập vào biến phải được thực hiện đến địa chỉ bộ nhớ của nó.

Kể từ khi câu hỏi được gắn thẻ "nhúng" và giả temp là một người dùng tuyên bố thay đổi, không phải là một đăng ký phần cứng liên quan (vì đây là những thường được xử lý trong một file .h riêng), hãy xem xét:

Một bộ xử lý nhúng có bộ nhớ dữ liệu đọc-ghi dễ bay hơi (RAM) và bộ nhớ dữ liệu chỉ đọc không bay hơi, ví dụ bộ nhớ FLASH trong kiến ​​trúc von-Neumann, nơi dữ liệu và không gian chương trình chia sẻ dữ liệu chung và bus địa chỉ. Nếu bạn khai báo const temp để có giá trị (ít nhất là nếu khác 0), trình biên dịch sẽ gán biến cho địa chỉ trong không gian FLASH, bởi vì ngay cả khi nó được gán cho địa chỉ RAM, nó vẫn cần bộ nhớ FLASH để lưu trữ giá trị ban đầu của biến, làm cho địa chỉ RAM trở nên lãng phí dung lượng vì tất cả các thao tác đều chỉ đọc.

Trong hậu quả:

int temp; là một biến lưu trữ trong RAM, khởi tạo 0 lúc khởi động (cstart), các giá trị được lưu trữ có thể được sử dụng.

const int temp; là một biến được lưu trữ trong (read-ony) FLASH, được khởi tạo thành 0 tại thời gian trình biên dịch, các giá trị được lưu trong bộ nhớ cache có thể được sử dụng.

volatile int temp; là một biến được lưu trữ trong RAM, được khởi tạo thành 0 khi khởi động (cstart), giá trị được lưu trong bộ nhớ cache sẽ KHÔNG được sử dụng.

const volatile int temp; là một biến lưu trữ trong FLASH (read-ony), khởi tạo 0 vào thời điểm biên dịch, các giá trị được lưu trữ sẽ KHÔNG được sử dụng

Ở đây có phần hữu ích:

Ngày nay các bộ xử lý nhúng nhất có khả năng thay đổi bộ nhớ không bay hơi chỉ đọc của họ bằng mô-đun chức năng đặc biệt, trong trường hợp này const int temp có thể thay đổi khi chạy, không được trực tiếp thay đổi. Nói theo cách khác, một hàm có thể sửa đổi giá trị tại địa chỉ nơi temp được lưu trữ.

Ví dụ thực tế sẽ là sử dụng temp cho số sê-ri của thiết bị. Lần đầu tiên bộ xử lý nhúng chạy, temp sẽ bằng 0 (hoặc giá trị được khai báo) và một hàm có thể sử dụng thực tế này để chạy thử nghiệm trong quá trình sản xuất và nếu thành công, yêu cầu được chỉ định số sê-ri và sửa đổi giá trị temp bằng một hàm đặc biệt. Một số bộ xử lý có một dải địa chỉ đặc biệt với bộ nhớ OTP (một lần lập trình) chỉ dành cho điều đó.

Nhưng ở đây có sự khác biệt:

Nếu const int temp là một ID sửa đổi thay vì một số sê-ri một thời gian có thể lập trình và không được khai báo volatile, một giá trị được lưu trữ có thể được sử dụng cho đến khi khởi động kế tiếp, có nghĩa là mới ID có thể không hợp lệ cho đến khi khởi động lại tiếp theo hoặc thậm chí tệ hơn, một số chức năng có thể sử dụng giá trị mới trong khi các hàm khác có thể sử dụng giá trị được lưu trữ cũ hơn cho đến khi khởi động lại. Nếu const int temp IS được khai báo voltaile, thay đổi ID sẽ có hiệu lực ngay lập tức.

1

Chúng tôi sử dụng từ khoá 'const' cho một biến khi chúng ta không muốn chương trình để thay đổi nó. Trong khi khi chúng ta khai báo biến 'const volatile', chúng ta đang nói chương trình không thay đổi nó và trình biên dịch biến này có thể thay đổi bất ngờ từ đầu vào đến từ thế giới bên ngoài.

+0

Câu trả lời này không tính đến ' volatile' specifier ở tất cả. –

4

Trong C, const và dễ bay hơi là loại vòng loại và hai giá trị này độc lập.

Về cơ bản, const có nghĩa là giá trị không thể sửa đổi được bởi chương trình.

Và dễ bay hơi có nghĩa là giá trị có thể thay đổi đột ngột (có thể từ bên ngoài chương trình).

Thực tế, tiêu chuẩn C đề cập đến một ví dụ về khai báo hợp lệ vừa là const vừa dễ bay hơi. Các ví dụ là

“const extern int dễ bay hơi real_time_clock;”

nơi real_time_clock có thể sửa đổi bởi phần cứng, nhưng không thể được gán cho, tăng lên hoặc giảm đi.

Vì vậy, chúng tôi đã xử lý const và dễ bay hơi riêng biệt. Bên cạnh đó, loại vòng loại này cũng áp dụng cho struct, union, enum và typedef.

2

Bạn có thể sử dụng const và bay hơi cùng nhau.Ví dụ, nếu 0x30 được giả định là giá trị của một cổng được thay đổi bằng cách chỉ điều kiện bên ngoài, tuyên bố sau đây sẽ ngăn chặn bất kỳ khả năng của tác dụng phụ bất ngờ:

const volatile char *port = (const volatile char *)0x30; 
Các vấn đề liên quan