gfortran 和 ifort 之间的编译器差异(可分配数组和全局变量)
Compiler difference between gfortran and ifort (allocatable arrays and global variables)
代码
下面的MWE描述了我想要使用的东西(注意,我没有设计这个,我只是想使用别人的代码,我通常不会使用全局变量)。
PROGRAM MAIN
IMPLICIT NONE
integer :: N
real(8), allocatable :: a(:,:)
N=3
allocate(a(N,3))
a=initialize_array()
CONTAINS
function initialize_array() result(a)
IMPLICIT NONE
real(8) :: a(N,3)
a=1
end function initialize_array
END PROGRAM MAIN
问题
gfortran 给出一个错误,显示为 Error: Variable 'n' cannot appear in the expression at (1)
,指向函数内部的 real(8) :: a(N,3)
。
在子程序中它会工作,那么这里可能是什么问题?
问题
为什么 ifort (v. 15.0.3) 会编译它,而 gfortran (v. 4.8.4) 不会?
我认为这可能是一种解释,尽管像@VladimirF 我实际上无法回忆或找到标准的相关部分(如果有的话)。
这一行
real(8) :: a(N,3)
声明函数的结果是一个名为 a
的数组。这掩盖了通过 主机关联 引用数组 a
的相同名称的可能性。函数范围内的a
不是程序范围内的a
。
维数取决于变量值的数组的声明,例如 a(N,3)
,要求变量的值在编译时已知(或至少可知)。在这种情况下,在主机范围内提供 n
属性 parameter
可以解决问题。虽然它不能解决糟糕的设计——但 OP 的手似乎在这一点上束手无策。
英特尔编译器编译这个并不让我感到惊讶,为了向后兼容,它编译了它的祖先多年来编译的各种奇怪的东西。
我只提供这个半生不熟的解释,因为经验告诉我,一旦我这样做,一位真正的 Fortran 专家(IanH、francescalus、(通常)VladimirF)就会非常愤怒 post 更正,我们都会学到一些东西。
正如其他人所评论的那样,很难说某些事情是否明确允许:该语言主要基于规则和限制条件。
所以,我不会证明代码没有错误(并且不允许gfortran拒绝它),但让我们看看是怎么回事。
首先,我反对 给出的一件事,因为这有点相关:
The declaration of an array with a dimension dependent on the value of a variable, such as a(N,3)
, requires that the value of the variable be known (or at least knowable) at compile time.
显式形状数组的边界不需要总是由 常量表达式给出(我们松散地定义为 "known/knowable at compile time"):在某些情况下显式形状数组可以有由变量给出的界限。这些被称为自动对象(以及规范表达式给出的界限)。
函数结果就是这样一个允许自动对象的地方。在函数结果声明的问题示例中,N
与主机关联并形成规范表达式。
与其用尽所有其他约束来查看 a
的声明是否真的被允许,不如让我们看看 gfortran 如何响应程序的小修改。
首先,gfortran 反对的问题代码的精简版本。
integer n
contains
function f() result(g)
real g(n)
end function f
end program
f
的函数结果名称为 g
。我们如何称呼函数结果并不重要,那么当我们称它为 f
?
时会发生什么
integer n
contains
function f()
real f(n)
end function f
end program
这对我来说很愉快。
如果我们在模块而不是主程序中构建第一个块会怎样?
module mod
integer n
contains
function f() result(g)
real g(n)
end function f
end module
这也编译。
自然的结论:即使 gfortran 拒绝第一个代码是正确的(我们错过了一些隐藏得很好的约束),它要么在不拒绝其他代码方面非常不一致,要么约束真的很奇怪。
代码
下面的MWE描述了我想要使用的东西(注意,我没有设计这个,我只是想使用别人的代码,我通常不会使用全局变量)。
PROGRAM MAIN
IMPLICIT NONE
integer :: N
real(8), allocatable :: a(:,:)
N=3
allocate(a(N,3))
a=initialize_array()
CONTAINS
function initialize_array() result(a)
IMPLICIT NONE
real(8) :: a(N,3)
a=1
end function initialize_array
END PROGRAM MAIN
问题
gfortran 给出一个错误,显示为 Error: Variable 'n' cannot appear in the expression at (1)
,指向函数内部的 real(8) :: a(N,3)
。
在子程序中它会工作,那么这里可能是什么问题?
问题
为什么 ifort (v. 15.0.3) 会编译它,而 gfortran (v. 4.8.4) 不会?
我认为这可能是一种解释,尽管像@VladimirF 我实际上无法回忆或找到标准的相关部分(如果有的话)。
这一行
real(8) :: a(N,3)
声明函数的结果是一个名为 a
的数组。这掩盖了通过 主机关联 引用数组 a
的相同名称的可能性。函数范围内的a
不是程序范围内的a
。
维数取决于变量值的数组的声明,例如 a(N,3)
,要求变量的值在编译时已知(或至少可知)。在这种情况下,在主机范围内提供 n
属性 parameter
可以解决问题。虽然它不能解决糟糕的设计——但 OP 的手似乎在这一点上束手无策。
英特尔编译器编译这个并不让我感到惊讶,为了向后兼容,它编译了它的祖先多年来编译的各种奇怪的东西。
我只提供这个半生不熟的解释,因为经验告诉我,一旦我这样做,一位真正的 Fortran 专家(IanH、francescalus、(通常)VladimirF)就会非常愤怒 post 更正,我们都会学到一些东西。
正如其他人所评论的那样,很难说某些事情是否明确允许:该语言主要基于规则和限制条件。
所以,我不会证明代码没有错误(并且不允许gfortran拒绝它),但让我们看看是怎么回事。
首先,我反对
The declaration of an array with a dimension dependent on the value of a variable, such as
a(N,3)
, requires that the value of the variable be known (or at least knowable) at compile time.
显式形状数组的边界不需要总是由 常量表达式给出(我们松散地定义为 "known/knowable at compile time"):在某些情况下显式形状数组可以有由变量给出的界限。这些被称为自动对象(以及规范表达式给出的界限)。
函数结果就是这样一个允许自动对象的地方。在函数结果声明的问题示例中,N
与主机关联并形成规范表达式。
与其用尽所有其他约束来查看 a
的声明是否真的被允许,不如让我们看看 gfortran 如何响应程序的小修改。
首先,gfortran 反对的问题代码的精简版本。
integer n
contains
function f() result(g)
real g(n)
end function f
end program
f
的函数结果名称为 g
。我们如何称呼函数结果并不重要,那么当我们称它为 f
?
integer n
contains
function f()
real f(n)
end function f
end program
这对我来说很愉快。
如果我们在模块而不是主程序中构建第一个块会怎样?
module mod
integer n
contains
function f() result(g)
real g(n)
end function f
end module
这也编译。
自然的结论:即使 gfortran 拒绝第一个代码是正确的(我们错过了一些隐藏得很好的约束),它要么在不拒绝其他代码方面非常不一致,要么约束真的很奇怪。