运行时错误,是源代码正确地编译链接后,在执行阶段遇到的错误。几乎没有任何程序可以完全的避免运行时错误。它可能发生在某些意想不到的情况下。比如:磁盘满了,内存不够了,文件被只读保护了,杀毒软件拦截了等等。
当然,做为编程人员在没有正式确定代码合格之前,出现运行时错误,最大的可能,还是代码写得有问题。
第二. Intel Fortran 编译后发生运行时错误是什么样子?
一般来说,IVF发生运行时错误以后,像下面这个样子一样:
大图
通常来说,Visual C++ Runtime Library 这个对话框上的信息对我们没有帮助。这是由于 IVF 的一些运行时库使用了 VC++ 的。咱们可以忽略它。(上图截图也没截完整)
对我们有用的信息,一般在 console 窗口(windows默认为黑色)里显示。
第三. 从运行时错误窗口我们能获得什么信息?
首先我们来看一段代码,它分为两个源代码文件:
main.f90:
program www_fcode_cn implicit none call root_call() end program www_fcode_cn
sub.f90:
subroutine root_call() call step1_call() End Subroutine root_call subroutine step1_call() call step2_call() End Subroutine step1_call subroutine step2_call() integer :: i read(*,*) i End Subroutine step2_call
其中 main.f90 书写了主程序,而 sub.f90 书写了若干子程序。在 main 调用了 root_call,然后再调用了 step1_call,最后调用 step2_call,这个函数读入一个整型变量。
当我们运行后,输入一个字母,比如 a,由于 a 无法转换为整型,所以会触发运行时错误。(如上图)
这里也可以看到,代码本身没有问题,但会出现运行时错误。这就是对程序的不恰当使用导致的。(当然我们也可以在代码中予以判断,但常规来说没有这个必要)
上述代码运行后,若输入字母,则会出现运行时错误:
forrtl: severe (59): list-directed I/O syntax error, unit -4, file CONIN$
Image PC Routine Line Source
console1.exe 01342233 Unknown Unknown Unknown
console1.exe 013410C0 _STEP2_CALL 11 sub.f90
console1.exe 01341104 _STEP1_CALL 6 sub.f90
console1.exe 01341144 _ROOT_CALL 2 sub.f90
console1.exe 01341031 _MAIN__ 3 main.f90
console1.exe 0139F9D2 Unknown Unknown Unknown
console1.exe 013A031A Unknown Unknown Unknown
console1.exe 013A046D Unknown Unknown Unknown
kernel32.dll 771FED5C Unknown Unknown Unknown
ntdll.dll 772F37EB Unknown Unknown Unknown
ntdll.dll 772F37BE Unknown Unknown Unknown
这里大致介绍一下这些信息的含义。
forrtl: severe (59): list-directed I/O syntax error, unit -4, file CONIN$
这是判断运行时错误最主要的信息,翻译一下就知道它的含义了。表示默认格式的输入输出错误。发生在 CONIN$ 通道,在 IVF 里,Console Input(屏幕输入)被称为 CONIN$,类似的 CONOUT$ 表示屏幕输出。
(其他运行时错误,可参考本站的 FAQ之常见错误其中有详细的介绍各种运行时错误。)
Image PC Routine Line Source
这句以下是一个表格,它表示调用堆栈。所谓调用堆栈,就是各层次函数的调用顺序。注意它显示的顺序是反的。
它表示表格里显示在最后一行的函数,调用了倒数第二行显示的函数,然后倒数第二行的函数调用了倒数第三行的函数......最后调用了第一行显示的函数。
表格头部的含义:
Image 表示调用发生在哪个目标文件里。console1.exe 就是我们自己的程序。ntdll.dll 和 kernel32.dll 是 windows 的。如果发现有 imsl_dll.dll 或者 mkl_dll.dll 一类的,就说明错误发生在函数库里。
PC 表示调用发生的内存地址(十六进制显示)。它通常对我们没有什么意义。但一般情况下可认为,大于 60000000 的,是windows系统所在的范围。小于 60000000 的,是我们书写的代码范围。
Routine 表示调用的函数的名称。通常我们只能看到我们自己书写的代码里的名称(可能会被加上下划线之类的),而系统或函数库中的,则显示为 unknown
Line 表示调用时的位置对应于源代码中的行数。同上,也只能看到我们自己书写的代码的范围。其他显示为 unknown
Source 表示调用时的位置对应于源代码的文件名。unknown 同上。
由上图我们可知,错误发生之前的一些事情:
1.首先,ntdll.dll 开始了调用。(这是windows用于加载我们的 console1.exe)这部分很神秘,所以routine,Line,Source都看不见。(^_^除非你有windows的源码)
2.然后,ntdll.dll 调用了 kernel32.dll ,这也是windows用于加载和初始化 console1.exe 的内存映射。很神秘,同上。前两个步骤发生的地址(PC)都很大,说明他们在 windows 的领空内。
3.之后呢,windows调用了我们的程序,但一开始我们依然看不到 routine , line 和 source,因为这个时候,我们的主程序还没有被调入。这个阶段执行的,是 Intel Fortran 为我们准备的运行时库(入口部分)。这部分用于初始化有关 fortran 语法的东西,比如接受命令行参数等等。这部分我们也没有源代码,因此 routine , line , source 都显示为 unknown。从这个步骤开始,发生的内存地址(PC)变低了,已进入我们的程序领空。
4.Intel Fortran 的运行时库准备就绪,调用了我们书写的主程序(routine 为 _MAIN__ ),这个调用位于 main.f90 的 第 3 行(call root_call())
5._ROOT_CALL 又调用了 _SETP1_CALL , 发生在 sub.f90 的第 2 行。
6._SETP1_CALL 又调用了 _SETP2_CALL,发生在 sub.f90 的第 6 行。
7._SETP2_CALL 又调用了 未知的函数(unknown),发生在 sub.f90 的第 11 行。
8.未知函数在未知的位置发生了错误。
由源代码 11 行可知,此处使用了 read(*,*) i 语句,这便是上面第7和第8步骤里的未知函数。read 在源代码里虽然是一个语法规定的语句,但编译后,则是一个函数(位于运行时库中),我们无法看到 read 的源代码。
由此,可认定,sub.f90 的第 11 行,read 语句执行出错,错误原因是:list-directed I/O syntax error, unit -4, file CONIN$
调用堆栈,可用于我们排查错误发生的位置。尤其是对于同一个函数,在源代码中多个位置被调用,我们可根据调用堆栈来判断是发生在哪个位置的调用出现了问题。
第四. 补充说明
几点补充:
1.调用堆栈不会显示得很多。如果调用层次太多了,可能只会显示最后几层。
2.如果大量的显示为 unknown,检查一下编译设置,是否为 Debug(而不是 Release),是否开启 Debug 信息等。
3.这里不一定能找出全部错误的位置来,有些问题,还需要进行 Debug 单步调试。