The docs answer both of your questions.
What happens to the code when you pass only -O without any level?
-O
-O1
Optimize. Optimizing compilation takes somewhat more time, and a lot more memory for a large function.
[...]
It enables optimizations.
What's the difference between this and -O0
-O0
Reduce compilation time and make debugging produce the expected results. This is the default.
As the default, it really does nothing at all.
In this mode, optimizations (or at least non-trivial ones) are effectively disabled.
Zero optimiziation doesn't detect heap overflow
On Compiler Explorer, you did enable optimizations (by using -O
).
If you stop optimizing the variables away (by removing -O
or by using -O0
), you get the expected error.
<source>: In function 'main':
<source>:7:15: warning: unused variable 'n' [-Wunused-variable]
7 | char n = x[11];
| ^
<source>:7:15: warning: 'x[11]' is used uninitialized [-Wuninitialized]
7 | char n = x[11];
| ^
Program returned: 1
Program stderr
=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001b at pc 0x0000004011a4 bp 0x7fffc6caef80 sp 0x7fffc6caef78
READ of size 1 at 0x60200000001b thread T0
#0 0x4011a3 in main /app/example.c:7
#1 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
#2 0x40109d in _start (/app/output.s+0x40109d)
0x60200000001b is located 1 bytes to the right of 10-byte region [0x602000000010,0x60200000001a)
allocated by thread T0 here:
#0 0x7f88c4e9d1af in malloc (/opt/compiler-explorer/gcc-12.1.0/lib64/libasan.so.8+0xbb1af)
#1 0x401167 in main /app/example.c:6
#2 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
[snip]
Demo on Compiler Explorer
The same would happen with optimizations if the program actually used n
.