F90
中,允许把整个数组作为一个操作数进行操作,也允许在赋值语句中对整个数组进行赋值,就像对一个简单变量的操作和赋值一样。针对数组的特性,也有专门的运算方法和内在函数。
5.2.1
数组的赋值可以使用赋值语句和数组构造器。
F90
中
数组赋值语句
的形式为:
V=e
,
V
代表数组名或数组片段,
e
为数组表达式,它们必须有相同的形状(即维数相同,每维长度相同但上下界可不同)。当
V
大小为
0
或为长度为
0
的字符型时,则没有值赋给
V
。当
e
为标量时,则把
e
处理成与
V
相同的形状,
V
的每个元素均等于标量
e
的值。数组表达式
e
中允许使用
+
、
-
、
*
、
/
、
**
等内部算术操作符。
F90
允许赋值语句的表达式内只有标量,如
A
是
数组,则
A=3
表示
。例如,
A(1:15)=2.0
,则
A(1)
到
A(15)
的每个元素都被赋值为
2.0
。因此,数组赋值语句可以对整个数组一次赋值完毕,不必用
DO
构造对每一个元素逐一赋值,如对
F77
中的初始化置零
DO
循环:
do i=1,10
do j=1,10
A(i,j)=0.
end do
end do
可以只要一个赋值语句
A=0.
就行。
例:
A(
1:50
)=E(1:99:2)+F(2:100:2,10)/G(3:101:2,5,5)
例:
Q(
10:40:10
)=A(5:20:5)//B(1:4)
例:
INTEGER A(20)
A(
1:20
)=A(20:1:-1)
!
将
A
数组元素值倒序重排
数组构造器
是由括号和斜线之间的一系列值或隐
DO
循环组成。其一般形式为:
(/
取值列表
/)
。取值列表可以是标量,隐
DO
循环或任意秩的数组。其中的所有值的类型都相同,以逗号隔开。如果列表中出现了数组,它的值是按列来赋的。
例:
INTEGER A(6)
A=(/1,2,3,4,5,6/)
!
斜杠与括号间不能有空格
例:
C1=(/4,8,7,6/)
!
C2=(/B(I,1:5),B(I:J,7:9)/)
!
C3=(/(I,I=1,4)/)
!
隐
DO
循环
C4=(/4,A(1:5),(I,I=1,4),7/)
!
下面是一些
数组构造器的替换格式
:
1)
用
方括号
代替括号和斜线,例如下面两个数组构造器是等价的:
例:
INTEGER C(4)
C=(/4,8,7,6/)
C=[4,8,7,6]
2)
冒号三元下标
(
代替隐
DO
循环
)
来指定值的范围和步长,例如下面两个数组构造器是等价的:
例:
INTEGER D(3)
D=(/1:5:2/)
!
三元下标格式
D=(/(I,I=1,5,2)/)
!
隐
DO
循环格式
数组的存储顺序
由于计算机的内存是一维的,所以不管数组是几维,它在内存中都是按一维来存储的,数组的存储顺序对应于输入输出时给定数组元素数据的顺序。
FORTRAN
规定,一维数组在机内存贮时是按序存放的。而对二维数组,用第一个下标指明行序,第二个下标指明列序,则数组是按列存放的。即按序存放第一列内的诸元素,接着是第二列内诸元素,然后第三列、第四列直到最后一列。如
A
是
数组,
则它在机内的存放顺序是
A(1,1)->A(2,1)->A(3,1) => A(1,2)->A(2,2)->A(3,2)
=> A(1,3)->A(2,3)->A(3,3)
。
高维数组的存放次序可由二维数组类推,即最右边指标(相当于最外层循环变量)变动最慢,最左边的第一个指标变动最快。
与
DO
循环的差异
F90
的赋值语句考虑到了在并行机上计算的功能,即使不是用的并行计算机,在形式上也是按并行化处理的。这与
F77
中的串行赋值是不同的,由此造成了数组的
F90
赋值结果与采用
DO
循环方式进行赋值的差异。
例:
INTEGER :: a(0:9)=(/0,1,2,3,4,5,6,7,8,9/)
a(1:9)=a(0:8)
a
的所有元素是并行处理的,结果是
(/0,0,1,2,3,4,5,6,7,8/)
:
|a(0)|a(1)|a(2)|a(3)|a(4)|a(5)|a(6)|a(7)|a(8)|a(9)|
↑
↑
↑
↑
↑
↑
↑
↑
|a(0)|a(1)|a(2)|a(3)|a(4)|a(5)|a(6)|a(7)|a(8)|a(9)|
但是,如果用
DO
循环的话
DO i=1,9
a(i)=a(i-1)
END DO
A
的元素是逐一处理的,结果是
(/0,0,0,0,0,0,0,0,0/)
:
|a(0)|a(1)|a(2)|a(3)|a(4)|a(5)|a(6)|a(7)|a(8)|a(9)|
→
→
→
→
→
→
→
→
②
③
④
⑤
⑥
⑦
⑧
⑨
要用
DO
循环达到和上面同样的效果,需要数组的拷贝:
INTEGER :: a(0:9)=(/0,1,2,3,4,5,6,7,8,9/), b(0:9)
DO i=1,9
a(i)=b(i-1)
END DO
RESHAPE
语句
可以用内在函数
RESHAPE
语句把一个一维数组改变形状后赋给另一已知形状的数组。由于
Fortran
的数组顺序不同于
C
,
Fortran
中左边第一维是变化最快的,
RESHAPE
语句可以用于当
Fortran
过程化为
C
过程时将
Fortran
数组改序成
C
数组序。它的一般形式为:
结果
=RESHAPE(
源
,
形状
[,
补充
][,
顺序
])
。
源
可以是任意数据类型的数组,它提供结果数组的元素。当补充被省略或其大小为
0
时,源的大小必须大于
PRODUCT(
形状
)
。
形状
为
7
个元素以下的一维固定的整型数组,它决定了结果数组的形状,其大小为正元素非负。
补充
为与源数组相同类型和属性的数组,当源数组的大小比结果数组小时用来补充元素值。
顺序
必须是和形状数组有相同形状的整型数组
,
其元素为
(1,2,...,n)
的排列,
n
是形状数组的大小。当顺序被省略时,默认值为
(1,2,...,n)
。
因此,
结果
数组的形状与形状数组相同,与源数组的类型和属性相同,大小是形状数组元素值的乘积。结果数组的元素是源数组的元素按顺序数组指定的维别顺序排列的,当省略顺序数组时元素按通常的顺序排列。源数组元素用完后按序用补充数组的元素,必要时重复使用补充数组直至结果数组的所有元素均有其值。
例:
RESHAPE((/3,4,5,6,7,8/),(/2,3/))
的结果是
。
例:
RESHAPE((/3,4,5,6,7,8/),(/2,4/),(/1,1/),(/2,1/))
的结果是
。
例:
INTEGER AR1(2,5)
REAL F(5,3,8),C(8,3,5)
AR1=RESHAPE((/1,2,3,4,5,6/),(/2,5/),(/0,0/),(/2,1/))
! AR1
的取值为
[1 2
3 4 5]
[6 0 0 0 0]
C=RESHAPE(F,(/8,3,5/),ORDER=(/3,2,1/))
!
将
Fortran
数组序化为
C
数组序
例:
INTEGER B(2,3),C(8)
B=RESHAPE((/1,2,3,4,5,6/),(/2,3/))
!
赋值给形状为
(2,3)
的数组
C=(/O,RESHAPE(B,(/6/)),7/)
!
赋值给向量
C
之前先把
B
转换成向量
WHERE
构造
F90
中提供了屏蔽数组赋值语句,它实质上是一种带条件的数组赋值语句,也就是说它只对某些符合条件的数组元素赋值。屏蔽数组赋值语句,为
WHERE
语句和
WHERE
构造,其形式非常类似于逻辑
IF
语句和
IF
构造。
WHERE
语句
的一般形式为:
WHERE(
屏蔽表达式
)
赋值语句
WHERE
构造
的一般形式为:
[
构造名
:]
WHERE(
屏蔽表达式
1)
[
块
]
[ELSEWHERE(
屏蔽表达式
2) [
构造名
]
[
块
]]
[ELSEWHERE [
构造名
]
[
块
]]
END WHERE [
构造名
]
其中
屏蔽表达式
都是数组逻辑表达式。这里要求数组赋值语句中被定义的变量和屏蔽表达式必须都是数组,且形状相同,而且数组赋值语句不能是自定义的赋值语句。
在执行
WHERE
语句时,当数组逻辑表达式的某个数组元素的值为
.TRUE.
时,才对赋值语句表达式的相应元素求值,赋给被定义的数组元素。同样,在执行
WHERE
构造时,逻辑表达式的取值结果
(
数组元素
)
为
.TRUE.
时,执行屏蔽表达式后面的块;而元素值为
.FALSE.
时,执行
ELSEWHERE
后面的块。注意与
IF
构造不同的是,这里没有关键字
THEN
和
ELSE
块。
例如,要对数组
A
中绝对值大于
1
的元素取倒数,其余的元素值不变,其语句为:
WHERE(ABS(A)>1.0) A
=
1./A
。如果同时还要记录哪些元素作了变换,可用数组
B
记录变换情况,语句为:
B=.FALSE.
WHERE(ABS(A)>1.0) B
=
.TRUE.
WHERE(ABS(A)>1.0) A
=
1./A
显然,上面最后一行可以写成:
WHERE(B) A
=
1./A
若采用
WHERE
构造,上例可改写为:
B=.FALSE.
WHERE(ABS(A)>1.0)
B
=
.TRUE.
A
=
1./A
END WHERE
这样改写后可以提高程序的执行效率。当然,还可以改写成下面的等价形式:
WHERE(ABS(A)>1.0)
B
=
.TRUE.
A
=
1./A
ELSEWHERE
B=.FALSE.
END WHERE
例:
INTEGER A,B,C
DIMENSION A(5),B(5),C(5)
DATA A/0,1,1,1,0/
DATA B/10,11,12,13,14/
WHERE(A.NE.0) C=B/A
则数组
C
的元素为
(-1,11,12,13,-1)
。
FORALL
屏蔽赋值
FORALL
是
F95
的新增功能。它是数组屏蔽赋值(
WHERE
语句和构造)功能的一对一元素的推广,其方式有
FORALL
语句和
FORALL
构造。
FORALL
语句
的一般形式为:
FORALL(
循环三元下标
[,
循环三元下标
]
…
[,
屏蔽表达式
])
赋值语句
FORALL
构造
的一般形式为:
[
构造名
:]
FORALL(
循环三元下标
[,
循环三元下标
]
…
[,
屏蔽表达式
])
[
块
]
END FORALL [
构造名
]
屏蔽表达式是数组逻辑表达式,缺省值为
.TRUE.
。块是赋值语句,或为
WHERE
语句或构造,或为
FORALL
语句或构造。
循环三元下标
的形式为:
循环变量
=
下界
:
上界
[:
步长
]
。循环变量是整型的。步长不能为
0
,缺省值为
1
。
例:
INTEGER :: mat(0:7,0:7)
FORALL(i=0:7,j=0:7) mat(i,j)=8*i+j
FORALL(i=0:7,j=0:7)
mat(i,j)=8*i+j
END FORALL
如果要用
F77
的语句写出来的话,则为
DO i=0,7
DO j=0,7
mat(i,j)=8*i+j
END DO
END DO
例:
REAL :: a(0:n)
WHERE(a>0.0)
a=SQRT(a)
FORALL(i=0:n,a(i)>0.0) a(i)=SQRT(a(i))
例:
WHERE(A/=0.) B=1./A
FORALL(I=1:N,J=1:N,A(I,J).NE.0.) B(I,J)=1./A(I,J)
FORALL (I=1:N,J=1:N)
IF(A(I,J).NE.0.) B(I,J)=1./A(I,J)
END FORALL
例:
INTEGER :: num(0:7)=(/0,1,2,3,4,5,6,7/)
FORALL(i=1:7) num(i)=num(i-1)
FORALL(i=1:7)
num(i)=num(i-1)
END FORALL
a(1:7)=a(0:6)
不
等价于
DO i=1,7
num(i)=num(i-1)
END DO
[e_521_01.f90]
5.2.2
允许把整个数组或数组片段作为一个单独的对象进行运算。例如当数组
A,B,C
有相同大小和形状时,
C=A+B
语句实现对数组所有元素的并行加法运算。所有的算术运算符
(+
,
-
,
*
,
/
,
**)
、逻辑运算符
(
如
.AND.
,
.OR.
,
.NOT.)
和所有关系运算符
(
如
.LT.
,
.EQ.
,
.GT.)
,还有很多内在函数都可以接受数组名称作为参数并对数组元素逐一运算。
例:
INTEGER, DIMENSION(100)::
even,odd,plu,min,tim,div,squ
even=(/(2*i,i=0,49)/); odd=(/(2*i+1,i=0,49)/)
plu=even+odd
min=even-odd
tim=even*odd
div=even/odd
squ=even**2
可以使用数组名作为参数的内在函数称为
基本内在函数
。
例:
REAL A(5),B(5),C(5)
INTEGER D(5)
DATA PI/3.14159265/
A=(/(REAL(I)*PI/180.,I=1,5)/)
B=COS(A); C=SQRT(A); D=CEILING(A*180.)
其中
COS
,
SQRT
和
CEILING
都是基本内在函数。当两个以上数组出现在赋值语句或表达式中时,数组的形状应该相同
(
称为
相容
)
。例如数组
A(2,3)
,
B(2,3)
,
C(2:3,6:8)
都是相容的,而数组
D(4,5)
,
E(5,4)
,
F(5,2,2)
都是不相容的。
例:
X(
1:10
)=X(10:1:-1) !
使用三元下标颠倒数组
X
如果数组片段指定的部分相容,则它也可以用于表达式和赋值。这样,不相容的数组也可以互相使用。例如,一个较小的完整数组可以和一个相容的大数组的片段进行运算:
例:
REAL A(5),B(4,7)
A=20.; B=5.; A=A-B(2,1:5)
数组与数组
两个数组作算术操作的结果仍是一个形状相同的数组,它的每个位置上元素的值是参与操作的相同位置上一对元素操作后的结果值。例如有数组
A
、数组
B
,形状如下:
则执行赋值语句
C=A+B
后,数组
C
的值即为
对于其它内部操作,如
+
、
-
、
*
、
/
、
**
等,操作结果也是一个形状相同的数组。它的每个位置上的元素值是参与操作的数组相同位置上的一对元素(不管它们的下标是否相同)作相同操作的结果。
数组与标量
数组表达式允许数组与标量作算术运算。当
A
、
B
为形状相同的数组时,赋值语句
A=B+2
是合法的。表达式是数组
B
与标量值
2
相加,这时可以把标量看作形状与
B
相同的数组,其每一元素的值都是
2
,而后与
B
数组和加,其结果为:
也即数组与标量操作相当于数组内每一元素与该标量操作。
数组内在函数
数组表达式中允许对数组求基本函数,其函数值仍是一个形状相同的数组,它的每个位置上的元素值就是被操作数组对应位置元素取该函数值,例如
A
、
B
为形状相同的一维数组,则语句
B=SQRT(A)
表示:
B(1)=SQRT(A(1))
、
B(2)=SQRT(A(2)),
…。
F90
中增加了许多新的数组专用内在函数,使得数组的运算更加灵活方便。下面是一部分新增的数组内在函数。
矩阵乘积函数
:
MATMUL(A,B)
描述:执行数值或逻辑型矩阵
A
与
B
的矩阵乘法。
说明:矩阵
A
和
B
必须是秩为
1
或
2
的数值型或逻辑型的有值数组,且
A
与
B
的类型必须相同,
A,B
中至少有一个秩是
2
。
A
与
B
的矩阵乘积规则和结果值与数学上的定义相同,
A
的最后一维的长度必须和
B
的第一维的长度相同。
向量点乘函数
:
DOT_PRODUCT(A,B)
描述:执行数值或逻辑型向量
A
与
B
的点积乘法。
说明:向量
A
和
B
必须是秩为
1(
即向量
)
的数值型或逻辑型的有值数组,且
A
与
B
的类型必须相同。向量
A
与
B
点乘的结果是标量,其规则和结果值与数学上的定义相同。
例:
DOT_PRODUCT((/1,2,3/),(/2,3,4/))
的值为
20
。
数组归约函数
:包括
SUM
、
PR0DUCT
、
MAXVAL
、
MINVAL
、
COUNT
、
ANY
和
ALL
函数。
★
元素求和函数
:
SUM(ARRAY[,DIM][,MASK])
描述:沿着维
DIM
,对在
MASK
真值中的数组
ARRAY
的所有元素求和。
说明:
ARRAY
是被求和的数组名。
DIM
用于指明选哪一维来求函数值。当“
DIM=1
”时分别按列求和,“
DIM=2
”时分别按行求和。
MASK
是屏蔽表达式,不满足条件的元素则被屏蔽,不参加求函数值。
例:
SUM((/4,5,6/))
的值是
15
。
例:
SUM(C,MASK=C>0.0)
是对
C
的所有正元素值的求和。
例:若
,则
SUM(A,DIM=1)
的值是
[3,5,7]
,
SUM(A,DIM=2)
的值是
[9,6]
。
★
元素连乘求积函数:
PRODUCT(ARRAY[,DIM][,MASK])
描述:沿着维
DIM
,对在
MASK
真值中的数组
ARRAY
的所有元素求连乘积。
例:
PRODUCT((/4,5,6/))
的值是
120
。
例:
PRODUCT(C,MASK=C>0.0)
是对
C
的所有正元素值的求连乘积。
例:若
,则
PRODUCT(A,DIM=1)
的值是
[2,6,12]
,
SUM(A,DIM=2)
的值是
[24,6]
。
数组查询函数
:包括
SIZE
、
SHAPE
、
ALLOCATED
、
LBOUND
和
UBOUND
函数。
★
求数组大小函数:
SIZE(ARRAY[,DIM])
描述:求数组
ARRAY
沿着维
DIM
的长度或数组元素的总数目。
★
求数组形状函数:
SHAPE(ARRAY)
。
描述:求数组或标量的形状。
数组的构造函数:包括
MERGE
、
PACK
,
UNPACK
和
SPREAD
函数。
描述:数组构造函数用于从已有数组的元素构造出新数组。
★
合并数组函数:
MERGE(TSOURCE,FSOURCE,MASK)
描述:在
MASK
的控制下,合并数组
TSOURCE
和
FSOURCE
。
说明:数组
TSOURCE
可以是任一类型,数组
FSOURCE
必须与
TSOURCE
具有相同的类型和类型参数。
MASK
必须是逻辑型数组。若
MASK
为真,则结果是
TSOURCE
,若为假,则结果是
FSOURCE
。
例:
MERGE(1.,0.,R<0)
的结果是:当
R=-2
,取值为
1.0
;当
R=2
,取值为
0.0
。
例:若
,
,
,则
MERGE(A,B,M)=
。
★
压缩数组函数:
PACK(ARRAY,MASK[,VECTOR])
描述:在
MASK
控制下,将数组
ARRAY
压缩成向量数组。
说明:
ARRAY
可是任意类型的数组。
MASK
必须是逻辑型数组并与
ARRAY
相容。
VECTOR(
可选
)
必须为向量数组,且与
ARRAY
具有相同的类型和类型参数。结果是秩为
1
的数组,其类型和类型参数与
ARRAY
相同;若
VECTOR
存在,结果大小等于
VECTOR
的大小,否则其大小是
MASK
中真元素的个数
t
,若
MASK
为标量且为真值,这时结果的大小与
ARRAY
相同。结果的值按数组中元素排序,
ARRAY
中的第
i
个元素对应于
MASK
的第
i
个真元素。若
VECTOR
存在,且大小
n>t
,则结果中第
i
个元素值为
VECTOR(i)
,
i=t+1,
…
,n
。
例:若
,其非
0
元素可由
PACK
函数收集,
PACK(A,MASK=A.NE.0)
的结果为
[9,7]
,
PACK(A,A.NE.0,VECTOR=(/1,2,3,4,5,6/))
的结果值为
[9,7,3,4,5,6]
。
★
拷贝数组函数:
SPREAD(SOURCE,DIM,NCOPIES)
描述:将数组
SOURCE
沿着
DIM
方向拷贝
NCOPIES
次后扩展成一新的数组。
说明:当
DIM=1
时,即沿着第一维下标变化的方向扩展,也即向下扩展。
DIM=2
时,沿着第二维下标变化方向扩展即向右扩展。
例:若
,片段
A(2,2:4)=[2.2,2.3,2.4]
,
则
SPREAD(A(2,2:4),1,3)
为
,
SPREAD(A(1:3,1),2,3)
为
。
例:
SPREAD("B",1,4)
为数组
(/"B","B","B","B"/)
。
数组重构形函数:
RESHAPE(SOURCE,SHAPE[,PAD][,ORDER])
数组运算函数:包括
TRANSPOSE
、
EOSHIFT
和
CSHIFT
三个函数。
★
矩阵转置函数:
TRANSPOSE(MATRIX)
描述:将秩为
2
的数组转置。
例:若
A
是数组
,则
TRANSPOSE(A)
为
,
则
EOSHIFT(B,SHIFT=1,BOUNDARY='*',DIM=2)
为
,
EOSHIFT(B,SHIFT=-1,DIM=1)
为
,则
MAXLOC(A,MASK=A.LT.5)
为
[1,1]
,
MAXLOC(A,DIM=1)
为
[1,2,3,2]
,
MAXLOC(A,DIM=2)
为
[1,4,3]
。
要注意的是,表达式中数组进行操作后,不再保留原来的下标形式,由系统将它们按线下界为
1
重新排列。这样,参与操作的两个数组原来的下标是否相同,可以不必考虑。但在引用某些与下标有关的函数时.要记住此时的函数值以整理过的新下标值出现。例如定义一个数组
A
及其初值如下:
INTEGER, DIMENSION(0:6),PARAMETER :: A=(/3,7,0,-2,3,6,-1/)
因值
7
是
A
中元素的最大值,元素的下标是
A(1)
,即数组
A
中最大值元素的下标为
1
。但内部函数
MAXLOC
的返回值是
2
而不是
1
,即返回的不是原来的下标值,而是以
1
为维下界整理过的下标值。
F90
数组运算内在函数表
例:
INTEGER :: a(10)
READ *, (a(i), i=1,10)
← 可以
1
行输入
10
个值,也可输入
1
个值
(
隐
DO
循环
)
READ *, a
← 同上
(
数组名
)
DO i=1,10
READ *,a(i)
←
1
行只能输入
1
个值,输入
2
个以上被忽略
END DO
READ *, a(3),a(4:6)
← 输入数组元素和数组片段:
a(3),a(4),a(5),a(6)
PRINT '(10I5)',(a(i),i=1,10)
←
1
行打印
10
个值
(
隐
DO
循环
)
PRINT '(10I5)',a
← 同上
(
数组名
)
PRINT '(5I5)',a(1:5)
←
1
行打印
5
个值
(
数组片段
)
DO i=1,10
PRINT '(I5)',a(i)
←
1
行打印
1
个值,共打
10
行
END DO
二维数组
:
二维数组的输入输出读写语句使用与一维情况时一样,数组元素输入输出的顺序是按前面提到的数组的存储顺序来进行的。由于二维数组的顺序是首先按列存放,因此对于输入
数组的
A
,执行语句
READ *,A
时,则读入数据的顺序是
A(1,1)->A(2,1)->A(3,1) => A(1,2)->A(2,2)->A(3,2)
=> A(1,3)->A(2,3)->A(3,3)
。按列存贮方式与我们数学上按行处理的习惯不一致,在给数组各元素赋值时,先输入第一列元素的值,再输入第二、第三列的值这样机内收到的矩阵才是正确的。
如一定要按行方式输入矩阵值,可以通过交换隐
DO
循环内外层循环变量的方法来实现。如
READ *,A
与
READ *,((A(I,J),I=1,3),J=1,3)
是等价的,交换内外层循环变量后:
READ
*,((A(J,I),I=1,3),J=1,3)
或
READ
*,((A(I,J),J=1,3),I=1,3)
,这时输入顺序是按行的:
A(1,1)->A(1,2)->A(1,3) => A(2,1)->A(2,2)->A(2,3)
=> A(3,1)->A(3,2)->A(3,3)
。
5.2.3
数组的动态分配
可分配数组
数组可以是静态的也可以是动态的。如果数组是静态的,则在编译时就被分配了固定的储存空间,并且直到程序退出时才被释放。程序运行时静态数组的大小不能改变。静态数组的缺陷是,即使数组已经使用完毕,它仍占据着内存空间,浪费了系统资源。在给定的计算机内存资源情况下,耗费了其他数组可以利用的内存,并且超过资源的数组将导致程序执行错误。因此,
F90
增加了动态的数组功能,动态数组的储存在程序运行当中是可以分配、改变和释放的。
动态数组只有两种:可分配数组和自动数组。自动数组和可分配数组很类似,区别在于当程序开始或结束时,自动数组会自动分配和释放内存。当用户分配动态存储空间时,数组的大小是在运行时而不是在编译时确定的。动态分配可以用于标量和任何类型的数组。当用户给数组指定了可分配属性时并没有立即分配内存,而是直到使用
ALLOCATE
语句后才分配。随后还可以用
DEALLOCATE
语句释放内存空间,这时数组可以以其它形状或目的来使用。
应该注意的是,
WinNT/9x
上运行的
Visual Fortran
动态内存分配受一些因素的限制,包括交换文件的大小和其它同时运行的应用程序所需的内存大小。如果动态分配的内存太大或试图使用其它应用程序的保护内存会产生一般内存保护错误。碰到这类问题可以通过控制面板来改变虚拟内存的大小或交换文件的大小,还有一些编程技术可以降低内存需要。
ALLOCATE
语句
ALLOCATE
语句动态创建可分配数组,使内存和对象相联系。分配的对象可以被命名为任何有
ALLOCATABLE
属性的变量。它的一般形式为:
ALLOCATE(
数组名
[
维界符
][,
数组名
[(
维界符
[,
维界符
...])]
] ...[,STAT=
状态值
])
。
例:
REAL A(:),B(:,:,:)
ALLOCATABLE A,B
ALLOCATE(A(-
2:40
),B(3,3,3))
当数组被分配时,内存分配给指定大小的数组。
ALLOCATE
语句中的秩必须和可分配数组的秩相同。在分配的同时,
ALLOCATE
语句中的上下界决定了数组的大小和形状。边界的值可以是正数、负数或零,缺省的下界为
1
。如果维上界比下界小,则该维的长度为零,并且数组的大小为零。大小为零的数组不能被赋值。
当前被分配的数组不能被再分配,否则会引起运行错误。错误状态可以由
ALLOCATE
语句中的
STAT
值获得。如果指定
STAT
选项,语句的成功执行时将返回
0
,否则返回正值。若未指定
STAT
选项且出现错误时,程序将中止执行。
例:
INTEGER, ALLOCATABLE :: A(:),B(:)
INTEGER ERR_MESSAGE
ALLOCATE(A(
10:25
),B(SIZE(A)),STAT=ERR_MESSAGE)
IF(ERR_MESSAGE.NE.0) PRINT *,'ALLOCATION ERROR'
可以用内在函数
ALLOCATED
来判断一个数组是否已被分配。它的形式为:
ALLOCATED(
数组名
)
。返回值是逻辑标量,已被分配时为真,现在还未被分配时为假,当数组的分配状态未定义时它也是未定义的。
例:
REAL, ALLOCATABLE :: A(:)
IF(.NOT.ALLOCATED(A)) ALLOCATE(A(5))
DEALLOCATE
语句
DEALLOCATE
语句用来释放已分配数组的内存。它的一般形式为:
DEALLOCATE(
数组名
[,
数组名
]...[,STAT=
状态值
])
。
例:
INTEGER, ALLOCATABLE :: A(:),B(:)
INTEGER ERR_MESSAGE
ALLOCATE(A(
10:25
),B(SIZE(A)))
DEALLOCATE(A,B,STAT=ERR_MESSAGE)
IF(ERR_MESSAGE.NE.0) PRINT *,'DEALLOCATION ERROR'
例:
INTEGER,DIMENSION(:),ALLOCATABLE :: freq
READ *,limit
ALLOCATE(freq(1:limit))
DEALLOCATE(freq)
只有被
ALLOCATE
语句分配的内存空间才可以被
DEALLOCATE
语句释放,否则产生运行错误。可以使用
ALLOCATED
语句判断数组是否被分配,错误状态可以由
ALLOCATE
语句中的
STAT
值获得。
当过程的执行被
RETURN
或
END
语句中止时,除非可分配数组是有
SAVE
属性的,否则它的分配状态变成未定义的。但是,
RETURN
和
END
语句并不释放数组分配的内存,所以应该在退出子程序前主动释放数组分配的内存。
可分配数组的联合状态可以是已分配的(该数组被
ALLOCATE
语句分配,可以被引用、定义或释放)或是目前未联合(该数组从未分配或上一个操作是释放,数组不能被引用或定义)。
当可分配数组赋值时就被定义。
例:
INTEGER, ALLOCATABLE :: A(:)
ALLOCATE(A(100))
!
A
被分配但未定义,
A
的分配状态是己分配
A(1:100)=1
!
A
被定义
DEALLOCATE(A)
!
A
被释放,
A
的分配状态是未分配
[5.1]
用类型说明语句定义:
(1)
一个整型数组,
10
个元素,名
I
;
(2)
一个逻辑型数组,
2
维,第一维维界是
0:7
,第
2
维维界是
-7:0
,名
L
;
(3)
一个字符型数组,
1
维,下标从
1
变到
100
,名
C
;
(4)
把矩阵
定义成数组,名
R
;
[5.2]
设数组
,
,
,
,
E
数组描述为
(1:2,1:3)
,
F
数组描述为
(1:3)
,问下列数组表达式是否合法,不合法的说明理由,合法的写出计算结果:
(1) E=A+B
(2) E=ABS(B)+2
(3) E=B+C
(4) C=A+C
(5) F=D*D
F=A(1:2,1)+B(1:3,1)
F=A(1,1:3)+B(1,1:3)
[5.3]
用
WHERE
构造,使上题中
A
元素≤
2
时,置
B
相应元素为自身的绝对值,否则使
B
中元素加
1
,写出最后
B
的内容。
[5.4]
读入一个二维数组
A
,形状为
(1:5,1:3)
,任意输入具体数据。
(1)
用
DO
循环求全部数组元素和,再用数组内部专用求和函数求和,比较两者结果。
(2)
用
DO
循环求数组元素连乘积,再用数组内部求连乘积函数求整个数组元素之积,比较两者结果。
[5.5]
用动态数组存放任意读入的
N
个储款数,统计这次存款总数,而后释放数组,再把它存贮任意读入的
M
个取款数,统计共被取出的款额,求收支相抵的余额。