我正在尝试编写一个简单的模块来处理算术运算中的物理单元。我的目的是在主要单位的基础上创建派生单位。
如下面的代码所示,我有一个派生类型unit_t
,它存储一个字符串,该字符串表示单位本身,单位的幂,转换因子(将其转换为SI),一个逻辑变量,用于显示是否克隆了该单元以及指向下一个或上一个单元的next
和prev
指针(如果我们有多个单元组合,例如kg * m / s**2
,则基本上是一个链接列表,将不同的单元相互连接。
我有一个名为unit_clone
的函数可以克隆一个主单元。 unit_int_pow
函数使指数运算符(**)重载,并且它只是克隆给定的基本单位并更新其指数。 units_mul
函数使乘法运算符(*)重载。该函数首先检查两个给定的单元是否被克隆(如果不是,它将克隆它们),然后仅使用next
和``prev`''指针将它们连接。
这是我的代码(您应该可以使用gfortran
进行编译)
module units
implicit none
type unit_t
character(len=16) :: symb
integer :: pow
real :: conv
logical :: cloned
type(unit_t), pointer :: next => null(), prev => null()
end type unit_t
! definitions
type(unit_t), target :: m = unit_t("m", 1, 1.d0, .false.)
type(unit_t), target :: km = unit_t("km", 1, 1.d3, .false.)
type(unit_t), target :: kg = unit_t("kg", 1, 1.d0, .false.)
type(unit_t), target :: s = unit_t("s", 1, 1.d0, .false.)
interface operator (**)
procedure unit_int_pow
end interface operator (**)
interface operator (*)
procedure units_mul
end interface operator (*)
contains
!> Cloning a given node (unit)
function unit_clone(u) result (clone)
implicit none
type(unit_t), intent(in) :: u
type(unit_t), allocatable, target :: clone
allocate(clone)
clone%symb = u%symb
clone%conv = u%conv
clone%pow = u%pow
clone%cloned = .true.
clone%next => u%next
clone%prev => u%prev
end function unit_clone
!> integer powers
function unit_int_pow(u1, p) result(u)
implicit none
type(unit_t), intent(in) :: u1
integer, intent(in) :: p
type(unit_t), allocatable, target :: u
u = unit_clone(u1)
u%pow = u%pow * p
end function unit_int_pow
!> multiplication
function units_mul (u1, u2) result (u1c)
implicit none
type(unit_t), intent(in) :: u1, u2
type(unit_t), allocatable, target :: u1c, u2c
if ( u1%cloned ) then
u1c = u1
else
u1c = unit_clone(u1)
end if
if ( u2%cloned ) then
u2c = u2
else
u2c = unit_clone(u2)
end if
u2c%prev => u1c
u1c%next => u2c
end function units_mul
end module units
program test
use units
implicit none
type(unit_t) :: u
u = kg**2 * m
print *, u%symb, "^", u%pow, " [expected: kg^2]"
print *, u%next%symb, "^", u%next%pow, " [expected: m^1]"
print *, u%next%prev%symb, "^", u%next%prev%pow, " [expected: kg^2]"
end program test
问题是,我得到以下输出:
kg ^ 2 [expected: kg^2]
�ȷ2�U ^ 1 [expected: m^1]
�ȷ2�U ^ 2 [expected: kg^2]
很显然,在访问next
或next%prev
单元(基本上是此短链表的开头)之后,代码将输出随机字符而不是symb
。如果更改派生类型unit_t
中变量的顺序,例如,如果在派生类型的末尾放置symb
,我将得到正确的symb
,但这错误的pow
秒。
任何想法都是这种相当奇怪的行为的罪魁祸首吗?
使用下面的Rudrigo的注释,我重写了代码,现在可以正常工作了。仅供参考,工作代码如下(如果您有其他建议或修改,请告诉我,Nombre respository)
module units
implicit none
type unit_t
character(len=16) :: symb
real :: conv
real :: pow = 1.d0
logical :: cloned = .false.
type(unit_t), pointer :: next => null(), prev => null()
end type unit_t
! units definitions
type(unit_t), target :: m = unit_t("m", 1.d0)
type(unit_t), target :: km = unit_t("km", 1.d3)
type(unit_t), target :: kg = unit_t("kg", 1.d0)
type(unit_t), target :: s = unit_t("s", 1.d0)
interface operator (**)
procedure unit_int_pow
end interface operator (**)
interface operator (*)
procedure units_mul
end interface operator (*)
contains
!> Cloning a given node (unit)
function unit_clone(u) result (clone)
implicit none
type(unit_t), intent(in) :: u
type(unit_t), pointer :: clone
allocate(clone)
clone%symb = trim(u%symb)
clone%conv = u%conv
clone%pow = u%pow
clone%cloned = .true.
clone%next => u%next
clone%prev => u%prev
end function unit_clone
!> integer powers
function unit_int_pow(u1, p) result(u)
implicit none
type(unit_t), intent(in) :: u1
integer, intent(in) :: p
type(unit_t), pointer :: u
if ( u1%cloned ) then
! TODO: should be able to handle complex cases like: a * (b * c)**3
! most likly, only updating the power of the linked list chain
! would do the job
else
u => unit_clone(u1)
end if
u%pow = u%pow * p
end function unit_int_pow
!> multiplication
function units_mul (u1, u2) result (u2c)
implicit none
type(unit_t), intent(in), target :: u1, u2
type(unit_t), pointer :: u2c
if ( u2%cloned ) then
if ( associated(u2%prev) ) then
u2c => u2%prev%next
else
u2c => u2
end if
else
u2c => unit_clone(u2)
end if
if ( u1%cloned ) then
if ( associated(u2%prev) ) then
u2c%prev => u1%prev%next
else
u2c%prev => u1
end if
else
u2c%prev => unit_clone(u1)
end if
u2c%prev%next => u2c
end function units_mul
end module units
答案 0 :(得分:1)
Fortran中的pointer
具有三种可能的关联状态:
target
); deallocate
。当子程序实例的执行完成时(例如,function units_mul
到达end function
时),所有未保存的局部变量都将变为未定义。此外,所有未保存的allocatable
局部变量或函数结果都将被释放,并且当可分配的实体被释放时,它也将变得不确定。
回到问题所在,u2c
是units_mul
函数中可分配的,未保存的局部变量,您可以在其中将u1c%next
与其关联。当此函数结束时,u2c
结束其生命周期并变为未定义状态,从而使u1c%next
也变为未定义状态,在Fortran语言中称为悬挂指针。 / p>
这是来自Fortran标准的文本,描述了这种现象(即使它是指模块主机关联的情况,但逻辑相同):
说明19.10
模块程序单元中的指针可能在 通过使用关联的子程序。这样的指针的寿命为 大于子程序中声明的目标,除非这样 目标已保存。因此,如果这样的指针与 本地目标,有可能在定义过程时 子程序完成执行后,目标将不复存在, 使指针“悬空”。本文档考虑了此类指针 具有未定义的关联状态。他们都不相关 也不分离。在此之前,它们无法在程序中再次使用 他们的地位已经恢复。不需要处理器 检测指针目标何时停止存在。
悬空指针不是可靠的指针,并且编译器无法控制它。他们可能出于某种原因 继续指向其最后一个内存地址(在某些情况下不小心给出了预期的结果,或者这些值会从随机内存地址中变得乱七八糟),但是最肯定的是< strong> break ,失败可能是任何事情,从错误的结果到 SIGSEG错误或内存地址冲突。
请参见以下示例代码:
program dangling_pointer
implicit none
integer, pointer :: p(:)
integer, allocatable :: a(:)
call sub1(p)
print *, 'sub1: ', p
call sub2(p)
print *, 'sub2: ', p
call sub3(p, a)
print *, 'sub3: ', p
p => fun4()
print *, 'fun4: ', p
contains
subroutine sub1(dummy_p)
! the pointer passed as argument outlives the local target
! when the procedure ends, it becomes a "dangling pointer"
integer, pointer :: dummy_p(:)
integer, allocatable, target :: local_a(:)
allocate(local_a(5))
local_a = 100
dummy_p => local_a
end
subroutine sub2(dummy_p)
! here the local variable is saved, so it persists. No problem here.
integer, pointer :: dummy_p(:)
integer, allocatable, target, save :: saved_a(:)
allocate(saved_a(5))
saved_a = 100
dummy_p => saved_a
end
subroutine sub3(dummy_p, out_a)
! here the target is a passed argument, so it persists. No problem here.
integer, pointer :: dummy_p(:)
integer, allocatable, target :: out_a(:)
allocate(out_a(5))
out_a = 100
dummy_p => out_a
end
function fun4() result(result_p)
! here the function result will be returned as a pointer. No problem here.
integer, pointer :: result_p(:)
allocate(result_p(5))
result_p = 100
end
end
使用gfortran 9.0.0,我得到:
sub1: 14316208 0 14287184 0 100
sub2: 100 100 100 100 100
sub3: 100 100 100 100 100
fun4: 100 100 100 100 100
修改
我认为此代码段可以解决您的问题:
allocate(u1c%next)
if (u2%cloned) then
u1c%next = u2
else
u1c%next = unit_clone(u2)
end if
u1c%next%prev => u1c