07-Fortran基础--Fortran指针(Pointer)的使用


0 引言

  Fortran是一种广泛使用的编程语言,特别适合科学计算和数值分析。Fortran 90引入了指针的概念,允许程序员动态地管理内存,并在程序中创建灵活的数据结构,前面已经简单介绍过指针类型的定义和赋值,这一部分详细讲下指针的几种用法。

1 指针(Poionter)的有关内容

  在Fortran中,指针是一种特殊的变量类型,用于存储内存地址。指针可以指向任何类型的数据,包括标量、数组、派生类、函数和其他变量。以下是一些Fortran中使用指针的基本概念和用法:

1.1 一般类型指针

  下面运行示例中包含了简单类型指针使用的差不多😀所有情况,可以仔细阅读注释进行理解。

	implicit none
    ! - 1 简单指针定义
    real(8),target :: realNum1 = 5.d0, realNum2 = 100.d0
    real(8),pointer :: ptr 
    real(8),pointer :: ptr2 

	! - 1. 简单的单一变量指针(有以下几种使用情况)
	
    ! a 简单指针赋值
    print *,"a:简单指针,ptr获取了realNum1的地址,此时打印ptr和realNum1是相同值"
    ptr => realNum1
    print *,"ptr=",ptr,"realNum1=",realNum1
    
    ! b 在a的基础上修改
    print *,""
    print *,"b:修改realNum1的值,ptr也发生改变,因为ptr => realNum1,使ptr和realNum1指向同一处内存空间"
    realNum1 = 55.d0
    print *,"ptr=",ptr,"realNum1=",realNum1
    
    ! c 在b基础上
    print *,""
    print *,"c:修改ptr的值,realNum1也发生变量,原因是ptr和realNum1地址一样"
    ptr = 60.d0
    print *,"ptr=",ptr,"realNum1=",realNum1
    
    ! d 在c的基础上
    print *,""
    print *,"d:修改ptr指向realNum2,发现此时ptr的值和realNum2相同,而realNum1保持c的结果"
    ptr => realNum2
    print *,"ptr=",ptr,"realNum2=",realNum2,"realNum1=",realNum1
    
    ! e:如果d之后把指针ptr置空 [若要运行将e和f屏蔽]
    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分配指针内存了"
    allocate(ptr2)
    ptr2 = 200.d0  ! 给指针ptr2赋值
    print *,"ptr2=",ptr2
    
    ! h:在g的基础上
    print *,""
    print *,"h:ptr2是指针,被allocate分配过,那是否还有指向的属性? 指向还是存在的,但这时侯会存在一个问题,	ptr2和realNum1都开辟过空间,而ptr2又指向了realNum1的空间,那就有泄露产生了,因为分配的指针是不会自己释放内存的;"
    ptr2 => realNum1
    print *,"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
	
	end 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 => arr1
    print *,"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 = 6
    print *,"arr1",arr1,"p1",p1
    
    ! d:数组指针释放
    print *,""
    print *,"c:可以使用allocate进行内存分配,分配和指向不冲突"
    deallocate(p1)
    if( associated(p1) == .true. )then
        print *,"p1未成功释放"
    else
        print *,"p1已释放"
    endif

	end program

运行结果

1.3 派生类(type)指针

  同样,用示例说明用法,主要介绍了派生类指针的基础用法和赋值

	program test
	implicit none
	! - 3 派生类指针定义
    type :: test_type  !> 定义派生类
        real(8) :: arr(100000)
        real(8) :: arr2(200000)
        real(8),pointer :: ptr3 
    end type
    type(test_type),target :: typeValue
    type(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 =>realNum1
    print *,"指针ptype分配内存,赋初值"
    write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",ptype%arr(1),"    派生类指针的指针",ptype%ptr3
    
    print *,"对于这样的派生类,使用指针是最高效、最省内存的了"
	
	end program

运行结果

1.4 函数指针

  同样,用示例说明用法,主要介绍了调用函数指针计算将函数指针作为参数在函数之间传递

program test
	implicit none
	! - 4 函数指针    
    procedure(f), pointer :: pf
    real(8) :: fv,x
	
	! -4. 函数指针的使用
    ! a: 函数指针的简单使用
    print *
    print *, "函数指针的简单使用"
    pf => f
    print *, 'pf(2) = ', pf(2.d0) ! 调用f(2.)
    pf => g
    print *, 'pf(2) = ', pf(2.d0) ! 调用g(2.)
    print *
    
    ! b: 函数指针可以作为变量进行传值
    pf => f
    x = 100.d0
    call calfun(pf,x,fv)
    write( *,*),"函数指针作为参数的运算结果",fv
    
contains

  real function f(x)
    real(8), intent(in) :: x
    f = x**2
  end function f

  real function g(x)
    real(8), intent(in) :: x
    g = x**3
  end function g
  
  subroutine calfun(fun,x,fv)
    procedure(f),pointer :: fun
    real(8),intent(in) :: x
    real(8),intent(out) :: fv
    
    fv = fun(x)
  end subroutine
  
end program 

2 可运行code

program test_pointer
	implicit none
	
	! - 1 简单指针定义
    real(8),target :: realNum1 = 5.d0, realNum2 = 100.d0
    real(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 type
    type(test_type),target :: typeValue
    type(test_type),pointer :: ptype ! 定义派生类指针
    
    ! - 4 函数指针    
    procedure(f), pointer :: pf
    real(8) :: fv,x
    
    ptr2 => null() ! 指针指向空
    
    ! - 1. 简单的单一变量指针(有以下几种使用情况)
    ! a 简单指针赋值
    print *,"a:简单指针,ptr获取了realNum1的地址,此时打印ptr和realNum1是相同值"
    ptr => realNum1
    print *,"ptr=",ptr,"realNum1=",realNum1
    
    ! b 在a的基础上修改
    print *,""
    print *,"b:修改realNum1的值,ptr也发生改变,因为ptr => realNum1,使ptr和realNum1指向同一处内存空间"
    realNum1 = 55.d0
    print *,"ptr=",ptr,"realNum1=",realNum1
    
    ! c 在b基础上
    print *,""
    print *,"c:修改ptr的值,realNum1也发生变量,原因是ptr和realNum1地址一样"
    ptr = 60.d0
    print *,"ptr=",ptr,"realNum1=",realNum1
    
    ! d 在c的基础上
    print *,""
    print *,"d:修改ptr指向realNum2,发现此时ptr的值和realNum2相同,而realNum1保持c的结果"
    ptr => realNum2
    print *,"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 => realNum1
    print *,"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 => arr1
    print *,"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 = 6
    print *,"arr1",arr1,"p1",p1
    
    ! d:数组指针释放
    print *,""
    print *,"c:可以使用allocate进行内存分配,分配和指向不冲突"
    deallocate(p1)
    if( associated(p1) == .true. )then
        print *,"p1未成功释放"
    else
        print *,"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 =>realNum1
    print *,"指针ptype分配内存,赋初值"
    write(*,"(a,f10.6,a,f10.6)"),"派生类指针下数组",ptype%arr(1),"    派生类指针的指针",ptype%ptr3
    
    print *,"对于这样的派生类,使用指针是最高效、最省内存的了"
    
    ! -4. 函数指针的使用
    ! a: 函数指针的简单使用
    print *
    print *, "函数指针的简单使用"
    pf => f
    print *, 'pf(2) = ', pf(2.d0) ! 调用f(2.)
    pf => g
    print *, 'pf(2) = ', pf(2.d0) ! 调用g(2.)
    print *
    
    ! b: 函数指针可以作为变量进行传值
    pf => f
    x = 100.d0
    call calfun(pf,x,fv)
    write( *,*),"函数指针作为参数的运算结果",fv
    
contains

  real function f(x)
    real(8), intent(in) :: x
    f = x**2
  end function f

  real function g(x)
    real(8), intent(in) :: x
    g = x**3
  end function g
  
  subroutine calfun(fun,x,fv)
    procedure(f),pointer :: fun
    real(8),intent(in) :: x
    real(8),intent(out) :: fv
    
    fv = fun(x)
  end subroutine
  
end program 

  程序中若有不理解的地方可以留言讨论,希望对需要的人有些许帮助。

🕝
🕝🕝
🕝🕝🕝
🕝🕝🕝🕝
🕝🕝🕝🕝🕝
🕝🕝🕝🕝🕝🕝
🕝🕝🕝🕝🕝🕝🕝

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Fortran中的指针是一种特殊的变量,它可以指向其他变量或数组的地址。通过使用指针,我们可以在程序中动态地访问和修改内存中的数据。在Fortran中,指针的声明和使用需要使用特定的语法。 在引用\[1\]中的示例中,我们可以看到如何声明和使用指针。首先,通过声明`integer, pointer :: p`,我们声明了一个指向整数的指针变量p。然后,通过`p=>a`,我们将指针p指向变量a。这样,我们就可以通过指针p来访问和修改变量a的值。 在引用\[2\]中的示例中,我们展示了如何使用指针来访问数组。通过声明`integer, pointer :: pa(:)`,我们声明了一个指向整数数组的指针变量pa。然后,通过`pa=>a`,我们将指针pa指向数组a。这样,我们就可以通过指针pa来访问和修改数组a的元素。我们还展示了如何使用指针来访问数组的部分元素,例如`pa=>a(1:3)`表示指针pa指向数组a的前三个元素。 在引用\[3\]中的示例中,我们展示了如何使用一些特殊的指针操作。通过`nullify(pa)`,我们将指针pa设置为空指针。然后,通过`associated(pa)`,我们可以检查指针pa是否与任何变量关联。在这个例子中,我们还展示了如何将指针指向一个整数变量,例如`pa=>a`。 总之,Fortran中的指针是一种强大的工具,可以用于动态地访问和修改内存中的数据。通过使用指针,我们可以更灵活地处理数组和变量。 #### 引用[.reference_title] - *1* [Fortran学习15:指针1](https://blog.csdn.net/faltas/article/details/127246050)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Fortran笔记,指针-Part1](https://blog.csdn.net/qq_21091051/article/details/122909692)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咋(za)说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值