【发布时间】:2014-11-04 16:05:44
【问题描述】:
我需要在 Fortran 中为一个项目实现树形结构,所以我已经阅读了各种在线指南,解释了如何做到这一点。但是,我不断收到错误或奇怪的结果。
假设我想构建一个二叉树,其中每个节点都存储一个整数值。我还希望能够将新值插入树中并打印树的节点。所以我写了一个类型“树”,它包含一个整数、两个指向子子树的指针和一个我设置为 .true 的布尔值。如果没有子子树:
module class_tree
implicit none
type tree
logical :: isleaf
integer :: value
type (tree), pointer :: left,right
end type tree
interface new
module procedure newleaf
end interface
interface insert
module procedure inserttree
end interface
interface print
module procedure printtree
end interface
contains
subroutine newleaf(t,n)
implicit none
type (tree), intent (OUT) :: t
integer, intent (IN) :: n
t % isleaf = .true.
t % value = n
nullify (t % left)
nullify (t % right)
end subroutine newleaf
recursive subroutine inserttree(t,n)
implicit none
type (tree), intent (INOUT) :: t
integer, intent (IN) :: n
type (tree), target :: tleft,tright
if (t % isleaf) then
call newleaf(tleft,n)
call newleaf(tright,n)
t % isleaf = .false.
t % left => tleft
t % right => tright
else
call inserttree(t % left,n)
endif
end subroutine inserttree
recursive subroutine printtree(t)
implicit none
type (tree), intent (IN) :: t
if (t % isleaf) then
write(*,*) t % value
else
write(*,*) t % value
call printtree(t % left)
call printtree(t % right)
endif
end subroutine printtree
end module class_tree
除非试图插入叶子,否则插入总是在左子树中完成。在这种情况下,将插入到两个子树中以确保节点始终具有 0 或 2 个子节点。打印是在前缀遍历中完成的。
现在如果我尝试运行以下程序:
program main
use class_tree
implicit none
type (tree) :: t
call new(t,0)
call insert(t,1)
call insert(t,2)
call print(t)
end program main
我得到了所需的输出 0 1 2 2 1。但是如果我在“call insert(t,2)”之后添加“call insert(t,3)”并再次运行,则输出为 0 1 2 0 然后我得到一个段错误。
我试图查看故障是否发生在插入或打印过程中,所以我尝试运行:
program main
use class_tree
implicit none
type (tree) :: t
call new(t,0)
call insert(t,1)
call insert(t,2)
write(*,*) 'A'
call insert(t,3)
write(*,*) 'B'
call print(t)
end program main
它使段错误消失,但我得到一个非常奇怪的输出 A B 0 1 2673568 6 1566250180。
在网上搜索类似错误时,我得到了类似here 的结果,它说这可能是由于递归调用过多。但是,对 insert(t,3) 的调用应该只包含 3 个递归调用......我还尝试使用带有 -g -Wall -pedantic -fbounds-check 的 gfortran 进行编译并使用调试器运行。似乎错误发生在打印子程序中的“if (t % isleaf)”行,但我不知道如何理解。
编辑:
在 cmets 之后,我在 gfortran 中使用 -g -fbacktrace -fcheck=all -Wall 编译并尝试检查内存的状态。我对此很陌生,所以我不确定我是否正确使用了我的调试器 (gdb)。
在三个插入之后和调用print 之前,似乎一切都很顺利:例如,当我在gdb 中输入p t % left % left % right % value 时,我得到了预期的输出(即3)。如果我只输入p t,则输出为 (.FALSE.,0,x,y),其中 x 和 y 是十六进制数(我猜是内存地址)。但是,如果我尝试p t % left,我会得到类似指针的“描述”:
PTR TO -> (Type tree
logical(kind=4) :: isleaf
integer(kind=4) :: value
因为每个指针都指向一个包含两个指针的树,所以它会重复很多次。我本来希望输出类似于 p t 的输出,但我不知道这是否正常。
我还尝试检查内存:例如,如果我输入 x/4uw t % left,我得到 4 个字,前 2 个字似乎对应于 isleaf 和 value,最后 2 个字对应于内存地址。通过这样的内存地址,我设法访问了所有节点,我没有发现任何问题。
段错误发生在打印例程中。如果我在故障后键入p t,它说我无法访问 0x0 地址。这是否意味着当我尝试打印它时,我的树以某种方式被修改了?
【问题讨论】:
-
我看不出你的代码有什么问题,但是当它全部变成递归和指针时我通常看不到。对我来说,这似乎是一个调试会话的机会。启动您最喜欢的调试器,单步执行您的代码,密切注意指针和内存使用情况。如果您还没有最喜欢的调试器,那么现在是结识的好时机。
-
启用编译器的所有检查。 Gfortran:
-g -fbacktrace -fcheck=all -Wall,ifort:-g -fbacktrace -check -warn。你的-fbounds-check不够用。 -
感谢您提供这些答案,我将使用调试器中的详细信息编辑我的帖子。
-
+1 勇敢地在 Fortran 中尝试此操作。