为什么我的程序循环两次?

时间:2016-06-21 22:29:14

标签: fortran gfortran

我尝试在Fortran中创建一个toUpper和toLower函数,它们似乎在我的实现中工作正常;程序会提示用户输入一个输入字符串,然后以大写字母然后以小写形式输出。

但是,在我添加了一个大写功能之后,程序会提示用户输入两次输入字符串,我无法找出原因。它似乎有效,但它做了两次。

有人可以如此友善地告诉我为什么我的程序会提示输入两次吗?代码和输出如下。

program stringutils
    character :: words*1000

    print*, 'Enter a string.'
    read(*,'(A)') words

    call toUpper(words)
    print*, 'toUpper: ', trim(words)

    call toLower(words)
    print*, 'toLower: ', trim(words)

    call capitalize(words)
    print*, 'capitalize: ', trim(words)

end program stringutils

subroutine toUpper(string)
    character :: string*1000
    integer :: i, charNum

    do i = 1, len(trim(string))
        charNum = iachar(string(i:i))
        if (charNum >= 97 .and. charNum <= 122) then
            string(i:i) = achar(charNum - 32)
        end if
    end do
end subroutine toUpper

subroutine toLower(string)
    character :: string*1000
    integer :: i, charNum

    do i = 1, len(trim(string))
        charNum = iachar(string(i:i))
        if (charNum >= 65 .and. charNum <= 90) then
            string(i:i) = achar(charNum + 32)
        end if
    end do
end subroutine toLower

subroutine capitalize(string)
    character :: string*1000
    integer :: i

    call toUpper(string(1:1))
    do i = 2, len(trim(string))
        if (iachar(string(i-1:i-1))==32) then
            call toUpper(string(i:i))
        else
            call toLower(string(i:i))
        end if
    end do
end subroutine capitalize

示例输出:

 Enter a string.
this IS a tEsT!
 toUpper: THIS IS A TEST!
 toLower: this is a test!
 capitalize: This Is A Test!
 Enter a string.
why is this running a second time?
 toUpper: WHY IS THIS RUNNING A SECOND TIME?
 toLower: why is this running a second time?
 capitalize: Why Is This Running A Second Time?

我在Windows中通过MinGW使用gfortran。

3 个答案:

答案 0 :(得分:1)

这是一个扩展的评论而不是答案......

我对您最初报告的问题原因的解释并不相信。我无法理解为什么程序会因为错误指定的例行程序而执行两次。尽管如此,通过对现代Fortran实践的更多关注,你的日常工作将得到很大改善。

你已经写了一堆例程来操作1000个字符的字符串。更可靠的是将它们编写为操作字符串,无论字符串传递的字符串是什么长度。现代Fortran(因为当我不确定时,至少90,可能在此之前)允许传递假定长度的字符参数,而不是这样:

subroutine toUpper(string)
    character(*) :: string
    integer :: i, charNum

    do i = 1, len(trim(string))
        charNum = iachar(string(i:i))
        if (charNum >= 97 .and. charNum <= 122) then
            string(i:i) = achar(charNum - 32)
        end if
    end do
end subroutine toUpper

现在,当调用例程时,它将对string中的所有字符进行操作,不多也不少。

我也建议进行其他一些修改。我没有时间详细介绍它们,但如果你在这里搜索,你会发现几个Q和As主题。这是一套入门套装:

  • 始终使用程序实体的显式声明,在程序(和模块)中包含implicit none。这是编写安全程序的首要原则。
  • 将您的惯例放入moduleuse。这可以确保编译器能够并确实检查您对它们进行的调用是否在语法上有效。
  • 我个人将subroutines全部放入functions并返回一个输入字符串的版本(上面(或下面))。让子程序修改输入字符串没有任何问题,但是编写函数更容易,例如,有一天你可能会想写HumpyString = capitalise(toLower(string))
  • 之类的东西。

答案 1 :(得分:1)

我在Linux中使用Gfortran编译了代码。 我经历了以下几点: 你的&#34;循环两次&#34;问题确实发生了,但不是每次都适合我。我有一个相当随机的分段错误。 我试着回答:

您认为正确,问题来自capitalize子例程。我无法使用gdb调试您的代码,但是seg错误似乎表明您试图访问未声明的字符串组件或类似的组件以及您正在调用toUpper和{ {1}}循环播放。那是我找到的东西:

在循环中,您可以通过发送toLower来发送toUppertoLower,这是一个唯一字符(长度为1)。但是在string(i:i)中,您声明了一个长度为1000的字符,而在此例程中,您只处理toUpper,即长度为1的字符,这不符合逻辑。通过将string(i:i)替换为character :: string*1000,它似乎正在工作并且更有意义,因为此子例程每次需要转换一个字符。

我无法解释为什么你的代码崩溃或循环两次但是由于你的字符长度溢出可能会出现某种内存泄漏......

无论如何,@High Performance Mark为您提供了关于假设长度字符参数的好建议。如果编码方式通过模块和接口执行参数检查,编译器可能已经能够警告您。也许一个character :: string*1条款是明智的。这些是Fortran 90的强大功能。

答案 2 :(得分:0)

虽然我不确定是什么导致它循环两次,但我知道它是大写函数的东西,也许我是如何将一个char发送到期望1000个字符的函数。所以我添加了charToUpper和charToLower函数,它现在按预期工作。这是新代码。我觉得大写功能可能更好......评论欢迎。

program stringutils
    character words*1000

    print*, 'Enter a string.'
    read(*,'(A)') words

    call toUpper(words)
    print*, 'toUpper: ', trim(words)

    call toLower(words)
    print*, 'toLower: ', trim(words)

    call capitalize(words)
    print*, 'capitalize: ', trim(words)

end program stringutils

subroutine toUpper(string)
    character string*1000
    integer i

    do i = 1, len(trim(string))
        call charToUpper(string(i:i))
    end do
end subroutine toUpper

subroutine toLower(string)
    character string*1000
    integer i

    do i = 1, len(trim(string))
        call charToLower(string(i:i))
    end do
end subroutine toLower

subroutine charToUpper(c)
    character c
    integer charNum

    charNum = iachar(c)
    if (charNum >= 97 .and. charNum <= 122) then
        c = achar(charNum - 32)
    end if
end subroutine charToUpper

subroutine charToLower(c)
    character c
    integer charNum

    charNum = iachar(c)
    if (charNum >= 65 .and. charNum <= 90) then
        c = achar(charNum + 32)
    end if
end subroutine charToLower

subroutine capitalize(string)
    character :: string*1000
    integer :: i

    call charToUpper(string(1:1))
    do i = 2, len(trim(string))
        if (iachar(string(i-1:i-1))==32) then
            call charToUpper(string(i:i))
        else
            call charToLower(string(i:i))
        end if
    end do
end subroutine capitalize