马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?我要加入
x
Over the years we have made lots of interesting and fun mistakes in Fortran 90 that we would like to share with you. We welcome your contributions and experiences so that we can share your pain.
<P>
<H3>Topics</H3>These "gotchas" are nasty because they will not fail on some machines, while failing on others (given various combinations of compilers and machine platforms).
<UL>
<LI><a>Danger with Optional Arguments</A>
<LI><a>Danger with intent(out)</A>
<LI><a>A suprise with non-advancing I/O</A>
<LI><a>Suprise with locally initialized variables</A>
<LI><a>Danger of calling Fortran 90 style routines</A>
<LI><a>Danger with interfaces to Fortran 77 subroutines</A>
<LI><a>A suprise with generic functions</A>
<LI><a>Big Danger with Undefined Pointers</A>
<LI><a>Subtle danger with overloading (=) to assign pointers</A>
<LI><a>Danger with pointers to pointers</A> </LI></UL>
<H3><FONT color=#aa0000>Danger with Optional Arguments </FONT></H3>In this example an optional argument is used to determine if a header is printed. <FONT size=4><PRE> subroutine print_char(this,header)
character(len=*), intent (in) :: this
logical, optional, intent (in) :: header
<FONT color=#aa0000> ! THIS IS THE WRONG WAY
if (present(header) .and. header) then
print *, 'This is the header '
endif</FONT>
print *, this
end subroutine print_char
</PRE></FONT>
<P><FONT size=4><PRE> subroutine print_char(this,header)
character(len=*), intent (in) :: this
logical, optional, intent (in) :: header
<FONT color=#00aa00> ! THIS IS THE RIGHT WAY
if (present(header)) then
if (header) print *, 'This is the header '
endif</FONT>
print *, this
end subroutine print_char
</PRE></FONT>
<P><B>Explanation</B><BR>The first method is not safe because the compiler is allowed to evaluate the header argument before the present function is evaluated. If the header argument is not in fact present an out of bounds memory reference could occur, which could cause a failure.
<H3><FONT color=#aa0000>Danger with intent(out) </FONT></H3>In this example we assign components of a derived type with intent(out). <FONT size=4><PRE> program intent_gotcha
type mytype
integer :: x
real :: y
end type mytype
type (mytype) :: a
a%x = 1 ; a%y = 2.
call assign(a)
<FONT color=#ccaa55> ! a%y COULD BE UNDEFINED HERE</FONT>
print *, a
contains
subroutine assign(this)
type (mytype), intent (out) :: this
<FONT color=#aa0000> ! THIS IS THE WRONG WAY
this%x = 2</FONT>
end subroutine assign
</PRE></FONT>
<P><FONT size=4><PRE> subroutine assign(this)
type (mytype), intent (out) :: this
<FONT color=#00aa00> ! THIS IS THE RIGHT WAY
this%x = 2 ; this%y = 2.</FONT>
end subroutine assign
end program intent_gotcha
</PRE></FONT>
<P><B>Explanation</B><BR>The problem is that when intent(out) is used with a derived type, any component not assigned in a procedure could become undefined on exit. For example, even though a%y was defined on entry to this routine, it could become undefined on exit because it was never assigned within the routine. The lesson is that all components of a derived type should be assigned within a procedure, when intent(out) is used. Intent(out) behaves like the result variable in a function: all components must be assigned.
<P>As an alternative, use intent(inout).
<H3><FONT color=#aa0000>A suprise with non-advancing I/O </FONT></H3>Many people think that the new non-advancing I/O in Fortran 90 is the same as stream I/O in other languages. It is not. <FONT size=4><PRE> do i = 1, 128
write (unit=6,fmt='(a)',advance='no') 'X'
end do
</PRE></FONT>
<P>We expect this program to print 128 X's in a row. However, unexpected behavior may occur if the record length for unit 6 is less than 128.
<P>One can inquire the record length in the follow way:
<P><FONT size=4><PRE> open(unit=6)
inquire(unit=6, recl=i)
print *, 'recl =', i
</PRE></FONT>
<P><B>Explanation</B><BR>All Fortran I/O is still record based. Non-advancing I/O allows partial reads and writes within a record. For many compilers the default record length is very large (e.g., 2147483647) giving the appearance of stream I/O. This is not true for all compilers however.
<P>On some compilers it is possible to set the record length as follows: <FONT size=4><PRE>open(unit=6, recl = 2147483646)
</PRE></FONT>
<P>On other compilers unit 6 is preconnected and the record length cannot be changed. (Thanks to Jon Richards of the USGS for this tip.)
<P>Note that unit 6 and unit * are not necessarily the same. Although they both may point to the default output device, with non-advancing I/O, each could keep track of the current location in its own record separately. Therefore we advise choosing one default unit and sticking with it.
<H3><FONT color=#aa0000>Suprise with locally initialized variables </FONT></H3>One must be careful when initializing a locally declared variable. <FONT size=4><PRE> real function kinetic_energy(v)
real, dimension(:), intent(in) :: v
integer i
<FONT color=#aa0000> ! THIS IS THE WRONG WAY
real :: ke = 0.0</FONT>
do i = 1, size(v)
ke = ke + v(i)**2
enddo
kinetic_energy = .5*ke
end function kinetic_energy
</PRE></FONT>
<P><FONT size=4><PRE> real function kinetic_energy(v)
real, dimension(:), intent(in) :: v
integer i
<FONT color=#00aa00> ! THIS IS THE RIGHT WAY
real :: ke
ke = 0.</FONT>
do i = 1, size(v)
ke = ke + v(i)**2
enddo
kinetic_energy = .5*ke
end function kinetic_energy
</PRE></FONT>
<P><B>Explanation</B><BR>A local variable that is initialized when declared has an implicit save attribute. ke is initialized only the first time the function is called. On subsequent calls the old value of ke is retained. This is a real suprise to C programmers.
<P>To avoid confusion it is best to add the save attribute to such locally initialized variables explicitly, even though this is redundant.
<H3><FONT color=#aa0000>Danger of calling Fortran 90 style routines </FONT></H3><FONT size=4><PRE> program main
real, dimension(5) :: x
x = 0.
<FONT color=#aa0000>! THIS IS WRONG
call incb(x)</FONT>
print *, x
end program main
subroutine incb(a)
! this is a fortran90 style subroutine
real, dimension(:) :: a
a = a + 1.
end subroutine incb
</PRE></FONT>
<P><B>Explanation</B><BR>The subroutine incb uses a Fortran 90 style assumed shape array (containing dimension(:)). Such routines must either be in a module, or have an explicit interface wherever they are used. In this example, neither one was true.
<P>One correct way to call such procedures is to use an explicit interface as follows: <FONT size=4><PRE> program main
real, dimension(5) :: x
<FONT color=#00aa00>! THIS IS THE RIGHT WAY
interface
subroutine incb(a)
real, dimension(:) :: a
end subroutine incb
end interface</FONT>
x = 0.
call incb(x)
print *, x
end program main
subroutine incb(a)
! this is a fortran90 style subroutine
real, dimension(:) :: a
a = a + 1.
end subroutine incb
</PRE></FONT>
<P>If the routine is in a module interfaces are generated automatically and do not need to be explicitly written. <FONT size=4><PRE><FONT color=#00aa00>! THIS IS ANOTHER RIGHT WAY
module inc
contains</FONT>
subroutine incb(a)
! this is a fortran90 style subroutine
real, dimension(:) :: a
a = a + 1.
end subroutine incb
<FONT color=#00aa00> end module inc</FONT>
program main
<FONT color=#00aa00> use inc</FONT>
real, dimension(5) :: x
x = 0.
call incb(x)
print *, x
end program main
</PRE></FONT><FONT size=3>If interfaces are used, the interface MUST match the actual function. </FONT>
<H3><FONT color=#aa0000>Danger with interfaces to Fortran 77 subroutines </FONT></H3><FONT size=4><PRE> program main
real, dimension(5) :: x
! interface to Fortran 77 style routine
interface
subroutine inca(a,n)
integer :: n
<FONT color=#aa0000>! THIS IS THE WRONG WAY
real, dimension(:) :: a</FONT>
<FONT color=#00aa00>! THIS IS THE RIGHT WAY
real, dimension(n) :: a</FONT>
end subroutine inca
end interface
x = 0.
call inca(x,5)
print *, x
end program main
subroutine inca(a,n)
! this is a fortran77 style subroutine
dimension a(n)
do 10 j = 1, n
a(j) = a(j) + 1.
10 continue
return
end
</PRE></FONT>
<P><B>Explanation</B><BR>The interface declaration must always match the actual subroutine declaration. In this case, the interface statement refers to a Fortran 90 style assumed shape array. The actual subroutine refers to a Fortran 77 explicit shape array. The lesson here is: Interfaces to Fortran 77 style routines must only use Fortran 77 style constructs.
<P>In this example, it is permitted to leave out the interface altogether since routines without interfaces are treated as Fortran77 style routines by default. However, if the interface is left out, the compiler will no longer check whether the arguments of calling procedures agree with the arguments listed in the interface. </P> |