Recursion Fortran递归树实现中的分段错误

Recursion Fortran递归树实现中的分段错误,recursion,tree,fortran,Recursion,Tree,Fortran,我需要在Fortran中为一个项目实现一个树结构,所以我在网上阅读了各种指南,解释了如何实现它。然而,我不断得到错误或奇怪的结果 假设我想构建一个二叉树,其中每个节点存储一个整数值。我还希望能够在树中插入新值并打印树的节点。所以我写了一个类型“tree”,它包含一个整数,两个指向子子树的指针和一个布尔值,我将其设置为.true。如果没有子目录树: module class_tree implicit none type tree logical :: isleaf intege

我需要在Fortran中为一个项目实现一个树结构,所以我在网上阅读了各种指南,解释了如何实现它。然而,我不断得到错误或奇怪的结果

假设我想构建一个二叉树,其中每个节点存储一个整数值。我还希望能够在树中插入新值并打印树的节点。所以我写了一个类型“tree”,它包含一个整数,两个指向子子树的指针和一个布尔值,我将其设置为.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 1。但是如果我在“调用插入(t,2)”之后添加“调用插入(t,3)”并再次运行,则输出为0 1 2 0,然后我得到一个segfault

我试图查看故障是否发生在插入或打印过程中,因此我尝试运行:

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
它使segfault消失,但我得到一个非常奇怪的输出a B0 1 2673568 6 1566250180

当我在网上搜索类似的错误时,我得到的结果就像它说的可能是由于太多的递归调用。但是,对insert(t,3)的调用应该只包含3个递归调用。。。我还尝试使用gfortran和-g-Wall-pedantic-fbounds检查编译,并使用调试器运行。似乎故障发生在打印子例程中的“if(t%isleaf)”行,但我不知道如何理解这一点

编辑:

在注释之后,我在gfortran中编译了
-g-fbacktrace-fcheck=all-Wall
,并尝试检查内存的状态。我对这个很陌生,所以我不确定我是否正确使用了我的调试器(gdb)

在三次插入之后和调用
print
之前,似乎一切都进行得很顺利:例如,当我在gdb中键入
pt%left%left%right%value
时,我得到了预期的输出(即3)。如果我只是键入
pt
,输出是(.FALSE.,0,x,y),其中x和y是十六进制数(我猜是内存地址)。但是,如果我尝试
pt%left
,我会得到指针的“描述”:

PTR TO -> (Type tree
logical(kind=4) :: isleaf
integer(kind=4) :: value
因为每个指针指向一个包含两个指针的树,所以它会重复很多次。我本希望输出类似于
pt
,但我不知道这是否正常

我还尝试检查内存:例如,如果我键入
x/4uw t%left
,我得到4个单词,前2个单词似乎对应于
isleaf
值,最后2个对应于内存地址。通过这样跟踪内存地址,我成功地访问了所有节点,没有发现任何错误


SEG故障发生在打印例行程序中。如果我在故障后键入
pt
,则表示我无法访问0x0地址。这是否意味着我的树在打印时被修改了?

问题的原因是,超出范围的变量不再有效。这与Python等语言形成了对比,在Python中,现有指针的数量是相关的(refcount)

在您的特定情况下,这意味着对
newleaf(left,n)
newleaf(right,n)
的调用分别设置
left
right
的值,但这些变量的作用域为ouf,因此无效

更好的方法是根据需要分配每个叶(第一个叶除外,因为它已经分配了,在程序结束之前不会超出范围)


我看不出您的代码有什么立即的错误,但当它全部变成递归和指针y时,我通常看不到。在我看来,这是一个调试会话的机会。启动您最喜欢的调试器,单步执行代码,保持对指针和内存使用的清晰关注。如果您还没有最喜欢的调试器,现在是了解它的好时机。启用编译器的所有检查。Gfortran:
-g-fbacktrace-fcheck=all-Wall
,ifort:
-g-fbacktrace-check-warn
。你的
-fbounds检查
还不够。感谢你的回答,我将用调试器的详细信息编辑我的帖子。+1感谢你有足够的勇气在Fortran中尝试此操作。它解决了问题,谢谢!但是,我很难理解为什么在前两次插入之后输出是正确的。在第一次插入之后,这两个叶子不应该已经无效了吗?另外,为什么我能够在调试器中“手动”访问所有节点?关于前两个插入的“正确性”,我猜原始叶子的内容还没有被覆盖,仍然可读。因此,没有报告错误(尽管,
valgrind
抱怨未初始化值…)。关于调试器:我真的不知道,但我的经验告诉我,调试器有时可能会改变程序的行为,从而导致程序完美运行。
recursive subroutine inserttree(t,n)
  implicit none
  type (tree), intent (INOUT) :: t
  integer, intent (IN) :: n

  if (t % isleaf) then
    allocate(t%left)
    allocate(t%right)
    call newleaf(t%left,n)
    call newleaf(t%right,n)

    t % isleaf = .false.
  else
    call inserttree(t % left,n)
  endif
end subroutine inserttree