It is all about observable behavior. The only observable behavior of your cycle is that i is 50000000U after the cycle. The compiler is allowed to optimize it and replace it with i = 50000000U; . This assignment i will also be optimized because the value of i has no observable consequences.
The volatile keyword tells the compiler that writing and reading from i have observable behavior, which prevents it from being optimized.
The compiler will also not optimize function calls where it does not have access to code. Theoretically, if the compiler had access to the entire OS code, it could optimize everything except volatile variables, which are often superimposed on hardware I / O operations.
These optimization rules all correspond to what is written in the C standard (see comments for links).
In addition, if you want to get a delay, use a specialized function (for example, the OS API), they are reliable and do not consume a processor, as opposed to a delay on a delay like yours.
source share