Difference between volatile and global variable in c

I am trying to understand difference between volatile and global variable in plain c language.

I know global variable can be accessible where it is defined and volatile keyword inform to compiler don't optimized

I have written a sample code to understand fundamental difference between them. code will not compile it's just example to understand difference

#include<stdio.h>

int x;                 // global variable  
volatile int y;        // global variable with volatile keyboard 

int main()
{
	x = 0; y = 0;
	
	if ( x == 1)
	{
		printf(" x : %d", x);
	}
	
	if ( y == 1)
	{
	    printf(" x : %d", y);
	}
	
	return 0;
}

ISR ()
{
	x = 1;  // x changes in interrupt
	y = 1;  // y change in interrupt
}

How x and y are different ?

optimizers make assumptions that a variable hasn't cannot change within the flow of a loop.

volatile tells the optimizer that some other mechanism can change the value such as an interrupt or hardware register if accessed using a pointer

google for more info

Depends how often you call the ISR. If it's often, eventually the non-volatile variable will register (by accident), but the volatile one is guaranteed to work every time.

I don't see how your example illustrates, or proves/disproves the language definitions, though. There is no great thought experiment in it, just a pedestrian use of the feature. A real exploration would require a complete, working program.

The compiler does not see anything in the code that affects the value of x, so it assumes that the value of x will be the 0 it was initialized with, which means ( x == 1 ) is always false and nothing in the if statement will ever be executed. The compiler does not consider the code used in the ISR, because it is never referenced in the remainder of the code. To the compiler the ISR is just an unused function that can be eliminated. The compiler will include the ISR, because it accesses y, but even there x may be optimized away.

The volatile keyword tells the compiler the value of y may change at any time, so it must be accessed directly every time it is used. This prevents making assumptions about its value, or optimizing the code by temporarily storing a copy of y in the processor itself.

in other words, from its allocation in memory

The comments in the previous post illustrate the need for an actual working program.

I have tried my best example to understand difference between them

Do you have any specific example ? please post

I have problem with this part ?

value of x should be change because its a global variable why don't x change?

have a look post output-phase-shift-on-linear-load where on a timer interrupt analogue data is being read from an ADC and placed in volatile arrays for later processing in the main program loop()

You should also note that on an 8 bit processor like the AVR, a 16 bit int cannot be accessed in a single operation, so in non-interrupt context, you should copy their value with interrupts disabled, and work only on the copy

2 Likes

there may be a block of code that repeatedly checks a pointed to value using a pointer to a memory mapped hardware register checking it's value.

    while (*pReg & SomeBit)
        ;

there is no code that changes the pointed to value. since the optimizers doesn't see anything changing the value of the pointed to variable it will make assumptions and possible even drop the loop.

of course the value of the hardware register may change because of some activity in the hardware.

this is avoided by defining the pointed value as volatile. the following line is in Arduino.h

#define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )

They aren't different, those are separate attributes due to keywords or placement in the code.

Variables outside of functions are global.

They may be further qualified

const
volatile
extern
static

and so forth.

volatile is of special use for the particular case where one hand might not know what the other is doing, and do something that isn't readily seen as wrong.

a7

The compiler does not detect anything in your code that alters the value of x. The fact that x is assigned a value in the ISR is not considered, because the compiler does not see any call to the ISR by the code.

The actual behavior my depend on the compiler being used, and the optimization settings. If all optimizations are disabled, the code may work, but without declaring x volatile that cannot be relied upon.

There are plenty on this forum, in cases where people have forgotten to use the "volatile" keyword, and then wonder why their program doesn't work as expected.

Depending on what optimizations the compiler implemented, the effect of declaring volatile in any particular case may not be noticeable.

In simple terms, volatile tells the compiler to go back to actual memory every time the variable is referenced. Without this the compiler is allowed to generate code that can keep a copy of the contents of a variable in a register and use the value from the register instead from memory. This can make the code smaller and run faster when compared to fetching the memory contents or storing the value to memory every time it is referenced/altered but it can potentially have issues if ISRs are involved.
If you have an ISR that modifies the variable, the foreground code may not see it if it is say in a loop looking at the variable, if volatile is not used.
i.e. the foreground code will be looking at the register copy of the variable and the not contents of the variable stored in memory.

The other thing that comes into play is atomicity.
This means being able to read / update a variable without interruption.
On the AVR this can be a big issue with volatile variables that are larger than 1 byte.
volatile only ensures that the variable is accessed from memory. It does not ensure atomicity.
The reason this is a big issue on the AVR is that ints are 16 bit (2 bytes) but the processor is only an 8 bit processor. Accessing/updating an integer on the AVR takes two memory accesses, and the processor could be interrupted in between those two accesses.
i.e. foreground can read 1 byte of the 2 byte integer and be interrupted.
The ISR updates the integer (both bytes) and exits.
The foreground resumes and fetches the 2nd byte of the integer - but it missed the 1st byte of the variable that the ISR just updated.

Code must take precautions to ensure that this does not happen.
One way is to only use 8 bit volatiles.
The other is to mask interrupts in the foreground when accessing volatile variables larger than 1 byte to ensure that an ISR cannot modify them while they are being accessed.

--- bill

1 Like

Verging on burying the lede. So it bears repeating.

A nice and totally foolproof set of macros is available in <atomic.h>.

For simple programs on UNOs and their ilk, it usually suffices to turn off interrupts, do your access (copying the volatile object is convenient and fast) and then turn interrupts off.

THX @bperrybap for the complete picture.

a7

The stuff in <atomic.h> is useful and really nice but only works on AVR, so it is not portable to other processors.
Using interrupts() and noInterrupts() will be portable across all platforms.

That said, if possible, the simplest solution is to use an 8 bit value.
Then the atomicity issue go away.

--- bill

or a 32-bit MCU

But be careful, the MCU may be 32-bit, but the data bus may not be, or the variable may not be aligned on 32-bit boundaries.

Not true for Read-Modify-Write (RMW) accesses, like x++.

1 Like

True.
It only goes away for simple booleans.

--- bill