Tại sao dễ bay hơi cần thiết trong C?


Câu trả lời:


425

Biến động nói với trình biên dịch không tối ưu hóa bất cứ điều gì phải làm với biến dễ bay hơi.

Có ít nhất ba lý do phổ biến để sử dụng nó, tất cả đều liên quan đến các tình huống trong đó giá trị của biến có thể thay đổi mà không cần hành động từ mã hiển thị: Khi bạn giao diện với phần cứng tự thay đổi giá trị; khi có một luồng khác đang chạy cũng sử dụng biến đó; hoặc khi có một bộ xử lý tín hiệu có thể thay đổi giá trị của biến.

Giả sử bạn có một phần cứng nhỏ được ánh xạ vào RAM ở đâu đó và có hai địa chỉ: cổng lệnh và cổng dữ liệu:

typedef struct
{
  int command;
  int data;
  int isbusy;
} MyHardwareGadget;

Bây giờ bạn muốn gửi một số lệnh:

void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
  // wait while the gadget is busy:
  while (gadget->isbusy)
  {
    // do nothing here.
  }
  // set data first:
  gadget->data    = data;
  // writing the command starts the action:
  gadget->command = command;
}

Nhìn có vẻ dễ dàng, nhưng nó có thể thất bại vì trình biên dịch có thể tự do thay đổi thứ tự ghi dữ liệu và lệnh. Điều này sẽ khiến tiện ích nhỏ của chúng tôi đưa ra các lệnh với giá trị dữ liệu trước đó. Cũng hãy xem sự chờ đợi trong khi vòng lặp bận rộn. Điều đó sẽ được tối ưu hóa. Trình biên dịch sẽ cố gắng khéo léo, đọc giá trị của isbusy chỉ một lần và sau đó đi vào một vòng lặp vô hạn. Đó không phải là những gì bạn muốn.

Cách để giải quyết vấn đề này là khai báo tiện ích con trỏ là không ổn định. Bằng cách này, trình biên dịch buộc phải làm những gì bạn đã viết. Nó không thể loại bỏ các bài tập bộ nhớ, nó không thể lưu các biến trong bộ đăng ký và nó cũng không thể thay đổi thứ tự của các bài tập:

Đây là phiên bản chính xác:

   void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
    {
      // wait while the gadget is busy:
      while (gadget->isbusy)
      {
        // do nothing here.
      }
      // set data first:
      gadget->data    = data;
      // writing the command starts the action:
      gadget->command = command;
    }

46
Cá nhân, tôi thích kích thước số nguyên là tính tự nhiên, ví dụ int8 / int16 / int32 khi nói chuyện với phần cứng. Câu trả lời hay mặc dù;)
tonylo

22
vâng, bạn nên khai báo mọi thứ với kích thước đăng ký cố định, nhưng này - đó chỉ là một ví dụ.
Nils Pipenbrinck

69
Dễ bay hơi cũng cần thiết trong mã luồng khi bạn đang chơi với dữ liệu không được bảo vệ đồng thời. Và vâng, có những thời điểm hợp lệ để làm điều đó, ví dụ bạn có thể viết một hàng đợi tin nhắn vòng tròn an toàn mà không cần bảo vệ đồng thời rõ ràng, nhưng nó sẽ cần các biến động.
Gordon Wrigley

14
Đọc đặc tả C khó hơn. Dễ bay hơi chỉ có hành vi được xác định trên I / O của thiết bị được ánh xạ bộ nhớ hoặc bộ nhớ được chạm bởi chức năng ngắt không đồng bộ. Nó không nói về luồng, và một trình biên dịch tối ưu hóa truy cập vào bộ nhớ được chạm bởi nhiều luồng là phù hợp.
ephemient

17
@tolomea: sai hoàn toàn. buồn 17 người không biết điều đó. dễ bay hơi không phải là một hàng rào bộ nhớ. nó chỉ liên quan đến việc tránh bỏ mã trong quá trình tối ưu hóa dựa trên giả định về các tác dụng phụ không nhìn thấy được .
v.oddou

187

volatiletrong C thực sự ra đời với mục đích không lưu trữ các giá trị của biến tự động. Nó sẽ báo cho trình biên dịch không lưu trữ giá trị của biến này. Vì vậy, nó sẽ tạo mã để lấy giá trị của volatilebiến đã cho từ bộ nhớ chính mỗi lần nó gặp nó. Cơ chế này được sử dụng bởi vì bất cứ lúc nào giá trị có thể được sửa đổi bởi HĐH hoặc bất kỳ ngắt. Vì vậy, sử dụng volatilesẽ giúp chúng tôi truy cập vào giá trị mỗi lần.


Thừa hưởng sự tồn tại? Không phải ban đầu đã được mượn từ C ++ sao? Chà, tôi dường như nhớ ...
cú pháp

Đây không phải là biến động tất cả về - nó cũng cấm một số sắp xếp lại nếu được chỉ định là không ổn định ..
FaceBro

4
@FaceBro: Mục đích volatilelà để các trình biên dịch có thể tối ưu hóa mã trong khi vẫn cho phép các lập trình viên đạt được ngữ nghĩa sẽ đạt được mà không cần tối ưu hóa như vậy. Các tác giả của Tiêu chuẩn dự kiến ​​rằng việc triển khai chất lượng sẽ hỗ trợ bất kỳ ngữ nghĩa nào hữu ích với các nền tảng mục tiêu và trường ứng dụng của họ và không hy vọng rằng các tác giả biên dịch sẽ tìm cách cung cấp ngữ nghĩa chất lượng thấp nhất phù hợp với Tiêu chuẩn và không 100% ngu ngốc (lưu ý rằng các tác giả của Tiêu chuẩn nhận ra rõ ràng trong lý do ...
supercat

1
... rằng việc triển khai có thể tuân thủ mà không có chất lượng đủ tốt để thực sự phù hợp cho bất kỳ mục đích nào, nhưng họ không nghĩ cần phải ngăn chặn điều đó).
supercat

1
@syntaxerror làm thế nào nó có thể được mượn từ C ++ khi C lớn hơn C ++ hơn cả thập kỷ (cả về bản phát hành đầu tiên và tiêu chuẩn đầu tiên)?
phuclv

178

Một cách sử dụng khác volatilelà xử lý tín hiệu. Nếu bạn có mã như thế này:

int quit = 0;
while (!quit)
{
    /* very small loop which is completely visible to the compiler */
}

Trình biên dịch được phép thông báo thân vòng lặp không chạm vào quitbiến và chuyển đổi vòng lặp thành while (true)vòng lặp. Ngay cả khi quitbiến được đặt trên bộ xử lý tín hiệu cho SIGINTSIGTERM; trình biên dịch không có cách nào để biết điều đó.

Tuy nhiên, nếu quitbiến được khai báo volatile, trình biên dịch buộc phải tải nó mỗi lần, bởi vì nó có thể được sửa đổi ở nơi khác. Đây chính xác là những gì bạn muốn trong tình huống này.


Khi bạn nói "trình biên dịch buộc phải tải nó mọi lúc, giống như khi trình biên dịch quyết định tối ưu hóa một biến nhất định và chúng tôi không khai báo biến đó là biến động, tại thời điểm chạy, biến nhất định được tải vào các thanh ghi CPU không có trong bộ nhớ ?
Amit Singh Tomar

1
@AmitSinghTomar Nó có nghĩa là những gì nó nói: Mỗi khi mã kiểm tra giá trị, nó được tải lại. Mặt khác, trình biên dịch được phép giả định rằng các hàm không tham chiếu đến biến không thể sửa đổi nó, vì vậy giả sử như CesarB dự định rằng vòng lặp trên không được đặt quit, trình biên dịch có thể tối ưu hóa nó thành một vòng lặp không đổi, giả sử rằng không có cách nào quitđể thay đổi giữa các lần lặp. NB: Đây không hẳn là một sự thay thế tốt cho lập trình luồng an toàn thực tế.
gạch dưới

nếu thoát là một biến toàn cục, thì trình biên dịch sẽ không tối ưu hóa vòng lặp while, đúng không?
Pierre G.

2
@PierreG. Không, trình biên dịch luôn có thể giả định rằng mã là đơn luồng, trừ khi được nói khác đi. Đó là, trong trường hợp không có volatilehoặc các dấu hiệu khác, nó sẽ cho rằng không có gì bên ngoài vòng lặp sửa đổi biến đó một khi nó đi vào vòng lặp, ngay cả khi đó là biến toàn cục.
CesarB

1
@PierreG. Vâng, thử ví dụ biên soạn extern int global; void fn(void) { while (global != 0) { } }với gcc -O3 -Svà xem xét các tập tin lắp ráp kết quả, trên máy tính của tôi nó movl global(%rip), %eax; testl %eax, %eax; je .L1; .L4: jmp .L4, đó là một vòng lặp vô hạn nếu toàn cầu không bằng không. Sau đó thử thêm volatilevà xem sự khác biệt.
CesarB

60

volatilebáo cho trình biên dịch rằng biến của bạn có thể được thay đổi bằng các phương tiện khác, ngoài mã đang truy cập vào nó. ví dụ: nó có thể là một vị trí bộ nhớ được ánh xạ I / O. Nếu điều này không được chỉ định trong các trường hợp như vậy, một số truy cập biến có thể được tối ưu hóa, ví dụ: nội dung của nó có thể được giữ trong một thanh ghi và vị trí bộ nhớ không được đọc lại.


30

Xem bài viết này của Andrei Alexandrescu, " Người bạn tốt nhất của lập trình viên đa luồng "

Các biến động từ khóa được đưa ra để ngăn chặn tối ưu hóa trình biên dịch mà có thể làm cho mã không chính xác trong sự hiện diện của sự kiện không đồng bộ nhất định. Ví dụ: nếu bạn khai báo một biến nguyên thủy là dễ bay hơi , trình biên dịch không được phép lưu vào bộ đệm đó trong một thanh ghi - một tối ưu hóa phổ biến sẽ là thảm họa nếu biến đó được chia sẻ giữa nhiều luồng. Vì vậy, quy tắc chung là, nếu bạn có các biến kiểu nguyên thủy phải được chia sẻ giữa nhiều luồng, hãy khai báo các biến đó biến động. Nhưng bạn thực sự có thể làm nhiều hơn với từ khóa này: bạn có thể sử dụng nó để bắt mã không an toàn cho chuỗi và bạn có thể làm như vậy trong thời gian biên dịch. Bài viết này cho thấy nó được thực hiện như thế nào; giải pháp liên quan đến một con trỏ thông minh đơn giản cũng giúp dễ dàng tuần tự hóa các phần quan trọng của mã.

Bài viết áp dụng cho cả CC++.

Cũng xem bài viết " C ++ và những hiểm họa của khóa kiểm tra hai lần " của Scott Meyers và Andrei Alexandrescu:

Vì vậy, khi xử lý một số vị trí bộ nhớ (ví dụ: cổng được ánh xạ bộ nhớ hoặc bộ nhớ được tham chiếu bởi ISR ​​[Các tuyến dịch vụ ngắt], một số tối ưu hóa phải bị treo. dễ bay hơi tồn tại để chỉ định xử lý đặc biệt cho các vị trí đó, cụ thể: (1) nội dung của biến dễ bay hơi là "không ổn định" (có thể thay đổi bằng phương tiện không xác định đối với trình biên dịch), (2) tất cả ghi vào dữ liệu dễ bay hơi là "có thể quan sát được" phải được thực hiện một cách tôn giáo và (3) tất cả các hoạt động trên dữ liệu dễ bay hơi được thực hiện theo trình tự xuất hiện trong mã nguồn. Hai quy tắc đầu tiên đảm bảo đọc và viết đúng. Cái cuối cùng cho phép thực hiện các giao thức I / O trộn lẫn đầu vào và đầu ra. Đây là những gì không chính thức đảm bảo tính dễ bay hơi của C và C ++.


Tiêu chuẩn có xác định xem việc đọc có được coi là "hành vi có thể quan sát" hay không nếu giá trị không bao giờ được sử dụng? Ấn tượng của tôi là nó nên như vậy, nhưng khi tôi tuyên bố nó ở nơi khác, một người nào đó đã thách thức tôi trích dẫn. Dường như đối với tôi, trên bất kỳ nền tảng nào mà việc đọc một biến dễ bay hơi có thể hình dung có thể có bất kỳ ảnh hưởng nào, một trình biên dịch nên được yêu cầu tạo mã thực hiện mỗi lần đọc được chỉ định chính xác; không có yêu cầu đó, sẽ rất khó để viết mã tạo ra một chuỗi các lần đọc có thể dự đoán được.
supercat

@supercat: Theo bài viết đầu tiên, "Nếu bạn sử dụng công cụ sửa đổi dễ bay hơi trên một biến, trình biên dịch sẽ không lưu bộ đệm đó vào các thanh ghi - mỗi lần truy cập sẽ đánh vào vị trí bộ nhớ thực của biến đó." Ngoài ra, trong phần §6.7.3.6 của tiêu chuẩn c99 có ghi: "Một đối tượng có loại đủ điều kiện dễ bay hơi có thể được sửa đổi theo cách không xác định khi thực hiện hoặc có các tác dụng phụ chưa biết khác." Nó cũng ngụ ý rằng các biến dễ bay hơi có thể không được lưu trong bộ đăng ký và tất cả các lần đọc và ghi phải được thực hiện theo thứ tự liên quan đến các điểm chuỗi, trong thực tế chúng có thể quan sát được.
Robert S. Barnes

Bài báo sau thực sự tuyên bố rõ ràng rằng đọc là tác dụng phụ. Cái trước chỉ ra rằng việc đọc không thể được thực hiện theo trình tự, nhưng dường như không loại trừ khả năng chúng bị tách biệt hoàn toàn.
supercat

"Trình biên dịch không được phép lưu vào bộ đệm trong một thanh ghi" - Hầu hết các cấu trúc RISC đều là các máy đăng ký, vì vậy mọi đọc-sửa-ghi đều phải lưu trữ đối tượng trong một thanh ghi. volatilekhông đảm bảo tính nguyên tử.
quá trung thực cho trang web này

1
@Olaf: Tải một cái gì đó vào một thanh ghi không giống như bộ nhớ đệm. Bộ nhớ đệm sẽ ảnh hưởng đến số lượng tải hoặc cửa hàng hoặc thời gian của chúng.
supercat

28

Giải thích đơn giản của tôi là:

Trong một số tình huống, dựa trên logic hoặc mã, trình biên dịch sẽ thực hiện tối ưu hóa các biến mà nó cho rằng không thay đổi. Các volatiletừ khóa ngăn chặn một biến được tối ưu hóa.

Ví dụ:

bool usb_interface_flag = 0;
while(usb_interface_flag == 0)
{
    // execute logic for the scenario where the USB isn't connected 
}

Từ đoạn mã trên, trình biên dịch có thể nghĩ usb_interface_flagđược định nghĩa là 0 và trong vòng lặp while nó sẽ bằng 0 mãi mãi. Sau khi tối ưu hóa, trình biên dịch sẽ coi nó như while(true)mọi lúc, dẫn đến một vòng lặp vô hạn.

Để tránh các loại kịch bản này, chúng tôi tuyên bố cờ là không ổn định, chúng tôi đang nói với trình biên dịch rằng giá trị này có thể được thay đổi bởi giao diện bên ngoài hoặc mô-đun chương trình khác, tức là không tối ưu hóa nó. Đó là trường hợp sử dụng cho dễ bay hơi.


19

Một sử dụng cận biên cho dễ bay hơi là sau đây. Giả sử bạn muốn tính đạo hàm số của hàm f:

double der_f(double x)
{
    static const double h = 1e-3;
    return (f(x + h) - f(x)) / h;
}

Vấn đề là x+h-xthường không bằng hdo lỗi vòng. Hãy nghĩ về nó: khi bạn trừ các số rất gần, bạn sẽ mất rất nhiều chữ số có nghĩa có thể làm hỏng tính toán của đạo hàm (nghĩ 1,00001 - 1). Một cách giải quyết có thể là

double der_f2(double x)
{
    static const double h = 1e-3;
    double hh = x + h - x;
    return (f(x + hh) - f(x)) / hh;
}

nhưng tùy thuộc vào nền tảng và trình chuyển đổi trình biên dịch của bạn, dòng thứ hai của chức năng đó có thể bị xóa bởi trình biên dịch tối ưu hóa mạnh mẽ. Vì vậy, bạn viết thay

    volatile double hh = x + h;
    hh -= x;

để buộc trình biên dịch đọc vị trí bộ nhớ chứa hh, từ bỏ cơ hội tối ưu hóa cuối cùng.


Sự khác biệt giữa sử dụng hhoặc hhtrong công thức phái sinh là gì? Khi hhđược tính công thức cuối cùng sử dụng nó như công thức đầu tiên, không có sự khác biệt. Có lẽ nó nên (f(x+h) - f(x))/hh?
Serge Zhukov

2
Sự khác biệt giữa hhhđược hhcắt bớt thành một số sức mạnh tiêu cực của hai bởi hoạt động x + h - x. Trong trường hợp này, x + hhxkhác nhau chính xác bởi hh. Bạn cũng có thể lấy công thức của mình, nó sẽ cho kết quả tương tự, vì x + hx + hhbằng nhau (nó là mẫu số quan trọng ở đây).
Alexandre C.

3
Không phải là cách dễ đọc hơn để viết này sẽ là x1=x+h; d = (f(x1)-f(x))/(x1-x)? mà không sử dụng dễ bay hơi.
Serge Zhukov

Bất kỳ tham chiếu nào mà trình biên dịch có thể xóa sạch dòng thứ hai của hàm?
CoffeeTableEspresso

@CoffeeTableEspresso: Không, xin lỗi. Càng biết nhiều về dấu phẩy động, tôi càng tin rằng trình biên dịch chỉ được phép tối ưu hóa nó nếu được nói rõ ràng như vậy, với -ffast-mathhoặc tương đương.
Alexandre C.

11

Có hai công dụng. Chúng được sử dụng đặc biệt thường xuyên hơn trong phát triển nhúng.

  1. Trình biên dịch sẽ không tối ưu hóa các hàm sử dụng các biến được xác định bằng từ khóa dễ bay hơi

  2. Dễ bay hơi được sử dụng để truy cập các vị trí bộ nhớ chính xác trong RAM, ROM, v.v ... Điều này được sử dụng thường xuyên hơn để kiểm soát các thiết bị ánh xạ bộ nhớ, truy cập các thanh ghi CPU và định vị các vị trí bộ nhớ cụ thể.

Xem ví dụ với danh sách lắp ráp. Re: Sử dụng từ khóa "dễ bay hơi" trong phát triển nhúng


"Trình biên dịch sẽ không tối ưu hóa các hàm sử dụng các biến được xác định bằng từ khóa dễ bay hơi" - điều đó hoàn toàn sai.
quá trung thực cho trang web này

10

Tính dễ bay hơi cũng hữu ích, khi bạn muốn buộc trình biên dịch không tối ưu hóa một chuỗi mã cụ thể (ví dụ: để viết một điểm chuẩn vi mô).


10

Tôi sẽ đề cập đến một kịch bản khác trong đó các chất bay hơi là quan trọng.

Giả sử bạn lập bản đồ bộ nhớ cho một tệp I / O nhanh hơn và tệp đó có thể thay đổi phía sau hậu trường (ví dụ: tệp không nằm trên ổ cứng cục bộ của bạn, nhưng thay vào đó được phục vụ qua mạng bởi một máy tính khác).

Nếu bạn truy cập dữ liệu của tệp ánh xạ bộ nhớ thông qua các con trỏ tới các đối tượng không bay hơi (ở cấp mã nguồn), thì mã được trình biên dịch tạo ra có thể tìm nạp cùng một dữ liệu nhiều lần mà bạn không biết về nó.

Nếu dữ liệu đó thay đổi, chương trình của bạn có thể trở nên sử dụng hai hoặc nhiều phiên bản dữ liệu khác nhau và rơi vào trạng thái không nhất quán. Điều này có thể dẫn đến không chỉ hành vi không chính xác về mặt logic của chương trình mà còn dẫn đến các lỗ hổng bảo mật có thể khai thác trong đó nếu nó xử lý các tệp hoặc tệp không tin cậy từ các vị trí không tin cậy.

Nếu bạn quan tâm đến bảo mật, và bạn nên, đây là một kịch bản quan trọng cần xem xét.


7

không ổn định có nghĩa là bộ lưu trữ có thể thay đổi bất cứ lúc nào và được thay đổi nhưng có gì đó nằm ngoài sự kiểm soát của chương trình người dùng. Điều này có nghĩa là nếu bạn tham chiếu biến, chương trình phải luôn kiểm tra địa chỉ vật lý (nghĩa là fifo đầu vào được ánh xạ) và không sử dụng nó theo cách được lưu trong bộ nhớ cache.


Không có trình biên dịch nào biến động có nghĩa là "địa chỉ vật lý trong RAM" hoặc "bỏ qua bộ đệm".
tò mò


5

Theo tôi, bạn không nên mong đợi quá nhiều từ volatile. Để minh họa, hãy xem ví dụ trong câu trả lời được bình chọn cao của Nils Pipenbrinck .

Tôi sẽ nói, ví dụ của anh ấy không phù hợp volatile. volatilechỉ được sử dụng để: ngăn trình biên dịch thực hiện tối ưu hóa hữu ích và mong muốn . Không có gì về chủ đề an toàn, truy cập nguyên tử hoặc thậm chí thứ tự bộ nhớ.

Trong ví dụ đó:

    void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
    {
      // wait while the gadget is busy:
      while (gadget->isbusy)
      {
        // do nothing here.
      }
      // set data first:
      gadget->data    = data;
      // writing the command starts the action:
      gadget->command = command;
    }

các gadget->data = datatrước gadget->command = commandchỉ duy nhất được đảm bảo trong mã biên soạn bởi trình biên dịch. Trong thời gian chạy, bộ xử lý vẫn có thể sắp xếp lại dữ liệu và gán lệnh, liên quan đến kiến ​​trúc bộ xử lý. Phần cứng có thể nhận được dữ liệu sai (giả sử tiện ích được ánh xạ tới I / O phần cứng). Hàng rào bộ nhớ là cần thiết giữa dữ liệu và gán lệnh.


2
Tôi muốn nói rằng volility được sử dụng để ngăn trình biên dịch thực hiện tối ưu hóa thường hữu ích và mong muốn. Như đã viết, có vẻ như volatilelàm giảm hiệu suất mà không có lý do. Về việc nó có đủ hay không, điều đó sẽ phụ thuộc vào các khía cạnh khác của hệ thống mà lập trình viên có thể biết nhiều hơn trình biên dịch. Mặt khác, nếu bộ xử lý đảm bảo rằng một lệnh ghi vào một địa chỉ nhất định sẽ xóa bộ đệm CPU nhưng trình biên dịch không cung cấp cách nào để xóa các biến được lưu trong bộ nhớ cache mà CPU không biết gì, việc xóa bộ đệm sẽ vô dụng.
supercat

5

Trong ngôn ngữ do Dennis Ritchie thiết kế, mọi quyền truy cập vào bất kỳ đối tượng nào, ngoài các đối tượng tự động không có địa chỉ, sẽ hoạt động như thể nó tính toán địa chỉ của đối tượng và sau đó đọc hoặc ghi lưu trữ tại địa chỉ đó. Điều này làm cho ngôn ngữ rất mạnh mẽ, nhưng cơ hội tối ưu hóa bị hạn chế nghiêm trọng.

Mặc dù có thể thêm một vòng loại mời một trình biên dịch để cho rằng một đối tượng cụ thể sẽ không bị thay đổi theo những cách kỳ lạ, nhưng một giả định như vậy sẽ phù hợp với đại đa số các đối tượng trong các chương trình C, và nó sẽ có không thực tế để thêm một vòng loại cho tất cả các đối tượng mà giả định đó sẽ phù hợp. Mặt khác, một số chương trình cần sử dụng một số đối tượng mà giả định đó sẽ không giữ được. Để giải quyết vấn đề này, Standard nói rằng các trình biên dịch có thể cho rằng các đối tượng không được khai báovolatile sẽ không quan sát hoặc thay đổi giá trị của chúng theo những cách nằm ngoài sự kiểm soát của trình biên dịch, hoặc nằm ngoài sự hiểu biết của trình biên dịch hợp lý.

Bởi vì các nền tảng khác nhau có thể có những cách khác nhau trong đó các đối tượng có thể được quan sát hoặc sửa đổi bên ngoài sự kiểm soát của trình biên dịch, điều phù hợp là các trình biên dịch chất lượng cho các nền tảng đó sẽ khác nhau trong cách xử lý chính xác volatilengữ nghĩa của chúng . Thật không may, vì Tiêu chuẩn không đề xuất rằng các trình biên dịch chất lượng dành cho lập trình cấp thấp trên nền tảng nên xử lý volatiletheo cách sẽ nhận ra bất kỳ và tất cả các tác động có liên quan của một hoạt động đọc / ghi cụ thể trên nền tảng đó, nhiều trình biên dịch không thực hiện được vì vậy theo những cách khiến cho việc xử lý những thứ như I / O nền trở nên khó khăn hơn theo cách hiệu quả nhưng không thể bị phá vỡ bởi "tối ưu hóa" của trình biên dịch.


5

Nói một cách đơn giản, nó báo cho trình biên dịch không thực hiện bất kỳ tối ưu hóa nào trên một biến cụ thể. Các biến được ánh xạ tới thanh ghi thiết bị được sửa đổi gián tiếp bởi thiết bị. Trong trường hợp này, dễ bay hơi phải được sử dụng.


1
Có điều gì mới trong câu trả lời này chưa được đề cập trước đây không?
slfan

3

Một biến động có thể được thay đổi từ bên ngoài mã được biên dịch (ví dụ: chương trình có thể ánh xạ biến dễ bay hơi sang thanh ghi ánh xạ bộ nhớ.) Trình biên dịch sẽ không áp dụng một số tối ưu hóa nhất định cho mã xử lý biến dễ bay hơi - ví dụ: nó đã thắng ' t tải nó vào một thanh ghi mà không ghi nó vào bộ nhớ. Điều này rất quan trọng khi làm việc với các thanh ghi phần cứng.


0

Như được đề xuất đúng bởi nhiều người ở đây, cách sử dụng phổ biến của từ khóa biến động là bỏ qua việc tối ưu hóa biến dễ bay hơi.

Ưu điểm tốt nhất xuất hiện trong đầu và đáng được đề cập sau khi đọc về biến động là - để ngăn ngừa sự quay trở lại của biến trong trường hợp a longjmp. Một bước nhảy không cục bộ.

Điều đó có nghĩa là gì?

Điều đó đơn giản có nghĩa là giá trị cuối cùng sẽ được giữ lại sau khi bạn hủy xếp chồng , để trở về một số khung ngăn xếp trước đó; thông thường trong trường hợp một số kịch bản sai lầm.

Vì nó nằm ngoài phạm vi của câu hỏi này, tôi không đi sâu vào chi tiết setjmp/longjmpở đây, nhưng nó đáng để đọc về nó; và làm thế nào tính năng biến động có thể được sử dụng để giữ lại giá trị cuối cùng.


-2

nó không cho phép trình biên dịch tự động thay đổi giá trị của các biến. một biến động là để sử dụng năng độ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.