Let us make a review of some of the fundamental concepts in order to understand -- critical section and volatile variable in interrupt context.
1. There are 32 general purpose registers (Fig-1: GPR: r0 - r31) within ATmega328P MCU; some of which are used for arithmetic and logical operations.

Figure-1:
2. There are 2048 bytes RAM (Fig-2: address range: 0x0100 - 0x08FF) inside the 328P MCU into which the variables are stored.

Figure-2:
3. When we declare a variable like byte y = 0x00;, the compiler usually (not always) chooses a RAM location (say: 0x0200) for the indentifier/variable y and then stores the value 0x00 into it.
4. If we want to add 1 with y, we carry out this operation: y++;. This operation involves the the following sub-operations:
(1) The present content (0x00) of RAM location 0x0200 comes into a regsiter (say: r17) of the MCU.
r17 <------- (0x0200): lds r17, 0x0200
(2) The content of register r17 is incremented by .
r17 <------- (r17) + 1 : inc r17
(3) The content of register r17 goes back into RAM location 0x0200.
(r17) -------> 0x0200: sts 0x0200, r17
5. Assume that we intend to do the y++ operation in the following loop() function of Arduino, where interrupt logic is enabled.
void loop()
{
y++; //(1) lds r17, 0x0200 (2) inc r17 (3) sts 0x0200, r17
}
(1) Assume that an interrupt has occured while inc r17 instruction is being executed in the loop() function of Step-5.
(2) The MCU will finish the execution of current instruction (inc r17) and will then jump to the ISR. The execution of sts 0x0200, r17 remains suspened. As a result, r17 contains 0x01 and RAM location 0x0200 conatisn 0x00 as it could not be updated.
6. This is the ISR routine where the variable y is aslo processed as a shared variable.
void ISRINT0()
{
if(y == 0x01) //(1) r17 <---- (0x0200) (2) CPI r17, 0x01 (3) BRNE LX
{
// do it
}
//LX:
}
(1) In the ISR, the MCU can use any register to load the value of y (content of location 0x0200) and it could be r17. If this is the case, then the content of r17 is 0x00. After returning from ISR, the MCU will not find 0x01 into r17, what it left there at the time of intrruption. This is the problem and here comes the concept of critical section or the critical codes.
(2) The above problem of Step-6(1) could be avoided by using the following modified loop() function (Step-7) where the interrupt logic is kept disabled for a while and let all the sub-operations (the crtical section/critical codes) of the loop() function of Step-5 be completed allowing the location 0x0200 be updated.
7. The modified loop() function.
void loop()
{
noInterrupts(); //interrupt logic is disabled; the MCU will finish all the sub-operations
y++; //(1) lds r17, 0x0200 (2) inc r17 (3) sts 0x0200, r17
interrupts(); //enable interrupt logic with 0x0200 location contains 0x01
}
8. The keyword volatile tells the compiler that the said variable could be changed at any time by ISR routine and hence it must be stored in RAM locations (s) and not in register(s).
Hope the above discussion will be helpful to you.