如何在Fortran中实时增加数组大小?

13
我的程序通过遍历3D数组,标记它找到的“簇”,然后进行一些检查,以查看任何相邻的簇是否具有比当前簇更高的标签。有第二个数组保存“正确”的簇标签。如果发现第n个相邻簇已正确标记,则将该元素分配为0;否则将其分配给正确的标签(例如,如果第n个位置的标签为2,而其邻居被标记为3,则将第三个元素labelArray设置为2)。我有一个很好的理由这样做,真的!
我想要的只是能够即时分配labelArray的第n个元素。我已经查看了可分配数组和声明为labelArray(*)的内容,但是尽管在网上搜索和StackOverflow上搜索,我仍然不太理解。
因此,对于如何做到这一点的任何帮助都将是极好的。
4个回答

13

以下是一个 Stack Overflow 问题,其中提供了一些代码示例,展示了使用 Fortran 可分配数组的几种方法:How to get priorly-unkown array as the output of a function in Fortran:声明、分配内存、测试是否已经被分配、使用新的 move_alloc 和赋值时进行内存分配。未在此处显示显式释放内存,因为这些示例使用了 move_alloc 和自动释放。

附言:如果您想重复添加一个元素,您应该考虑数据结构的方法。逐个增加数组元素并不是一种有效的方法。在 Fortran 中将一个数组从 N 个元素扩展到 N+1 个元素可能意味着创建一个新的数组并复制所有现有元素。更合适的数据结构可能是链表。您可以通过创建用户定义类型并使用指针在 Fortran 中创建链表。您可以将成员链接在一起,从一个指向下一个。增加另一个成员的开销很小。缺点是最容易按顺序访问列表成员。您没有使用索引的数组轻松访问成员的能力来以任何顺序访问成员。

我在网上找到有关 Fortran 链表的信息:http://www-uxsup.csx.cam.ac.uk/courses/Fortran/paper_12.pdfhttp://www.iag.uni-stuttgart.de/IAG/institut/abteilungen/numerik/images/4/4c/Pointer_Introduction.pdf


看了@Vladimir写的内容和你提供的链接,这段代码是否可行?allocate(temp(size(labelArray)+1))// temp(:size(labelArray))=labelArray// call move_alloc(temp,labelArray) - AncientSwordRage
那看起来是正确的。如果数组大小很大,由于赋值语句引起的复制可能会导致效率低下。 - M. S. B.
好的,当我使用一个超大数组运行它时,实际元素并不是很多。然而,我受限于只能使用Fortran90,我认为它没有指针? - AncientSwordRage
我现在在一个看似随意的代码行处遇到了段错误,这与分配和释放内存的代码片段无关。我有点困惑为什么会出现这种情况。如果我移动分配/释放内存的代码,它会在释放内存的那一行出现段错误:deallocate(labelArray)。你有什么想法吗? - AncientSwordRage
我发现了问题所在,是因为在释放之前没有分配labelArray。我会去修复。编辑:现在它可以工作了。谢谢大家! - AncientSwordRage
显示剩余3条评论

10
如果你声明一个可分配的数组,你需要使用延迟形状,格式如下: allocatable :: labelArray(:,:) 或者 real,dimension(:,:),allocatable :: labelArray 其中双冒号的数量表示数组的秩(索引数)。
如果数组未被分配,你可以使用:
 allocate(labelarray(shapeyouwant))

确保正确的索引数量。例如,对于索引在第1维从2到3,在第2维从-1到5的数组,请使用allocate(labelarray(2:3,-1:5))

如果要更改维度,您必须首先使用

deallocate(labelArray)

要将已分配的数组重新分配为新形状,您首先需要使用新形状分配一个新数组,将现有数组复制到新数组中,并使用move_alloc()将旧数组的引用移动到新数组中。

  call allocate(tmp(size_old+n_enlarge))
  tmp(1:size_old) = array(1:size_old)
  call move_alloc(tmp, array)

旧的数组在新数组引用被 move_alloc() 移动时会自动释放。


Fortran 95 在数组不在作用域内(例如子程序末尾)时自动释放。

Fortran 2008 具有自动分配赋值的好特性。如果你说 array1=array2 并且 array1 没有被分配,那么它将自动分配正确的形状。

它也可用于重新分配(请参阅 Fortran 数组添加值时自动增长Fortran 90 动态数组如何添加新元素

 labelArray = [labelArray, new_element]

这是一个有效的语句吗?allocate(labelArray(size(labelArray)+1))我想要的只是在添加元素时增加大小。 - AncientSwordRage
3
不,这是不可能的。你不能改变数组分配的维度。这会变得太复杂了。程序很可能需要复制一份副本。你必须 1) 创建一个新的更大的数组并将其复制过去,或者 2) 复制你的数组,释放它,重新分配更大的空间并将保存的值复制回来。如果编译器支持,你可以使用move_alloc,而不是复制,这样你不必释放你的数组,只需分配更大的空间并将保存的值复制回来即可。 - Vladimir F Героям слава
其实,这太复杂了... C语言中有realloc函数,但Fortran却没有。 - Vladimir F Героям слава
2
这一定是评论两部分之间时间最长的记录。 - AncientSwordRage
我打算在我的日历上设置一个日期,在10年后回来打破这个记录。从前,有一个人,他总是不说完整句子直到...... - Mateen Ulhaq

1
晚了点的评论...请查看Numerical Recipes for Fortran 90。他们实现了一个漂亮的reallocate函数,符合Fortran 90标准。在这种情况下,您的数组必须具有指针属性,而不是可分配属性。
该函数接收旧数组和所需大小,并返回指向新调整大小的数组的指针。
如果可能,请使用Fortran 95或2003。如果无法使用2003,则95是一个很好的折衷方案。它提供了更好的指针语法。

0

综上所述,增加可分配数组大小的Fortran最先进的方法如下:

  allocate(tmp(size_old+n_enlarge))
  tmp(1:size_old) = array(1:size_old) !copy the data
  call move_alloc(tmp, array) !rename

请注意,move_alloc 仅涉及复制内部描述符(而不是数组中的实际数据),因此指向的存储是相同的。
总之,move_alloc 提供了一种有效的方式来重新分配更大尺寸的数组,而无需复制两次数据(只需要复制一次数据)。也就是说,您需要进行复制,但不需要进行复制输出。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接