1

What is the difference between Register and volatile? when to use which one? what is the meaning of volatile register variable?

register int a; volatile int a;

MCG
  • 1,011
  • 1
  • 10
  • 21
Radhe
  • 35
  • 1
  • 4
  • 4
    These terms are not really related nor are they mutually exclusive. `register` is a _storage-class specifier_ (like `static` and `extern`) while `volatile` is a _type qualifier_ (like `const`). So you are kind of asking what's the difference between a cheese and a dog. As for what they do, did you try to read a book? – Lundin Aug 08 '17 at 13:50
  • I googled "meaning of volatile c" and "meaning of register c" for you. Both top hits lead to the linked SO duplicates. – Lundin Aug 08 '17 at 13:59
  • Use of `register` keyword should increase the performance of the system where as the use of `volatile` will decrease performance due to multiple accesses to memory. – MCG Aug 08 '17 at 14:10

2 Answers2

6

volatile means that the value of the variable can be changed by something which is not visible for the compiler. That means that the variable has to have a valid memory representation, it has to be read before any use, and stored if changed.

register - variable should be stored in the register if possible. The registers do not have the address so the operator & cannot be used on them. Ignored nowadays by most the compilers except very specific form:

avr gcc example: register unsigned char counter asm("r3");

which binds permanently the variable to the particular register. It decreases the register pool and makes optimisation more difficult

register unsigned char counter asm("r3");

Examples:

volatile int y;

int x8(volatile int x)  // the x is read as many times as it is used
{
    return x * x * x * x * x * x * x * x; 
}

    sub     sp, sp, #8
    str     r0, [sp, #4]
    ldr     r3, [sp, #4]
    ldr     r1, [sp, #4]
    ldr     r2, [sp, #4]
    mul     r3, r1, r3
    mul     r3, r2, r3
    ldr     r2, [sp, #4]
    ldr     r0, [sp, #4]
    mul     r3, r2, r3
    mul     r3, r0, r3
    ldr     r2, [sp, #4]
    ldr     r0, [sp, #4]
    mul     r3, r2, r3
    mul     r3, r0, r3
    ldr     r0, [sp, #4]
    mul     r0, r3, r0
    add     sp, sp, #8
    bx      lr



int test(volatile int x) // parameter changed - stored
{
    return x++;
}

    sub     sp, sp, #8
    str     r0, [sp, #4]
    ldr     r0, [sp, #4]
    add     r3, r0, #1
    str     r3, [sp, #4]
    add     sp, sp, #8
    bx      lr


int test2(void) same as above
{
    return y++;
}
    ldr     r3, .L6
    ldr     r0, [r3]
    add     r2, r0, #1
    str     r2, [r3]
    bx      lr
0___________
  • 60,014
  • 4
  • 34
  • 74
  • is there any practical use for `register` on a modern 32-bit MCU? – Andy J Aug 08 '17 at 13:55
  • practical - no. Compilers are usually much better in micro optimizations than the coders. – 0___________ Aug 08 '17 at 13:58
  • Are you sure in the first example `x` is read every time it is used? The compiler may argue that all the reads are sequenced at the first read and perform the calculation. Since there are no other reads or writes in between that can cause the sequencing to fail. – Ajay Brahmakshatriya Aug 08 '17 at 14:59
  • it is a weird gcc implementation. Yes it has to be read every time is used as it could be changed meanwhile – 0___________ Aug 08 '17 at 15:00
  • It could have, but the behavior should be valid too if all of them were read before. This the programmer has no way to guarantee. Which means the compiler can also enforce that. In a loop, it is a different case all together. But fixed number of reads can be force sequenced. – Ajay Brahmakshatriya Aug 08 '17 at 15:02
  • I agree gcc must be doing it, but it is not required to. – Ajay Brahmakshatriya Aug 08 '17 at 15:03
  • IMO not. As volatile value may change during (in my example) multiplication – 0___________ Aug 08 '17 at 15:03
  • @PeterJ *As volatile value may change during (in my example) multiplication* yes I agree. But since we are talking about asynchronous behavior, how is the programmer going to ensure that all the reads on x do not happen before the change happens. All the reads happening before the change is a completely valid behavior. – Ajay Brahmakshatriya Aug 08 '17 at 15:51
  • What I meant is Yes gcc might be re reading each value, but it is not required to do that by the standard. – Ajay Brahmakshatriya Aug 08 '17 at 15:52
  • Is required. Read it carefully. – 0___________ Aug 08 '17 at 15:59
  • @Ajay Brahmakshatriya `how programmer going to ensure that all the reads on x do not happen before the change happens.` read and write operations are atomic. So you can write before read has finished and vice versa. Only RW sequences are not atomic – 0___________ Aug 08 '17 at 16:02
  • Okay, lets say the compiler generated code like you said. With every read being a load. Now is the programmer guaranteed that the second or the third read will be __after__ the external change? No right? So that read being the same as the first read is also a valid behavior of the program. – Ajay Brahmakshatriya Aug 08 '17 at 16:04
  • Can I know the reason of downvote – 0___________ Aug 08 '17 at 19:23
4

volatile keyword should be used always when modification of variable can be asynchronous such as registers in CPU.

Example would be that you are using microcontroller and you are waiting flag when button is pressed. Then you are reading register like this:

uint8_t* reg = 0x12345678; //Register address
while (*reg & 0x02) { //Read register until 0 = button pressed

}

This can easily fail since compiler will check it once and later will ignore value and you may end in infinite loop or you don't even get anything since CPU can cache value of reg because it assumes noone can modify value where it points to.

If you do this:

volatile uint8_t* reg = 0x12345678; //Register address
while (*reg & 0x02) { //Read register until 0 = button pressed

}

You force compiler to do read instruction from actual memory before it uses content of variable. There is no cache in this case.


register keyword is just a hint for compiler that it should put variable to CPU register, but this is not always the case and some compilers ignore this keyword. It is implementation defined.

unalignedmemoryaccess
  • 7,246
  • 2
  • 25
  • 40
  • your definition is of `volatile` is extremely imprecise. There is nothing about the optimization, it is about retrieving and storing its value. – 0___________ Aug 08 '17 at 13:39
  • 1
    @PeterJ if you disable optimizations, volatile has no sense. – unalignedmemoryaccess Aug 08 '17 at 13:40
  • 1
    @PeterJ Accessing a volatile object is a side-effect and the compiler is not allowed to optimize expressions containing side-effects, as per the definition of the "abstract machine" (5.1.2.3). Therefore the volatile keyword will implicitly block optimizations, as required by the very core of the C language. – Lundin Aug 08 '17 at 13:54
  • @tilz0R On modern compilers some optimizations are there even when "disabled". C standard does not link `volatile` with optimizations. Adding `volatile` won't necessarily "prevent compiler optimizations" on a variable. – Eugene Sh. Aug 08 '17 at 13:54
  • @Lundin Having a variable defined as `volatile` doesn't mean the variable won't get optimized away if, say, not used. – Eugene Sh. Aug 08 '17 at 13:55
  • @EugeneSh. True, but the compiler is not allowed to optimize away _access_ to such a variable. – Lundin Aug 08 '17 at 13:56
  • The point I try to say here is that you need volatile if variable content is not changed only from main context. Such as peripheral registers in MCU. – unalignedmemoryaccess Aug 08 '17 at 13:56
  • @Lundin Yes. So saying `volatile` is preventing optimization on a variable is a bit misleading and confusing, as it will prevent only a specific subset of optimizations. – Eugene Sh. Aug 08 '17 at 13:59
  • @EugeneSh. It will prevent caching variable. So it will add instruction ti always read variable from ram before using it. – unalignedmemoryaccess Aug 08 '17 at 14:00
  • @tilz0R you seem to have a different understanding of the word "register" than the OP or the other answerer. What you call accessing a register is actually accessing RAM, which is sort of the opposite of what happens when qualifying a variable as a register variable. Adding a hint about the register keyword at the end is just adding to the confusion. -1. – Daniel S. Aug 08 '17 at 14:02
  • to prevent something to be read from the cache is forcing cache invalidation - for example by placing the barrier instruction. Try to complie and see if you have any there – 0___________ Aug 08 '17 at 14:03
  • Btw whether or not volatile should act as a memory barrier is up for debate - this is not black or white. The C language somewhat clearly says "yes it should", but compilers tend to insist that we use system API:s for memory barriers instead. Also, C was clearly not designed with multi-core pre-fetch systems in mind. Muddy topic. – Lundin Aug 08 '17 at 14:03
  • @DanielS. `register` *It's a hint to the compiler that the variable will be heavily used and that you recommend it be kept in a processor register if possible.* So my consumption is correct. – unalignedmemoryaccess Aug 08 '17 at 14:04
  • volatile does nothing with atomicity. It is quite common misunderstanding. – 0___________ Aug 08 '17 at 14:05
  • 1
    @PeterJ Atomic access has nothing to do with memory barriers, and not necessarily anything to do with compiler optimizations either. – Lundin Aug 08 '17 at 14:08
  • @Lundin on the single core platform yes because the code is executed one same core and cache for every thread will be valid all the time On multicore "atomic" has a much broader meaning as more than one core may do an atomic operation on the same data in cache. Dealing with depends on the hardware - some invalidate the cache if it happens some do not leaving it to the programmer discretion. – 0___________ Aug 08 '17 at 14:20
  • `volatile` can also get optimized if the compiler can argue that it can't get modified. For instance a variable with automatic scope that doesn't have address taken. – Ajay Brahmakshatriya Aug 08 '17 at 14:48
  • @tilz0R *It will prevent caching variable. So it will add instruction to always read variable from ram before using it.* this doesn't make sense in every architecture. Infact on X86 there is full cache coherency and any access you make is guaranteed to be consistent with the RAM. There are no instruction to explicitly read the data from the RAM. If you meant caching in registers, ignore my comment. – Ajay Brahmakshatriya Aug 08 '17 at 14:55
  • volatile auto variable will have the actual memory representation. It is completely wrong. I afraid that you have no clue what the volatile means - and just repeat some myths and disinformations from different forums. – 0___________ Aug 08 '17 at 16:12
  • @PeterJ can you point to anywhere in the standard which states that? – Ajay Brahmakshatriya Aug 08 '17 at 16:13
  • @tilz0R I assure you I am not trolling. Say you have a local variable on the stack (X86 implementation). In its entire life time, it's address is not taken, but it is declared volatile. Do you still think the compiler should put it into memory? – Ajay Brahmakshatriya Aug 08 '17 at 16:16
  • @AjayBrahmakshatriya 5.1.2.3 and then about side effects. – 0___________ Aug 08 '17 at 16:17
  • `Say you have a local variable on the stack (X86 implementation). In its entire life time, it's address is not taken, but it is declared volatile. Do you still think the compiler should put it into memory?` I do not understand what `address taken` means. https://godbolt.org/g/pbSnWT @Ajay Brahmakshatriya - can you spot the difference? – 0___________ Aug 08 '17 at 16:22
  • @Ajay A compiler absolutely cannot keep a `volatile` variable in a register. The volatile qualification prevents the compiler from assuming that subsequent reads will return the same value, thus forcing it to do a new read each time and making enregistering optimizations impossible. That's the guarantee provided by the C++ standard. Microsoft's compiler (in `/volatile:ms` mode on VS 2015 and later; always on in earlier versions), you get an even stronger guarantee than that (something akin to `std::atomic`, with strong acquire and release semantics). – Cody Gray - on strike Aug 08 '17 at 16:42
  • @CodyGray Okay, I may have misinterpreted some parts of the standard. Thank you for clarifying. – Ajay Brahmakshatriya Aug 08 '17 at 16:49
  • The `register` qualifier only means (6.7.1) "A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible." However, the definition of such a register variable is elsewhere in the standard defined as a variable which address is never taken. This is important for determining if accessing an uninitialized auto variable is UB or not. 6.3.2.1 "If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class **(never had its address taken)**..." – Lundin Aug 09 '17 at 06:14
  • Further this is guaranteed by the & operator 6.5.3.2: "The operand of the unary & operator shall be..." "...not declared with the register storage-class specifier". Therefore a register variable can never has its address taken. It is a term directly from the C standard. – Lundin Aug 09 '17 at 06:16