愤怒的三炮 发表于 2023-8-6 19:18:39

编译问题

主函数和function写在同一文件中,参数类型和数量都是错的,但编译还是通过了,结果也算出来了。

program main
    implicit none

    integer:: a=0, b=100
    integer:: func
    print*, func(a,b)

end

function func(arg)
    implicit none
    real,intent(in)::arg
    integer:: func
    func = arg + 1
end

编译器只是给出了警告,但还是能编译成功的。
我知道这是隐式接口的问题,但我不清楚具体发生了什么。请问:
1. 将 function 写在 program 的前面和写在 program 的后面有什么区别?
2. 将 function 和 program 写在不同的文件中编译和写在同一文件中编译有什么区别?
3. Fortran 的编译逻辑和 C 语言有什么不同吗?

-- gcc version 8.1.0

fcode 发表于 2023-8-6 20:06:56

这里面涉及到一些未定义行为。所以不同编译器可能表现不同。
我个人是不喜欢研究这些未定义行为的。
按规范的写,就可以了。不规范会有什么影响,我并不喜欢深究。

此外,我现在所有可执行代码,都放在module或主程序里。已经不再使用外部子程序。

function 写在前面,可能利于某些编译器自动生成接口并检查接口是否一致(这句话并不是说编译器一定会去做接口检查)。让编译器给出警告或者直接拒绝。
其实一部分编译器就算你写在后面,也一样可以检查接口。有些编译器还可以设置,是否检查接口。

写在不同的文件里(在这个问题上),一般和写在后面是没什么区别的。

第三个问题太复杂了,没点时间专门去分析编译器源码并归纳总结,是回答不了的。

fcode 发表于 2023-8-6 20:22:03

不管是做为程序员,还是编译器的开发者。
都想尽了一切办法,让错误提前。
所谓错误提前,就是让错误尽可能的在以下4个阶段里更早的阶段被发现:

编译错误,链接错误,运行时错误,逻辑错误(运行结果不符合预期)

如果接口不一致,编译器也不做检查,那么错误就会是运行时错误,如果运行时库也不检查,那就是运行崩溃。
所以优秀的编译器才会在编译链接时主动的去检查接口。
但做为程序员,不能一味的指望编译器来发现。万一编译器不发现呢?

在你的案例中,既然你的编译器给你了警告,我想,实际它在处理的时候,应该是主动为你考虑并填补了。
例如,如果编译器什么都不处理,调用者压入2个参数的堆栈。但被调用者只使用了1个,并且清理堆栈顶的时候按1个去清理,那么会产生堆栈失衡,导致运行崩溃。
如果你的程序能正常运行,可能是你的编译器自动为你做了处理,例如舍弃了第二个参数未压入堆栈。
另一个可能的情况是,你使用的是64位编译器,用的 x64 fast call 调用协定。那么2个参数是放入寄存器的,多放了一个寄存器也没关系。不需要清理堆栈,也不会造成失衡。所以程序能够运行。

不管怎么样,能够运行只是偶然的。去研究为什么会偶然,去研究到底编译器中间做了什么非语法严格强制规定的措施?对一个普通的 Fortran 程序员,没有什么价值。

愤怒的三炮 发表于 2023-8-6 21:09:21

fcode 发表于 2023-8-6 20:22
不管是做为程序员,还是编译器的开发者。
都想尽了一切办法,让错误提前。
所谓错误提前,就是让错误尽可能 ...

感谢感谢!
以前用Matlab和C比较多。看见有的fortran函数是用内部过程,又看见有些代码是分开写的,比较混乱。
谢谢刘涛详细的解答

fcode 发表于 2023-8-7 08:44:20

把可以放入module的函数都放入module,就会强制检查接口是否一致(这是语法明确规定的)。
这样,你就不必用这个问题而纠结了。并且也符合现代 Fortran 代码的形式(说白了就是优雅)。
页: [1]
查看完整版本: 编译问题