参考:彭国伦:《Fortan 95程序设计》,第10章,2002年
指针最简单的应用可以用来保存变量,或者是动态使用内存。更进一层则可以应用在特别的“数据结构”上,例如创建“串行结构”、“树状结构”等等。
目录:
- 指针的基本概念指针数组指针与函数指针的基本应用指针的高级应用
- 单向串行双向串行、环状串行插入及删除串行的应用
1. 指针的基本概念
指针变量用来保存一个“内存地址”,当程序读写指针变量时,实际上会经过两个步骤:
(1)取出指针中所保存的内存地址;
(2)到这个内存地址读写数据。
指针变量中所保存的内存地址来源可以有两种:
(1)记录其他非指针变量的内存地址;
(2)程序执行中动态配置的一块内存。
实例1:通过指针访问所指向的变量
! 指针的简单使用,访问、修改所指向的变量
subroutine pointer_visit()
implicit none
integer, target :: a = 1 ! 可被指针指向的整型变量
integer, target :: b = 9999
integer, pointer :: pa ! 可指向整型的指针变量
pa => a ! 将指针指向整型变量
! 通过指针变量访问其指向的变量,包含两步
! step 1: 通过指针变量所存储的地址访问内存
! step 2: 获得该内存中的内容(依赖所定义的指针类型)
write(*,*) pa
! 修改整型变量,对指针进行解析的结果相应发生变量
! 实际为step 2结果不同,指针所存储的地址并未变化
a = 2
write(*,*) pa
! 通过指针变量修改所指向的变量
pa = 3
write(*,*) a
! 把指针重新指向别的整型变量,b
pa => b
write(*,*) b
end subroutine pointer_visit
输出结果为:
1
2
3
9999
实例2:通过指针动态配置内存
! 通过指针动态配置内存
subroutine pointer_dynamic()
implicit none
integer, pointer :: pa
allocate(pa) ! 配置一块可以存放整型的内存空间给指针pa
pa = 10 ! 该内存空间存储的是整数10
write(*,*) pa ! 访问指针所指向的内存的存储内容
deallocate(pa) ! 释放该动态内存空间
end subroutine pointer_dynamic
要点:
1. 函数allocate可用来配置内存空间给可变大小的数组使用(定义数组时须使用allocatable来修饰),此处案例表明allocate还可用来配置一块内存空间给指针使用,并将该内存的地址存放在指针p中。
2. 指针是变量,可重新设置它的指向(如实例1中所示)。但通过allocate指向内存的指针在重定向前,需通过deallocate将内存释放,否则在程序结束前,仍占计算机的内存。
???使用动态内存的场景???
注意:
使用指针前,一定要先设置好指针的目标。
否则,在Windows系统下可能会先死一堆内存地址,显示内存读写不正常的信息。在UNIX系统下会出现Segmentation fault的错误信息。
示例代码:
... integer, pointer :: pb write(*,*) pb ...
编译时并不会进行指针未指向的检查,但是在运行时的显示错误信息:
引发了异常: 读取访问权限冲突。 **PB** 是 0xCCCCCCCC。
Fortran提供ASSOCIATED, NULLFY, NULL等函数来完善对未指向的指针的检测、处理。
ASSOCIATED(pointer, [target])
检查指针pointer是否设置指向(如果输入两D个变量则为是否指向target),返回值为布尔变量。
NULLIFY(pointer1, [pointer2, ...])
用来把指针设置成还没指向任何内存地址。
NULL()
返回一个不能使用的内存地址,用来对指针初始化。Fortran 95添加的。
NULLIFY及NULL函数是为了确保ASSOCIATED函数不会出错,?即意味着未使用NULLIFY和NULL函数的情况下,即使指针未指向,ASSOCIATED也可能判断为已经指向某个内存了?与编译器和操作系统有关? <== 见下例,未明确为空指针,判断结果为TRUE
启示:使用指针时,应使用NULL,NULLIFY初始化
示例:
! 测试ASSOCIATED, NULLIFY, NULL函数
subroutine pointer_null()
implicit none
integer, target :: a = 10
integer, pointer :: pa
integer, pointer :: pb => null() ! 初始化指针为空指针
! 未明确指向的指针, T
write(*,*) associated(pa)
nullify(pa)
! 明确为空指针, F
write(*,*) associated(pa)
! 指向整型变量的指针, T
pa => a
write(*,*) associated(pa)
! 明确为空指针, F
write(*,*) associated(pb)
end subroutine pointer_null
输出结果为:
T F T F
是否使用NULL, NULLIFY的区别如下,断点设置在变量声明结束后:
指针可以声明成任何数据类型,甚至是使用TYPE自定义的数据类型。但无论指针指向哪一种数据类型,指针变量所占用的的内存空间均相同。比如:在32位机器上,记录一个内存地址,固定需要使用32 bits = 4 bytes的空间。
2. 指针数组
指针可指向数组,也称为指针数组。
实例1:通过指针访问数组
! 指向数组的指针
subroutine pointer_array1()
implicit none
integer, target :: a(5)=(/1, 2, 3, 4, 5/)
integer, pointer :: pa(:) ! 指向数组的指针声明时只需明确维数
! 数组指针pa指向数组a
pa=>a
write(*,*) pa
! 数组指针pa指向数组的部分a
pa=>a(1:3)
write(*,*) pa
pa=>a(1:5:2)
write(*,*) pa
pa=>a(5:1:-1)
write(*,*) pa
end subroutine pointer_array1
输出结果为:
1 2 3 4 5
1 2 3
1 3 5
5 4 3 2 1
思考一个问题:
为何可以指向数组的一部分?
数组指针pa所存储的应该不止是数组a的地址,还应当包括依据该地址所进行解析的顺序,比如5:1:-1;亦或是存储的是一串地址?但这样似乎不符合上一节所说的,即指针变量大小相同。
实例2:通过指针动态分配内存以存储数组
注意,在声明多维数组是,若附加了TARGET属性,数组大小必须明确,否则编译错误。
声明如下:
integer, target :: array_3d(:,:,:) ! 3维数组
编译错误提示如下:
error #6596: If a deferred-shape array is intended,
then the ALLOCATABLE or POINTER attribute is missing;
if an assumed-shape array is intended,
the array must be a dummy argument. [ARRAY_3D]
大意为:
若想要一个延迟定型(deferred-shape)的数组,ALLOCATABLE或POINTER属性必不可少;
若想要一个确定大小(assumed-shape)的数组,数组必须是哑元参数(dummy argument)。
故使用TARGET属性时,应当明确数组大小。
实例2:通过指针动态开辟内存空间
! 通过指针动态开辟内存以存储数组
subroutine pointer_array2()
implicit none
integer, pointer :: pa_1d(:) ! 1维指针数组
integer, pointer :: pa_2d(:,:) ! 2维指针数组
integer, pointer :: pa_3d(:,:,:) ! 3维指针数组
integer, target :: array_3d(4,3,2) ! 3维数组
integer :: index_i
! 开辟内存时应当明确数组大小(维数不变)
allocate(pa_1d(3))
pa_1d = (/1, 2, 3/)
write(*,"(3I2,/)") pa_1d
deallocate(pa_1d) ! 使用完毕,应当释放内存
forall(index_i=1:3)
array_3d(:,index_i,1) = index_i
array_3d(:,index_i,2) = index_i+3
end forall
write(*, "( 2( 3(4I2,/), / ) )") array_3d
! pa_2d指向array_3d的部分,一个4*3的二维数组
pa_2d=>array_3d(:,:,1)
write(*, "(3(4I2,/))") pa_2d
! pa_2d指向array_3d的部分,一个2*3的二维数组
pa_2d=>array_3d(1:4:2,:,1)
write(*, "(3(2I2,/))") pa_2d
! pa_3d指向array_3d的部分,一个3*3*2的三维数组
pa_3d=>array_3d(1:3,1:3,1:2)
write(*, "(2(3(3I2,/),/))") pa_3d
end subroutine pointer_array2
输出结果为:
1 2 3 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 6 6 6 1 1 1 1 2 2 2 2 3 3 3 3 1 1 2 2 3 3 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6
ALLOCATABLE数组 vs 指针数组
ALLOCATABLE数组在函数结束时(比如在某个函数中定义),自动DEALLOCATE;
而指针数组(此处说的是pa_1d这种情况),除非代码显式的DEALLOCATE,否则一直存在于内存中(?函数之外如何访问呢?浪费内存)
3. 指针与函数
按书上所介绍的,使用interface较为繁琐,跳过,直接使用module封装,
module func
contains
! 寻找最小数
! 参数:p为指针
! 函数的返回为指针
function getmin(p)
implicit none
integer, pointer :: p(:)
integer, pointer :: getmin
! local variable
integer :: index_i, length_i, min_i
! 初始化指针
getmin => null()
! 获取指针数组的长度
length_i = size(p,1)
! 给定min_i的初值
min_i = p(1)
! 遍历指针数组
do index_i = 1, length_i
if( p(index_i) p(index_i)
end if
end do
end function getmin
end module func
program Chapter10
use func
implicit none
! Variables
integer, target :: a(5) = (/9, 10, 4, 88, -1/)
integer, pointer :: pa(:)
! use module func !
pa => a
write(*, "('The min of', 5I5, ' is:', /, I2)") pa, getmin(pa)
stop
end program Chapter10
输出结果为:
The min of 9 10 4 88 -1 is: -1



