07-Fortran基础--Fortran指针Pointer的使用
- 0 引言
- 1 指针(Poionter)的有关内容
- 1.1 一般类型指针
- 1.2 数组指针
- 1.3 派生类(type)指针
- 1.4 函数指针
- 2 可运行code
0 引言
Fortran是一种广泛使用的编程语言,特别适合科学计算和数值分析。Fortran 90引入了指针的概念,允许程序员动态地管理内存,并在程序中创建灵活的数据结构,前面已经简单介绍过指针类型的定义和赋值,这一部分详细讲下指针的几种用法。
1 指针(Poionter)的有关内容
在Fortran中,指针是一种特殊的变量类型,用于存储内存地址。指针可以指向任何类型的数据,包括标量、数组、派生类、函数和其他变量。以下是一些Fortran中使用指针的基本概念和用法:
1.1 一般类型指针
下面运行示例中包含了简单类型指针使用
的差不多😀所有情况,可以仔细阅读注释进行理解。
implicit none! - 1 简单指针定义real(8),target :: realNum1 = 5.d0, realNum2 = 100.d0real(8),pointer :: ptr real(8),pointer :: ptr2 ! - 1. 简单的单一变量指针(有以下几种使用情况)! a 简单指针赋值print *,"a:简单指针,ptr获取了realNum1的地址,此时打印ptr和realNum1是相同值"ptr => realNum1print *,"ptr=",ptr,"realNum1=",realNum1! b 在a的基础上修改print *,""print *,"b:修改realNum1的值,ptr也发生改变,因为ptr => realNum1,使ptr和realNum1指向同一处内存空间"realNum1 = 55.d0print *,"ptr=",ptr,"realNum1=",realNum1! c 在b基础上print *,""print *,"c:修改ptr的值,realNum1也发生变量,原因是ptr和realNum1地址一样"ptr = 60.d0print *,"ptr=",ptr,"realNum1=",realNum1! d 在c的基础上print *,""print *,"d:修改ptr指向realNum2,发现此时ptr的值和realNum2相同,而realNum1保持c的结果"ptr => realNum2print *,"ptr=",ptr,"realNum2=",realNum2,"realNum1=",realNum1! e:如果d之后把指针ptr置空 [若要运行将e和f屏蔽]nullify(ptr)print *,""print *,"e:使ptr指向为空,发现打印ptr会报错; 仅仅ptr的指向噶了,并不影响它指向的空间"if(associated(ptr) == .true.)thenprint *,"ptr=",ptr,"realNum2=",realNum2,"realNum1=",realNum1elseprint *,"ptr=,Null ","realNum2=",realNum2,"realNum1=",realNum1endif!! f:如果d之后把指针ptr释放了(deallocate)!print *,""!print *,"f:执行这一步之前将e先屏蔽,使用deallocate释放调指针ptr,从输出结果可以看出,仅仅ptr的指向噶了,并不影响它指向的空间"!deallocate(ptr)!if(associated(ptr) == .true.)then! print *,"ptr=",ptr,"realNum2=",realNum2,"realNum1=",realNum1!else! print *,"ptr=,Null ","realNum2=",realNum2,"realNum1=",realNum1!endif! g:给指针分配内存print *,""print *,"g:既然可以使用deallocate释放指针,那当然也可以用allocate分配指针内存了"allocate(ptr2)ptr2 = 200.d0 ! 给指针ptr2赋值print *,"ptr2=",ptr2! h:在g的基础上print *,""print *,"h:ptr2是指针,被allocate分配过,那是否还有指向的属性? 指向还是存在的,但这时侯会存在一个问题, ptr2和realNum1都开辟过空间,而ptr2又指向了realNum1的空间,那就有泄露产生了,因为分配的指针是不会自己释放内存的;"ptr2 => realNum1print *,"ptr2=",ptr2,"realNum1=",realNum1! i: 在h的基础上print *,""print *,"i:指针ptr和指针ptr2之间的关系是怎样的?让ptr=>ptr2打印结果,发现ptr和ptr2此时指向的为同一空间,值都为realNum1;"ptr=>ptr2 print *,"ptr=",ptr,"ptr2=",ptr2,"realNum1=",realNum1end program
运行结果
1.2 数组指针
同样,用示例说明用法,主要介绍了数组指针基础用法
、用指针进行数组截断
、分析对内存的影响
。
implicit none! - 2 指针数组定义integer,allocatable :: ind(:)real(8),target :: arr1(3),arr2(5)real(8),pointer :: p1(:) !> 指针数组 real(8),pointer :: p2(:) !> 指针数组 ! 先给arr1和arr2赋值call RANDOM_SEED()call RANDOM_NUMBER(arr1)arr2 = 5.d0! a:数组指针简单使用print *,""print *,"a:指针ptr数组简单使用,二者地址一致打印结果相同,同简单类型指针类似,p1和arr1任何一个被该,另一个也随着被改;"p1 => arr1print *,"arr1",arr1,"p1",p1! b:数组指针的特别用法print *,""print *,"b:数组指针可以指向数组中特定索引的元素(a:b),实现拆分数组,指针可以多次指向同一目标, 指向的空间/内存必须是连续的;"p1 => arr1(2:3)print *,"arr1",arr1,"p1",p1! c:分配内存print *,""print *,"c:可以使用allocate进行内存分配,分配和指向不冲突"allocate(p1(6))p1 = 6print *,"arr1",arr1,"p1",p1! d:数组指针释放print *,""print *,"c:可以使用allocate进行内存分配,分配和指向不冲突"deallocate(p1)if( associated(p1) == .true. )thenprint *,"p1未成功释放"elseprint *,"p1已释放"endifend program
运行结果
1.3 派生类(type)指针
同样,用示例说明用法,主要介绍了派生类指针的基础用法和赋值
。
program testimplicit none! - 3 派生类指针定义type :: test_type !> 定义派生类real(8) :: arr(100000)real(8) :: arr2(200000)real(8),pointer :: ptr3 end typetype(test_type),target :: typeValuetype(test_type),pointer :: ptype ! 定义派生类指针! -3. 派生类中指针使用较为常见,因为派生类中可能会同时存在多个大数组,对内存是致命伤害! - a:派生指针的赋值ptype => typeValue ! 先给指针指向目标,此时二者表示同一内存区域call RANDOM_NUMBER(ptype%arr) ! 给派生数组赋值ptype%ptr3 =>realNum1 ! 给指针的指针赋值print *print *,"派生类指针初始化"write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",ptype%arr(1)," 派生类指针的指针",ptype%ptr3! - b:指针ptype内存释放?print *deallocate(ptype)print *,"释放指针ptype,不影响派生变量typeValue前面的赋值"write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",typeValue%arr(1)," 派生类指针的指针",typeValue%ptr3! -c:派生类指针内存分配 print *allocate(ptype)call RANDOM_NUMBER(ptype%arr) ! 指针赋值ptype%ptr3 =>realNum1print *,"指针ptype分配内存,赋初值"write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",ptype%arr(1)," 派生类指针的指针",ptype%ptr3print *,"对于这样的派生类,使用指针是最高效、最省内存的了"end program
运行结果
1.4 函数指针
同样,用示例说明用法,主要介绍了调用函数指针计算
、将函数指针作为参数在函数之间传递
。
program testimplicit none! - 4 函数指针 procedure(f), pointer :: pfreal(8) :: fv,x! -4. 函数指针的使用! a: 函数指针的简单使用print *print *, "函数指针的简单使用"pf => fprint *, 'pf(2) = ', pf(2.d0) ! 调用f(2.)pf => gprint *, 'pf(2) = ', pf(2.d0) ! 调用g(2.)print *! b: 函数指针可以作为变量进行传值pf => fx = 100.d0call calfun(pf,x,fv)write( *,*),"函数指针作为参数的运算结果",fvcontainsreal function f(x)real(8), intent(in) :: xf = x**2end function freal function g(x)real(8), intent(in) :: xg = x**3end function gsubroutine calfun(fun,x,fv)procedure(f),pointer :: funreal(8),intent(in) :: xreal(8),intent(out) :: fvfv = fun(x)end subroutineend program
2 可运行code
program test_pointerimplicit none! - 1 简单指针定义real(8),target :: realNum1 = 5.d0, realNum2 = 100.d0real(8),pointer :: ptr real(8),pointer :: ptr2 ! - 2 指针数组定义integer,allocatable :: ind(:)real(8),target :: arr1(3),arr2(5)real(8),pointer :: p1(:) !> 指针数组 real(8),pointer :: p2(:) !> 指针数组 ! - 3 派生类指针定义type :: test_type !> 定义派生类real(8) :: arr(100000)real(8) :: arr2(200000)real(8),pointer :: ptr3 end typetype(test_type),target :: typeValuetype(test_type),pointer :: ptype ! 定义派生类指针! - 4 函数指针 procedure(f), pointer :: pfreal(8) :: fv,xptr2 => null() ! 指针指向空! - 1. 简单的单一变量指针(有以下几种使用情况)! a 简单指针赋值print *,"a:简单指针,ptr获取了realNum1的地址,此时打印ptr和realNum1是相同值"ptr => realNum1print *,"ptr=",ptr,"realNum1=",realNum1! b 在a的基础上修改print *,""print *,"b:修改realNum1的值,ptr也发生改变,因为ptr => realNum1,使ptr和realNum1指向同一处内存空间"realNum1 = 55.d0print *,"ptr=",ptr,"realNum1=",realNum1! c 在b基础上print *,""print *,"c:修改ptr的值,realNum1也发生变量,原因是ptr和realNum1地址一样"ptr = 60.d0print *,"ptr=",ptr,"realNum1=",realNum1! d 在c的基础上print *,""print *,"d:修改ptr指向realNum2,发现此时ptr的值和realNum2相同,而realNum1保持c的结果"ptr => realNum2print *,"ptr=",ptr,"realNum2=",realNum2,"realNum1=",realNum1!! e:如果d之后把指针ptr置空!nullify(ptr)!print *,""!print *,"e:使ptr指向为空,发现打印ptr会报错,仅仅ptr的指向噶了,并不影响它指向的空间"!if(associated(ptr) == .true.)then! print *,"ptr=",ptr,"realNum2=",realNum2,"realNum1=",realNum1!else! print *,"ptr=,Null ","realNum2=",realNum2,"realNum1=",realNum1!endif!!! f:如果d之后把指针ptr释放了(deallocate)!print *,""!print *,"f:执行这一步之前将e先屏蔽,使用deallocate释放调指针ptr,从输出结果可以看出,仅仅ptr的指向噶了,并不影响它指向的空间"!deallocate(ptr)!if(associated(ptr) == .true.)then! print *,"ptr=",ptr,"realNum2=",realNum2,"realNum1=",realNum1!else! print *,"ptr=,Null ","realNum2=",realNum2,"realNum1=",realNum1!endif! g:给指针分配内存print *,""print *,"g:既然可以使用deallocate释放指针,那当然也可以分配指针"allocate(ptr2)ptr2 = 200.d0 ! 给指针ptr2赋值print *,"ptr2=",ptr2! h:在g的基础上print *,""print *,"h:ptr2是指针,被allocate分配过,那是否还有指向的属性? 指向还是存在的,但这时侯会存在一个问题,&ptr2和realNum1都开辟过空间,而ptr2又指向了realNum1的空间,那就有泄露产生了,因为分配的指针是不会自己释放内存的;"ptr2 => realNum1print *,"ptr2=",ptr2,"realNum1=",realNum1! i: 在h的基础上print *,""print *,"i:指针ptr和指针ptr2之间的关系是怎样的?让ptr=>ptr2打印结果,发现ptr和ptr2此时指向的为同一空间,值都为realNum1;"ptr=>ptr2 print *,"ptr=",ptr,"ptr2=",ptr2,"realNum1=",realNum1! -2. 指针数组的使用! 先给arr1和arr2赋值call RANDOM_SEED()call RANDOM_NUMBER(arr1)arr2 = 5.d0! a:数组指针简单使用print *,""print *,"a:指针ptr数组简单使用,二者地址一致打印结果相同,同简单类型指针类似,p1和arr1任何一个被该,另一个也随着被改;"p1 => arr1print *,"arr1",arr1,"p1",p1! b:数组指针的特别用法print *,""print *,"b:数组指针可以指向数组中特定索引的元素(a:b),实现拆分数组,指针可以多次指向同一目标, 指向的空间/内存必须是连续的;"p1 => arr1(2:3)print *,"arr1",arr1,"p1",p1! c:分配内存print *,""print *,"c:可以使用allocate进行内存分配,分配和指向不冲突"allocate(p1(6))p1 = 6print *,"arr1",arr1,"p1",p1! d:数组指针释放print *,""print *,"c:可以使用allocate进行内存分配,分配和指向不冲突"deallocate(p1)if( associated(p1) == .true. )thenprint *,"p1未成功释放"elseprint *,"p1已释放"endif! -3. 派生类中指针使用较为常见,因为派生类中可能会同时存在多个大数组,对内存是致命伤害! - a:派生指针的赋值ptype => typeValue ! 先给指针指向目标,此时二者表示同一内存区域call RANDOM_NUMBER(ptype%arr) ! 给派生数组赋值ptype%ptr3 =>realNum1 ! 给指针的指针赋值print *print *,"派生类指针初始化"write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",ptype%arr(1)," 派生类指针的指针",ptype%ptr3! - b:指针ptype内存释放?print *deallocate(ptype)print *,"释放指针ptype,不影响派生变量typeValue前面的赋值"write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",typeValue%arr(1)," 派生类指针的指针",typeValue%ptr3! -c:派生类指针内存分配 print *allocate(ptype)call RANDOM_NUMBER(ptype%arr) ! 指针赋值ptype%ptr3 =>realNum1print *,"指针ptype分配内存,赋初值"write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",ptype%arr(1)," 派生类指针的指针",ptype%ptr3print *,"对于这样的派生类,使用指针是最高效、最省内存的了"! -4. 函数指针的使用! a: 函数指针的简单使用print *print *, "函数指针的简单使用"pf => fprint *, 'pf(2) = ', pf(2.d0) ! 调用f(2.)pf => gprint *, 'pf(2) = ', pf(2.d0) ! 调用g(2.)print *! b: 函数指针可以作为变量进行传值pf => fx = 100.d0call calfun(pf,x,fv)write( *,*),"函数指针作为参数的运算结果",fvcontainsreal function f(x)real(8), intent(in) :: xf = x**2end function freal function g(x)real(8), intent(in) :: xg = x**3end function gsubroutine calfun(fun,x,fv)procedure(f),pointer :: funreal(8),intent(in) :: xreal(8),intent(out) :: fvfv = fun(x)end subroutineend program
程序中若有不理解的地方可以留言讨论,希望对需要的人有些许帮助。
🕝
🕝🕝
🕝🕝🕝
🕝🕝🕝🕝
🕝🕝🕝🕝🕝
🕝🕝🕝🕝🕝🕝
🕝🕝🕝🕝🕝🕝🕝