【问题标题】:Fortran 90 - segmentation faultFortran 90 - 分段错误
【发布时间】:2012-10-08 06:41:32
【问题描述】:

我正在制作一个将十进制整数转换为其二进制表示的程序。这是我的代码:

program test
implicit none
integer, dimension(:), allocatable :: binary
integer :: decimalnum, i, initvalue

print*, "Enter decimal number to convert: "
read*,initvalue

decimalnum = initvalue
i = 0

do while (decimalnum > 0)
    if (MOD(decimalnum,2)==0) then
        binary(i) = 0                  ! this is as far as the program executes up to 
        decimalnum = decimalnum / 2
        i = i + 1
    else if (MOD(decimalnum,2)==1) then
        binary(i) = 1
        decimalnum = (decimalnum -1) / 2
        i = i + 1
    end if
end do
end program test

在标记点,它返回错误Segmentation fault并以代码139退出。

为什么会这样?

提前致谢。

【问题讨论】:

  • 您可以访问debug 吗?这样的gdb
  • 不,我不知道怎么用。
  • 我对 Fortran 了解不多,但我只是查找了“可分配”,看起来您仍然需要“分配”语句来实际保留空间。
  • 我将该行更改为allocate(binary(i) = 0),但编译器返回syntax error in allocate statement
  • 另外:数组从索引 1 开始。

标签: fortran fortran90


【解决方案1】:

以下是将整数i 转换为其二进制表示的简单方法:

write(*,'(b16)') i

正如所写,这不会写任何前导0s。如果你想要领先的0s,试试

write(*,'(b16.16)') i

当然,前面的代码将二进制表示写入默认输出单元,但使用 Fortran 的内部写入功能,我可以轻松地将位写入字符变量。例如:

character(len=16) :: bits
...
write(bits,'(b16.16)') i

将i 的二进制数字写入字符变量bits。

现在,如果你真正想要的是创建一个整数数组,每个整数代表二进制表示的一位,那么像这样

integer, dimension(16) :: bitarray
...
bitarray = 0
...
do ix = 1,len(bits)
    if (bits(ix:ix)=='1') bitarray(ix) = 1
end do

可能会起作用。

【讨论】:

    【解决方案2】:

    1) 发生崩溃是因为您只为数组 binary(:) 分配了 1 个元素,而 While 循环可能已移至 i = 2,此时您的数组的索引超出范围(崩溃)。

    2) Fortran 有许多直接处理位的内在函数。例如,

    a) Bit_Size(var) 返回“var”中的位数,所以如果你必须使用可分配的,现在你可以提前知道所需的数组大小。

    b) BTest(iVar, pos) 返回 .True。如果 iVar 中 pos 的位是 1

    例如,使用上面的其他声明:

    Integer     :: n
    
    n = Bit_Size(decimalnum)    
    
    If( Allocated(Binary) ) DeAllocate(Binary)          ! good practice
    Allocate( Binary(1:n) )                             ! in general, should get IOStat, just in case
    
    Binary(:) = 0
    
    ForAll(i=1:n, Mask = BTest( decimalnum, i-1 ) ) ! remember, bit intrinsics index from 0
        Binary(i) = 1
    End ForAll
    

    ...与 Do 和 While 相比,这更有效,并且可能有助于(一点)实现 smp。也可以使用 Where/EndWhere 构造,但我发现 ForAll 的效率更高。

    c) IBits(var, pos, len) 从 var 中提取从 pos 开始的 len 个位数的位,例如,如果您想创建“显式”二进制表示,这可能是一种方法。

    等等等等

    3)如果您“真的是说”将 DecimalNum 转换为 Bin,那么如果 Dec 还包括浮点 Dec(即 Reals),那么您会遇到(大量)其他问题,因为 Reals 的位表示是指数形式的。我会假设情况并非如此,因为它的代码/解释要复杂得多。

    最后,在 Fortran 中,Nums 通常是“有符号的”Nums,并且前导位用于确定 +ve (0) 或 -ve (1)。因此,如果您要朝着“其他”方向(Bin2Dec)前进,那么您会更喜欢一个额外的 arg(也许是可选的)来控制结果是有符号还是无符号。如果无符号,则输出 var 与输入 var 相比需要“更大”(例如,如果将无符号 1 字节 int 转换为 Fortran int,则必须使用至少 2 字节 int(即输入 Integer(1) 必须是输出到 Integer(2)) 等。

    【讨论】:

      【解决方案3】:

      根据 cmets,在定义 binary 数组之前,您需要执行 allocate 语句(或在幕后为您分配的东西)。分配语句的最简单形式类似于ALLOCATE(binary(10)),它将为binary 数组提供10 个元素,使用默认的(可以使用分配语句更改该数组)起始数组索引1。

      在使用数组之前很难知道分配的大小,有两种基本方法:

      • 执行两遍,第一遍简单计算需要多少元素,然后分配数组,然后第二遍实际分配相关元素。
      • 将数组分配为初始大小(可能为零),并根据需要逐步增大数组。

      在决定使用方法时需要权衡取舍,这与分配和计数时每个测试的评估等相对开销相关。

      在 Fortran 90 中(至少是 Fortran 95 的时候了!),增长一个可分配的数组有些复杂(分配一个临时的,将数据从原始复制到临时,解除分配原始,将原始分配到新的大小,复制数据从临时回到调整大小的原始,临时解除分配)。在 Fortran 2003 中,此操作变得微不足道。

      【讨论】:

      • @AussieGamer:您将很难找到一个能够接受该语言 2008 年修订版添加的大部分附加语法的编译器;现在市场上的大多数编译器都实现了 Fortran 2003 的大部分内容。
      【解决方案4】:

      所以这可能是一种糟糕的形式,而且运行时肯定很糟糕(它会为每一位复制数组),但这就是我想出的。它似乎有效。

        program test
            implicit none
            integer, dimension(:), allocatable :: binary
            integer :: decimalnum, i, initvalue, curSize, curBit
      
      
            print*, "Enter decimal number to convert: "
            read*,initvalue
      
            decimalnum = initvalue
            i = 1
            ALLOCATE ( binary(1) )
            curSize = 1
      
            DO WHILE (decimalnum > 0)
              IF (i > curSize ) THEN
                  curSize = curSize * 2
                  CALL expandArray( curSize, i-1 )
              END IF
      
              IF (MOD(decimalnum,2)==0) then
                  binary(i) = 0                  ! this is as far as the program executes up to 
                  decimalnum = decimalnum / 2
                  i = i + 1
              ELSE IF (MOD(decimalnum,2)==1) then
                  binary(i) = 1
                  decimalnum = (decimalnum -1) / 2
                  i = i + 1
              END IF
      
            end do
            PRINT*, binary
      
      
        CONTAINS
            SUBROUTINE expandArray( newSize, oldSize )
                IMPLICIT NONE
                INTEGER, DIMENSION(:), ALLOCATABLE :: temp
                INTEGER :: j, newSize, oldSize
                ALLOCATE( temp(newSize) )
                DO j=1,oldSize
                    temp(j) = binary(j)
                END DO
                DEALLOCATE (binary)
                ALLOCATE( binary(newSize) )
                DO j=1,oldSize
                    binary(j) = temp(j)
                END DO
                DO j=oldSize+1,newSize
                    binary(j) = 0
                END DO
                DEALLOCATE (temp)
            END SUBROUTINE
      
        END PROGRAM test
      

      【讨论】:

      • 注意:刚刚编辑过——我错误地将二进制作为 expandArray 的参数,而不是全局变量。出于某种原因,它仍然有效。无论如何,它现在已经修复了。
      • 我同意作者的观点,这段代码可能很糟糕,它会为每一位复制数组。它使用了 FORTRAN77 的功能(特别是 continue 语句),这些功能已被更好的设施所取代。
      • @HighPerformanceMark 尝试对其进行修饰——更好的数组重新分配,摆脱执行/继续。显然,我仍然不是 Fortran 程序员,因此非常感谢专业人士的编辑/反馈。
      猜你喜欢
      • 1970-01-01
      • 2020-03-19
      • 2014-02-24
      • 1970-01-01
      • 2015-08-07
      • 2013-04-20
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      相关资源
      最近更新 更多