By doing nothing with the values except printing them you
unfortunately slip through a loop-hole in several compilers
that ends up basically acting like the variables are never being used,
so I added a few lines where I added zero to the values to your example
to reflect that more common case.
If you had done any other operation other than printing on the variables common
compiler switches that should be used during development
would have warned this was not standard-conforming code.
For example, if you add zero to the values and compiler with
gfortran -Wall xx.f90 -o xx
./xx
You would have been warned about X1.
If you preset core you can see you had NaN values in
the static arrays.
If you had set floating point traps it would not have let
you run using the undefined values.
You should always have on as many debug flags as you can when
developing. Some people spent a good deal of time writing those
features because they are so valuable.
At least with gfortran, use -Wall at a minimum whenever
developing new code unless it causes a massive slowdown (which
is rare except with very large high-performance codes that
have long wall-clocks). So just making a simple script that
uses three sets of compiler switches, each of which can help
detect different issues:
#!/bin/bash
#@(#) debug.sh - compiler and run with debug flags
BASE_OPTIONS='
-fimplicit-none
-Wuninitialized
-Wextra
-Wall
-pedantic-errors
-fcheck=all
-Wimplicit-interface
-Wimplicit-procedure
-ffree-form
-fbacktrace
-fmax-errors=3
'
MORE='
-finit-real=snan
-Wcharacter-truncation
-Warray-temporaries
'
EXTRA='
-ffpe-trap=invalid,zero,overflow
'
set -x
rm -f ./a.out
gfortran -g -O0 $BASE_OPTIONS "$@" && ./a.out
rm -f ./a.out
gfortran -g -O0 $BASE_OPTIONS $MORE "$@" && ./a.out
rm -f ./a.out
gfortran -g -O0 $BASE_OPTIONS $MORE $EXTRA "$@" && ./a.out
exit
And using something like your example
program test
real(8) :: x1, x2(5)
real(8), allocatable :: x3(:)
allocate(x3(5))
x2=x2+0
x3=x3+0
x1=x1+0
print *, x1
print *, x2
print *, x3
end program
+ gfortran -g -O0 -fimplicit-none -Wuninitialized -Wextra -Wall -pedantic-errors -fcheck=all -Wimplicit-interface -Wimplicit-procedure -ffree-form -fbacktrace -fmax-errors=3 xx.f90
xx.f90:5:9:
5 | x1=x1+0
| ^
Warning: ‘x1’ may be used uninitialized in this function [-Wmaybe-uninitialized]
+ ./a.out
-1.4614335721641550E+051
0.0000000000000000 -1.4614335721641550E+051 0.0000000000000000 -1.4614331294416530E+051 0.0000000000000000
0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
Will not catch everything, but you do get a warning x1 is not set
+ gfortran -g -O0 -fimplicit-none -Wuninitialized -Wextra -Wall -pedantic-errors -fcheck=all -Wimplicit-interface -Wimplicit-procedure -ffree-form -fbacktrace -fmax-errors=3 -finit-real=snan -Wcharacter-truncation -Warray-temporaries xx.f90
+ ./a.out
NaN
NaN NaN NaN NaN NaN
0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000
Seeing those NaN values is a red flag and it makes it a lot less likely the
program will inadvertently run with undefined values.
+ gfortran -g -O0 -fimplicit-none -Wuninitialized -Wextra -Wall -pedantic-errors -fcheck=all -Wimplicit-interface -Wimplicit-procedure -ffree-form -fbacktrace -fmax-errors=3 -finit-real=snan -Wcharacter-truncation -Warray-temporaries -ffpe-trap=invalid,zero,overflow xx.f90
+ ./a.out
Program received signal SIGFPE: Floating-point exception - erroneous arithmetic operation.
Backtrace for this error:
#0 0x7fa93b23cd21 in ???
#1 0x7fa93b23bef5 in ???
#2 0x7fa93b07020f in ???
#3 0x56334a787319 in test
at /tmp/xx.f90:5
#4 0x56334a7876b7 in main
at /tmp/xx.f90:11
fwish: line 27: 708172 Floating point exception(core dumped) ./a.out
+ exit
Checking for floating point exceptions stops you early on so you can figure
out what went wrong.
In some classes I used to teach compiler options were introduced early, with an emphasis on
a good set of debug switches. One of the most important features a good compiler has is the ability to
catch errors. New users should always make sure they carefully read the options their compiler has.
Unfortunately many switches are esoteric and complicated but often mixed in with the more important switches, which seems to put off a lot of people. This results in people using compilers for years without realizing there are really useful switches available.
There has been some discussion lately about new courses being created. I would highly recommend that somewhere in those courses someone discusses switches for debugging, switches for performance/production release, switches for profiling, and switches needed for using a debugger.
I have seen codes released into production running 20% slower than they should; codes with easily detetected bugs being put out in the wild; and so on that a good knowledge of readily available compiler options would have prevented.