首页 > 解决方案 > 重置(解除分配/无效)已损坏的 Fortran 可分配数组

问题描述

当出现Incorrect fortran errors: allocatable array is already assigned中描述的情况时;DEALLOCATE 指向一个无法解除分配的数组(损坏的内存留下了一个看起来已分配但没有“指向”有效地址的可分配数组),有什么可以在 Fortran 中完成来解决它,即重置数组作为释放,而不试图释放它指向的内存?

这种情况是一个 Fortran/C 程序,其中一段 C 代码故意破坏(将垃圾写入)分配的内存。这适用于普通类型的数组。但是对于用户定义类型的可分配数组,它本身包括一个可分配组件,写入属于可分配组件的部分的垃圾意味着现在该组件显示为已分配,即使它不是。当我知道我不关心它当前似乎指向的内存时,我宁愿在之后修复它,而是“取消”可分配组件,而不是让 C 代码知道它应该损坏什么。使用指针,这只是一个问题nullify,但是使用可分配数组?

标签: fortranallocatable-array

解决方案


如果内存真的像堆栈损坏/堆损坏那样损坏。你什么也做不了。程序必然会失败,因为非常底层的信息丢失了。这适用于任何编程语言,甚至 C。

如果损坏的是 Fortran 数组描述符,则无法从 Fortran 中更正它。Fortran 不会向 Fortran 程序员公开这些实现细节。它只能通过ISO_Fortran_binding.h从 C调用的特殊头文件获得。

如果发生的唯一损坏是让 Fortran 将数组分配到未分配的位置,那么从 C 中恢复它应该相当简单。只需要更改分配内存的地址即可。可分配数组总是连续的。

人们也可以尝试一些肮脏的技巧,比如告诉子例程你传递的是一个指针,而实际上它是一个可分配的指针并将其无效。它可能会在许多实现中起作用。但是以可控的方式取消地址要干净得多。即使它只是您从 Fortran 调用的一个无效 C 函数。

因为您真的只想将地址更改为 0,而不是对数组范围、步幅和其他细节进行任何其他特殊处理,所以即使没有标头也应该很简单。

请注意,描述符仍将包含其他变量中的无意义数据,但这些应该无关紧要。


这是一个快速而肮脏的测试:

Fortran:

  dimension A(:,:)
  allocatable A
  
  interface
    subroutine write_garbage(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
    subroutine c_null_alloc(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
  end interface
  
  call write_garbage(A)
  
  print *, allocated(A)
  
  call c_null_alloc(A)
  
  print *, allocated(A)
  
end

C:

#include <stdint.h>
void write_garbage(intptr_t* A){
  *A = 999;
}

void c_null_alloc(intptr_t* A){
  *A = 0;
}

结果:

> gfortran c_allocatables.c c_allocatables.f90
> ./a.out 
 T
 F

ISO_Fortran_binding.h如果您的编译器提供了正确的版本,则应该使用它。还有implicit none其他无聊的东西...


我完全不推荐的一个非常肮脏(和非法)的黑客攻击:

  dimension A(:,:)
  allocatable A
  
  interface
    subroutine write_garbage(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
    subroutine null_alloc(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
  end interface
  
  call write_garbage(A)
  
  print *, allocated(A)
  
  call null_alloc(A)
  
  print *, allocated(A)
  
end

subroutine null_alloc(A) bind(C)
      dimension A(:,:)
      pointer A
      
      A => null()
end subroutine
> gfortran c_allocatables.c c_allocatables.f90
c_allocatables.f90:27:21:

   10 |     subroutine null_alloc(A) bind(C)
      |                         2
......
   27 | subroutine null_alloc(A) bind(C)
      |                     1
Warning: ALLOCATABLE mismatch in argument 'a' between (1) and (2)
> ./a.out 
 T
 F

推荐阅读