(6.1) 指针的创建
指针用来保存一个内存地址来间接使用数据。当程序要读写指针变量时,会先取出指针中所保存的内存位置,再到这个内存位置读写数据。指针可以指向包括自定义数据类型在内的任何数据类型,但不管是哪种数据类型,指针变量都占用相同的内存空间(通常是4bytes),因为指针变量实际上是用来记录内存地址的。
!第一种指针的创建方法
PROGRAM main
IMPLICIT NONE
INTEGER,TARGET :: a = 1 !声明一个目标变量
INTEGER,POINTER :: p !声明一个可以指向整数的指针
p => a !p指向了a
WRITE(*,*) p !1
a = 2
WRITE(*,*) p !2
p = 3
WRITE(*,*) a !3
END PROGRAM main
!第二种指针的创建方法
PROGRAM main
IMPLICIT NONE
INTEGER,POINTER :: p
ALLOCATE(p) !配置一块内存空间给指针变量p
p = 1 !得到内存后的指针变量p可以像整型数一样使用
WRITE(*,*) p !1
DEALLOCATE(p) !释放掉配置的内存
END PROGRAM main
FORTRAN的指针和C的指针有着明显的区别,当FORTRAN的指针指向某一个变量后,这个指针就相当于是这个变量的分身,使得使用这个指针和使用这个变量变得没有区别,但是如果是C的话则必须用取址符来访问指针所指向的变量。另外,如果指针指向变量,指针可以随时改变它的指向;但是如果指针是用
ALLOCATE
配置的,在改变指向前需要将它DEALLOCATE
掉。
前面所讲的用ALLOCATABLE
声明可变大小的数组和这里讲的用ALLOCATE
配置空间给指针,都可以实现数组动态内存分配。它们的区别是:声明成ALLOCATABLE
的数组的生存周期只在声明它的函数内,函数结束后数组会自动DEALLOCATE
这些内存;如果声明成指针,等到程序结束才会自动释放掉这些内存。
!置空指针的第一种方法
INTEGER,POINTER :: p => NULL() !函数NULL()会返回一个不能使用的内存地址
!置空指针的第二种方法
INTEGER,POINTER :: p
NULLIFY(p)
!检查POINTER是否指向TARGET或POINTER是否有指向,返回值为布尔型
ASSOCIATED(POINTER,[TARGET])
!用指针高效访问高维数组中的某一个元素
INTEGER,TARGET,DIMENSION(5,5,5) :: target_arr
INTEGER,POINTER :: pointer_single
pointer_single => target_arr(2,2,2)
!用指针高效访问大型矩阵的某一部分
INTEGER,TARGET,DIMENSION(10000,10000) :: large_matrix
INTEGER,POINTER,DIMENSION(:,:) :: pointer_arr
pointer_arr => large_matrix(20:30,20:30)
!用指针高效访问自定义数据类型
TYPE person !一个person类型占28bytes
CHARACTER(20) name
REAL(KIND = 4) :: height,weight
END TYPE person
TYPE(person),TARGET :: target_p1,target_p2,target_temp
TYPE(person),POINTER :: pointer_p1,pointer_p2,pointer_temp
pointer_p1 => target_p1
pointer_p2 => target_p2
pointer_temp => target_temp
!交换person类型要移动28*3=84bytes空间
target_temp = target_p1
target_p1 = target_p2
target_p2 = target_temp
!交换person类型的指针要移动4*3=12bytes空间
pointer_temp = pointer_p1
pointer_p1 = pointer_p2
pointer_p2 = pointer_temp
(6.2) 指针数组
!第一种指针数组的创建方法
PROGRAM main
IMPLICIT NONE
INTEGER,TARGET :: target_arr(5) = (/1,2,3,4,5/)
!INTEGER,TARGET,DIMENSION(5) :: target_arr = (/1,2,3,4,5/) !也可以这样写
INTEGER,POINTER :: pointer_arr(:) !指针数组在声明时只需要说明它的维数就行了
!INTEGER,POINTER,DIMENSION(:) :: pointer_arr !也可以这样写
pointer_arr => target_arr !pointer_arr(1:5) => target_arr(1:5)
WRITE(*,*) pointer_arr !1 2 3 4 5
pointer_arr => target_arr(1:3) !pointer_arr(1:3) => target_arr(1:3)
WRITE(*,*) pointer_arr !1 2 3
END PROGRAM main
!第二种指针数组的创建方法
PROGRAM main
IMPLICIT NONE
INTEGER,POINTER,DIMENSION(:) :: pointer_arr
ALLOCATE(pointer_arr(5))
pointer_arr = (/1,2,3,4,5/)
WRITE(*,*) pointer_arr !1 2 3 4 5
DEALLOCATE(pointer_arr)
END PROGRAM main
PROGRAM main
IMPLICIT NONE
INTEGER,TARGET,DIMENSION(2,2,2) :: target_arr
INTEGER,POINTER,DIMENSION(:,:) :: pointer_arr !定义pointer_arr是2维指针数组
DATA target_arr /1,2,3,4,5,6,7,8/
pointer_arr => target_arr(:,:,1) !pointer_arr(i,j) => target_arr(i,j,1)
WRITE(*,"(8I2)") pointer_arr !1 2 3 4
END PROGRAM main
(6.3) 指针与函数
指针可以作为函数参数和函数返回值,这时必须要写函数的接口块,并且指针参数在声明时不能有
INTENT
属性。
PROGRAM main
IMPLICIT NONE
INTERFACE
FUNCTION get_min(pointer_arr) RESULT(min)
INTEGER,POINTER,DIMENSION(:) :: pointer_arr
INTEGER,POINTER :: min
INTEGER :: i,lenth
END FUNCTION get_min
END INTERFACE
INTEGER,TARGET,DIMENSION(5) :: target_arr = (/2,3,1,5,4/)
INTEGER,POINTER,DIMENSION(:) :: pointer_arr
pointer_arr => target_arr
WRITE(*,*) get_min(pointer_arr)
END PROGRAM main
FUNCTION get_min(pointer_arr) RESULT(min)
IMPLICIT NONE
INTEGER,POINTER,DIMENSION(:) :: pointer_arr !pointer_arr指针作为函数参数
INTEGER,POINTER :: min !min指针作为函数返回值
INTEGER :: i,lenth
lenth = SIZE(pointer_arr,1) !获取矩阵的列数(这里就是获取p的长度)
min => pointer_arr(1) !注意不能写成“=”
DO i = 2,lenth
IF(min > pointer_arr(i)) THEN
min => pointer_arr(i)
END IF
END DO
END FUNCTION get_min
之前讲过,可以将函数封装在模块中的,减少编写接口块带来的麻烦:
MODULE func_module
IMPLICIT NONE
CONTAINS
FUNCTION get_min(pointer_arr) RESULT(min)
INTEGER,POINTER,DIMENSION(:) :: pointer_arr
INTEGER,POINTER :: min
INTEGER :: i,lenth
lenth = SIZE(pointer_arr,1)
min => pointer_arr(1)
DO i = 2,lenth
IF(min > pointer_arr(i)) THEN
min => pointer_arr(i)
END IF
END DO
END FUNCTION get_min
END MODULE func_module
PROGRAM main
USE func_module
IMPLICIT NONE
INTEGER,TARGET,DIMENSION(5) :: target_arr = (/2,3,1,5,4/)
INTEGER,POINTER,DIMENSION(:) :: pointer_arr
pointer_arr => target_arr
WRITE(*,*) get_min(pointer_arr)
END PROGRAM main
(6.4) 串行结构
接下来的内容,将介绍指针的一个非常常见的应用——“串行(háng)”。串行是指多个同类的东西连接在一起。先来看一个单向串行的程序:
MODULE type_def
IMPLICIT NONE
TYPE node
INTEGER :: content !节点存储的内容
TYPE(node),POINTER :: next !指向下一个节点的指针
END TYPE node
END MODULE type_def
PROGRAM main
USE type_def
IMPLICIT NONE
TYPE(node),POINTER :: node1,node2,node3
ALLOCATE(node1)
ALLOCATE(node2)
ALLOCATE(node3)
!将数据装入节点中
node1%content = 10
node2%content = 20
node3%content = 30
!创建单向串行
node1%next => node2
node2%next => node3
node3%next => NULL() !node3没有下一个可以指向了,需要置空
!根据串行的关系访问节点存储的数据
WRITE(*,*) node1%content !10
WRITE(*,*) node1%next%content !20,相当于node2%content
WRITE(*,*) node1%next%next%content !30,相当于node3%content
DEALLOCATE(node1)
DEALLOCATE(node2)
DEALLOCATE(node3)
END PROGRAM main
上面这个程序只是示范了串行创建的大概原理,实际上创建串行更多用到的是下面这种搭配循环的方法:
MODULE type_def
IMPLICIT NONE
TYPE node
INTEGER :: content
TYPE(node),POINTER :: next
END TYPE node
END MODULE type_def
PROGRAM main
USE type_def
IMPLICIT NONE
TYPE(node),POINTER :: head !串行头
TYPE(node),POINTER :: p !访问串行节点的指针变量
INTEGER :: i,n
WRITE(*,*) "请输入节点数量:"
READ(*,*) n
!节点头的数据要先设定
ALLOCATE(head)
head%next => NULL()
WRITE(*,*) "请输入第 1个节点数据:"
READ(*,*) head%content
p => head
DO i = 2,n
ALLOCATE(p%next)
p => p%next
WRITE(*,"('请输入第',I2,'个节点数据:')") i
READ(*,*) p%content
END DO
p%next => NULL() !串行最后一个节点的下一个需要置空
p => head
DO WHILE(ASSOCIATED(p))
WRITE(*,*) p%content
p => p%next
END DO
END PROGRAM main
加强一下串行结构,使之成为可以沿前后两个方向移动的双向串行:
MODULE type_def
IMPLICIT NONE
TYPE node
INTEGER :: content
TYPE(node),POINTER :: previous !指向上一个节点的指针
TYPE(node),POINTER :: next !指向下一个节点的指针
END TYPE node
END MODULE type_def
PROGRAM main
USE type_def
IMPLICIT NONE
TYPE(node),POINTER :: head !串行头
TYPE(node),POINTER :: tail !串行尾
TYPE(node),POINTER :: p !访问串行节点的指针变量
TYPE(node),POINTER :: p_temp
INTEGER :: i,n
WRITE(*,*) "请输入节点数量:"
READ(*,*) n
!节点头(或者节点尾)的数据要先设定
ALLOCATE(head)
head%previous => NULL()
head%next => NULL()
WRITE(*,*) "请输入第 1个节点数据:"
READ(*,*) head%content
p => head
DO i = 2,n
ALLOCATE(p%next)
p_temp => p
p => p%next
p%previous => p_temp
WRITE(*,"('请输入第',I2,'个节点数据:')") i
READ(*,*) p%content
END DO
p%next => NULL()
tail => p
!正向输出
p => head
DO WHILE(ASSOCIATED(p))
WRITE(*,*) p%content
p => p%next
END DO
!反向输出
p => tail
DO WHILE(ASSOCIATED(p))
WRITE(*,*) p%content
p => p%previous
END DO
END PROGRAM main
目前为止所介绍的串行结构都是有头有尾的结构,串行结构还有另外一种类型叫做环状串行。它可以把串行的头尾连接起来,变成一个圈:
MODULE type_def
IMPLICIT NONE
TYPE node
INTEGER :: content
TYPE(node),POINTER :: previous
TYPE(node),POINTER :: next
END TYPE node
END MODULE type_def
PROGRAM main
USE type_def
IMPLICIT NONE
TYPE(node),POINTER :: node1,node2,node3
TYPE(node),POINTER :: p
INTEGER :: n = 6 !环状串行可以一直向前或者向后抓取数据,n可以设置成任意大小
INTEGER :: i
ALLOCATE(node1)
ALLOCATE(node2)
ALLOCATE(node3)
node1 = node(10,node3,node2)
node2 = node(20,node1,node3)
node3 = node(30,node2,node1)
!正向输出
p => node1
DO i = 1,n
WRITE(*,*) p%content
p => p%next
END DO
!反向输出
p => node3
DO i = 1,n
WRITE(*,*) p%content
p => p%previous
END DO
DEALLOCATE(node1)
DEALLOCATE(node2)
DEALLOCATE(node3)
END PROGRAM main
利用串行可以实现快速地插入或删除一条数据:
MODULE nodes_module
IMPLICIT NONE
TYPE node
INTEGER :: content
TYPE(node),POINTER :: previous
TYPE(node),POINTER :: next
END TYPE node
CONTAINS
!在pos之前插入new_node(pos%previous、new_node、pos)
SUBROUTINE insert_before_node(pos,new_node)
TYPE(node),POINTER :: pos
TYPE(node),POINTER :: new_node
new_node%next =>pos
new_node%previous => pos%previous
IF(ASSOCIATED(pos%previous)) pos%previous%next => new_node
pos%previous => new_node
END SUBROUTINE insert_before_node
!在pos之后插入new_node(pos、new_node、pos%next)
SUBROUTINE insert_after_node(pos,new_node)
TYPE(node),POINTER :: pos
TYPE(node),POINTER :: new_node
new_node%next => pos%next
new_node%previous => pos
IF(ASSOCIATED(pos%next)) pos%next%previous => new_node
pos%next => new_node
END SUBROUTINE insert_after_node
!删除pos节点(pos%previous、pos、pos%next)
SUBROUTINE delete_node(pos)
TYPE(node),POINTER :: pos
TYPE(node),POINTER :: previous
TYPE(node),POINTER :: next
previous => pos%previous
next => pos%next
DEALLOCATE(pos)
!重新连接pos释放后的前后两个节点需要注意两点问题:
!1.如果pos是第一条数据,不需要向前重连
!2.如果pos是最后一条数据,不需要向后重连
IF(ASSOCIATED(previous)) previous%next => next
IF(ASSOCIATED(next)) next%previous => previous
pos => next
END SUBROUTINE delete_node
!输出串行
SUBROUTINE print_nodes(head)
TYPE(node),POINTER :: head
TYPE(node),POINTER :: p
p => head
DO WHILE(ASSOCIATED(p))
WRITE(*,*) p%content
p => p%next
END DO
END SUBROUTINE print_nodes
!释放整个串行的内存
SUBROUTINE delete_nodes(head)
TYPE(node),POINTER :: head,p_temp
DO WHILE(ASSOCIATED(head))
p_temp => head%next
DEALLOCATE(head)
head => p_temp
END DO
END SUBROUTINE delete_nodes
END MODULE nodes_module
PROGRAM main
USE nodes_module
IMPLICIT NONE
TYPE(node),POINTER :: head
TYPE(node),POINTER :: pos
TYPE(node),POINTER :: p,p_temp
INTEGER i,n
WRITE(*,*) "请输入节点数量:"
READ(*,*) n
ALLOCATE(head)
head%previous => NULL()
head%next => NULL()
WRITE(*,*) "请输入第 1个节点数据:"
READ(*,*) head%content
p => head
DO i = 2,n
ALLOCATE(p%next)
p_temp => p
p => p%next
p%previous => p_temp
WRITE(*,"('请输入第',I2,'个节点数据:')") i
READ(*,*) p%content
END DO
p%next => NULL()
CALL print_nodes(head)
!删掉第三条数据
CALL delete_node(head%next%next)
CALL print_nodes(head)
!在第三个位置插入数据
ALLOCATE(pos)
pos%content = 30
CALL insert_after_node(head%next,pos)
CALL print_nodes(head)
!释放整个串行的内存
CALL delete_node(head)
END PROGRAM main
在读取文件时,有时候我们事先不确定文件中会有多少条数据,就不能用数组来读取,因为不知道数组该声明成多大,这时可以用串行来很好地解决这个问题。现在有两个文件数据
data1.txt
和data2.txt
记录了两个人数不同的班级学生的考试成绩,截取其中一部分如下图所示,现在要编写一个可以读取成绩的程序,让用户输入文件名来决定要读取哪一个文件,还要提供给用户通过座位号来查询成绩的功能。
MODULE nodes_module
IMPLICIT NONE
TYPE student
INTEGER :: id
INTEGER :: chinese
INTEGER :: math
INTEGER :: english
END TYPE student
TYPE node
TYPE(student) :: s
TYPE(node),POINTER :: next
END TYPE node
CONTAINS
!根据id查找学生并返回串行位置
FUNCTION search_student(head,id) RESULT(pos)
TYPE(node),POINTER :: head
INTEGER :: id
TYPE(node),POINTER :: pos
pos => NULL()
DO WHILE(ASSOCIATED(head))
IF(head%s%id == id) THEN
pos => head
RETURN
END IF
head => head%next
END DO
END FUNCTION search_student
END MODULE nodes_module
PROGRAM main
USE nodes_module
IMPLICIT NONE
CHARACTER(20) :: file_name
CHARACTER(79) :: header !记录文件的第一行
TYPE(node),POINTER :: head,p,pos
INTEGER :: id,n,read_state = 0
WRITE(*,*) "请输入文件名:"
READ(*,*) file_name
OPEN(10,file = file_name,STATUS = 'old')
READ(10,"(A79)") header
ALLOCATE(head)
head%next => NULL()
READ(10,*) head%s
p => head
n = 0 !记录学生人数
DO WHILE(read_state == 0)
ALLOCATE(p%next)
p => p%next
n = n + 1
READ(10,FMT = *,IOSTAT = read_state) p%s
END DO
WRITE(*,"('一共有',I2,'名学生。')") n
WRITE(*,*) "请输入想要查询的学生的ID:"
READ(*,*) id
pos => search_student(head,id)
IF(ASSOCIATED(pos)) THEN
WRITE(*,"('查询结果如下:',/,A79)") header
WRITE(*,"(4(I3,2X))") pos%s%id,pos%s%chinese,pos%s%math,pos%s%english
ELSE
WRITE(*,*) "未找到该学生!"
END IF
END PROGRAM main
《 F O R T R A N S Y N T A X 》 系 列 博 客 创 作 参 考 资 料 来 源 《FORTRAN\ SYNTAX》系列博客创作参考资料来源 《FORTRAN SYNTAX》系列博客创作参考资料来源
- 《Fortran95程序设计》.彭国伦.中国电力出版社.
- 《工程分析程序设计》.陈斌、周屈兰.西安交通大学出版社.
- 《Fortran程序设计(第四版)》.Stephen J.Chapman.中国电力出版社.
博 客 创 作 : A i d e n L e e 博客创作:Aiden\ Lee 博客创作:Aiden Lee
特别声明:文章仅供学习参考,转载请注明出处,严禁盗用!