Sự khác biệt giữa const và const dễ bay hơi


89

Nếu chúng ta khai báo một biến volatilemỗi khi giá trị mới được cập nhật
Nếu chúng ta khai báo một biến constthì giá trị của biến đó sẽ không bị thay đổi

Vậy const volatile int temp;
thì việc khai báo biến tempnhư trên có công dụng gì?
Điều gì xảy ra nếu chúng tôi khai báo là const int temp?


Bạn sẽ không sử dụng const volatile int temp;ở phạm vi khối (tức là bên trong { }), nó không có tác dụng gì ở đó.
MM

Câu trả lời:


145

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

Phần volatilecủa bộ định tính 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 một hệ thống nhúng, điều này thường được sử dụng để truy cập các thanh ghi phần cứng có thể đọc và được cập nhật bởi phần cứng, nhưng không có ý nghĩa gì khi ghi vào (hoặc có thể là lỗi khi ghi vào).

Một ví dụ có thể là thanh ghi trạng thái cho một cổng nối tiếp. Các bit khác nhau sẽ cho biết một ký tự đang chờ được đọc hoặc nếu thanh ghi truyền đã sẵn sàng chấp nhận một ký tự mới (tức là., - 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.

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 thanh ghi dẫn đến một lần đọc thực sự của phần cứng - bằng cách sử dụng giá trị được lưu trong bộ nhớ cache từ lần đọc trước đó sẽ thắng ' t 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 con trỏ này không được đánh dấu là hiện hữu volatile, một số vấn đề có thể xảy ra:

  • kiểm tra vòng lặp while có thể chỉ đọc thanh ghi trạng thái một lần, vì trình biên dịch có thể giả định rằng bất cứ điều gì nó trỏ đến sẽ không bao giờ thay đổi (không có gì trong kiểm tra vòng lặp while hoặc bản thân vòng lặp 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 với một vòng lặp vô hạn không bao giờ dừng ngay cả khi đã nhận được ký tự.
  • Việc đọc thanh ghi nhận có thể được trình biên dịch chuyển đến trước vòng lặp while - một lần nữa vì không có gì trong hàm cho biết điều đó *recv_regđược thay đổi bởi vòng lặp, không có lý do gì mà nó không thể được đọc trước khi vào vòng lặp.

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


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

39
  • volatile sẽ thông báo cho trình biên dịch không tối ưu hóa mã liên quan đến biến, thường là khi chúng ta biết nó có thể được thay đổi từ "bên ngoài", ví dụ như bởi một luồng khác.
  • const sẽ cho trình biên dịch biết rằng chương trình bị cấm sửa đổi giá trị của biến.
  • const volatilelà một điều rất đặc biệt có thể bạn sẽ thấy được sử dụng đúng 0 lần trong đời (tm). Như dự kiế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 đó sẽ không có tối ưu hóa nào được thực hiện trên biến.

12
Tôi đã nghĩ rằng volatilecác biến thường là những gì xảy ra khi bạn bắt đầu làm rối với phần cứng, không phải với các luồng khác. Nơi tôi đã thấy const volatileđược sử dụng là trong những thứ như thanh ghi trạng thái ánh xạ bộ nhớ hoặc những thứ tương tự.
CHỈ LÀ Ý KIẾN CHÍNH XÁC CỦA TÔI

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à duy nhất :).
mingos

25
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.
Daniel Grillo

28

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

Hằng số 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.


9
Thêm một vì chỉ ra rằng constdữ liệu không phải là "hằng số".
Bogdan Alexandru

7

Tôi cần sử dụng điều này trong một ứng dụng nhúng trong đó 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 này là 'không đổi' trong thời gian chạy, nhưng không có bộ định tính dễ bay hơi, 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ằng cách tính toán trước giá trị hằng số và sử dụng lệnh hợp ngữ tức thì hoặc tải hằng số từ một vị trí gần đó để mọi cập nhật cho giá trị CANID ban đầu trong vùng flash cấu hình sẽ bị bỏ qua. CANID phải là hằng số dễ bay hơi.


7

Trong C, const và variable là các định nghĩa loại và hai điều này độc lập.

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

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

Trên thực tế, tiêu chuẩn C đề cập đến một ví dụ về khai báo hợp lệ là hằng số và biến động. Ví dụ là

"Extern const variable int real_time_clock;"

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

Vì vậy, chúng ta nên xử lý const và biến động riêng biệt. Ngoài ra, các định nghĩa kiểu này cũng áp dụng cho struct, union, enum và typedef.


5

Bạn có thể sử dụng const và biến động cùng nhau. Ví dụ: nếu 0x30 được giả định là giá trị của một cổng chỉ được thay đổi bởi các điều kiện bên ngoài, thì khai báo sau sẽ ngăn chặn bất kỳ khả năng xảy ra các tác dụng phụ ngẫu nhiên:

const volatile char *port = (const volatile char *)0x30;

4

constcó nghĩa là biến không thể được sửa đổi bởi mã c, không phải là nó không thể thay đổi. 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ó vẫn có thể thay đổi.

volatilecó 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 quyền truy cập vào biến phải được thực thi đến địa chỉ bộ nhớ của nó.

Vì câu hỏi được gắn thẻ "nhúng" và giả sử templà một biến do người dùng khai báo, không phải là một thanh ghi liên quan đến phần cứng (vì chúng thường được xử lý trong một tệp .h riêng biệt), hãy xem xét:

Một bộ xử lý nhúng có cả bộ nhớ dữ liệu đọc-ghi dễ bay hơi (RAM) và bộ nhớ dữ liệu chỉ đọc không thay đổi, ví dụ bộ nhớ FLASH theo kiến ​​trúc von-Neumann, nơi dữ liệu và không gian chương trình chia sẻ một bus địa chỉ và dữ liệu chung.

Nếu bạn khai báo const tempcó một giá trị (ít nhất nếu khác 0), trình biên dịch sẽ gán biến cho một địa chỉ trong không gian FLASH, vì ngay cả khi nó được gán cho một địa chỉ RAM, nó vẫn cần bộ nhớ FLASH để lưu giá trị ban đầu. của biến, làm cho địa chỉ RAM trở nên lãng phí không gian vì tất cả các hoạt động ở chế độ chỉ đọc.

Hậu quả:

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

const int temp;là một biến được lưu trữ trong FLASH (read-ony), được khởi tạo bằng 0 tại thời điểm 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 bằng 0 khi khởi động (cstart), các 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 được lưu trữ trong FLASH (read-ony), được khởi tạo bằng 0 tại thời điểm trình biên dịch, các giá trị được lưu trong bộ nhớ cache sẽ KHÔNG được sử dụng

Đây là phần hữu ích:

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

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

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

Nếu const int templà ID có thể sửa đổi thay vì số sê-ri có thể lập trình một lần và KHÔNG được khai báo volatile, một giá trị được lưu trong bộ nhớ cache có thể được sử dụng cho đến lần khởi động tiếp theo, có nghĩa là ID mới 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 khác có thể sử dụng giá trị được lưu trong bộ nhớ cache cũ hơn cho đến khi khởi động lại. Nếu const int tempIS khai báo voltaile, thay đổi ID sẽ có hiệu lực ngay lập tức.


Chà câu trả lời này dài


2

Nói một cách dễ hiểu, Giá trị trong biến 'const variable' không thể được sửa đổi theo chương trình nhưng có thể được sửa đổi bằng phần cứng. Dễ bay hơi ở đây là ngăn chặn bất kỳ sự tối ưu hóa trình biên dịch nào.


1

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

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.