重置(取消分配/取消)已损坏的 Fortran 可分配数组

Reset (deallocate / nullify) a Fortran allocatable array that has been corrupted

当发生 Incorrect fortran errors: allocatable array is already allocated; DEALLOCATE points to an array that cannot be deallocated 中描述的情况时(损坏的内存留下看似已分配但未“指向”有效地址的可分配数组),是否可以在 Fortran 中执行任何操作以治愈它,即将数组重置为已释放,而不尝试释放它指向的内存?

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

如果内存真的像堆栈一样损坏 corruption/heap 损坏。你无能为力。该程序注定会失败,因为 low-level 信息丢失了。对于任何编程语言都是如此,甚至 C.

如果损坏的是 Fortran 数组描述符,则您无法从 Fortran 中更正它。 Fortran 不会向 Fortran 程序员公开这些实现细节。它只能通过来自 C.

的名为 ISO_Fortran_binding.h 的特殊 header 获得

如果发生的唯一损坏是让 Fortran 认为数组被分配到它没有分配的地方,那么从 C 中恢复它应该相当简单。所有必要的就是改变分配的地址记忆。可分配数组始终是连续的。

人们也可以尝试一些卑鄙的把戏,比如告诉一个子程序你传递的是一个指针,而实际上它是一个可分配的指针并使它无效。它可能适用于许多实现。但是以可控的方式使地址无效要干净得多。即使它只是您从 Fortran 调用的一个无效 C 函数。

因为你真的只想将地址更改为 0 而不是对数组范围、步幅和其他细节做任何其他特殊的东西,即使没有 header 也应该很简单。

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


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

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