Why is volatile needed in C?

前端 未结 18 2220
攒了一身酷
攒了一身酷 2020-11-22 04:07

Why is volatile needed in C? What is it used for? What will it do?

相关标签:
18条回答
  • 2020-11-22 04:32

    Volatile is also useful, when you want to force the compiler not to optimize a specific code sequence (e.g. for writing a micro-benchmark).

    0 讨论(0)
  • 2020-11-22 04:33

    volatile in C actually came into existence for the purpose of not caching the values of the variable automatically. It will tell the compiler not to cache the value of this variable. So it will generate code to take the value of the given volatile variable from the main memory every time it encounters it. This mechanism is used because at any time the value can be modified by the OS or any interrupt. So using volatile will help us accessing the value afresh every time.

    0 讨论(0)
  • 2020-11-22 04:35

    I'll mention another scenario where volatiles are important.

    Suppose you memory-map a file for faster I/O and that file can change behind the scenes (e.g. the file is not on your local hard drive, but is instead served over the network by another computer).

    If you access the memory-mapped file's data through pointers to non-volatile objects (at the source code level), then the code generated by the compiler can fetch the same data multiple times without you being aware of it.

    If that data happens to change, your program may become using two or more different versions of the data and get into an inconsistent state. This can lead not only to logically incorrect behavior of the program but also to exploitable security holes in it if it processes untrusted files or files from untrusted locations.

    If you care about security, and you should, this is an important scenario to consider.

    0 讨论(0)
  • 2020-11-22 04:36

    Volatile tells the compiler not to optimize anything that has to do with the volatile variable.

    There are at least three common reasons to use it, all involving situations where the value of the variable can change without action from the visible code: When you interface with hardware that changes the value itself; when there's another thread running that also uses the variable; or when there's a signal handler that might change the value of the variable.

    Let's say you have a little piece of hardware that is mapped into RAM somewhere and that has two addresses: a command port and a data port:

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

    Now you want to send some command:

    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;
    }
    

    Looks easy, but it can fail because the compiler is free to change the order in which data and commands are written. This would cause our little gadget to issue commands with the previous data-value. Also take a look at the wait while busy loop. That one will be optimized out. The compiler will try to be clever, read the value of isbusy just once and then go into an infinite loop. That's not what you want.

    The way to get around this is to declare the pointer gadget as volatile. This way the compiler is forced to do what you wrote. It can't remove the memory assignments, it can't cache variables in registers and it can't change the order of assignments either:

    This is the correct version:

       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;
        }
    
    0 讨论(0)
  • 2020-11-22 04:38

    In simple terms, it tells the compiler not to do any optimisation on a particular variable. Variables which are mapped to device register are modified indirectly by the device. In this case, volatile must be used.

    0 讨论(0)
  • 2020-11-22 04:41

    In my opinion, you should not expect too much from volatile. To illustrate, look at the example in Nils Pipenbrinck's highly-voted answer.

    I would say, his example is not suitable for volatile. volatile is only used to: prevent the compiler from making useful and desirable optimizations. It is nothing about the thread safe, atomic access or even memory order.

    In that example:

        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;
        }
    

    the gadget->data = data before gadget->command = command only is only guaranteed in compiled code by compiler. At running time, the processor still possibly reorders the data and command assignment, regarding to the processor architecture. The hardware could get the wrong data(suppose gadget is mapped to hardware I/O). The memory barrier is needed between data and command assignment.

    0 讨论(0)
提交回复
热议问题