《Fortran-第十一讲.pptx》由会员分享,可在线阅读,更多相关《Fortran-第十一讲.pptx(53页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Fortran程序设计第十一讲第十一讲 派生数据类型派生数据类型 指针与动态分配指针与动态分配复习第十讲内容复习第十讲内容SAVE语句和属性语句和属性永久地保存局部变量和数组的值永久地保存局部变量和数组的值采用采用SAVE属性属性INTEGER,SAVE:count采用采用SAVE语句语句SAVE:count在类型声明语句中初始化在类型声明语句中初始化INTEGER:count=1内部过程内部过程PROGRAM main.CONTAINSSUBROUTINE localsub1(var1,var2).END SUBROUTINE localsub1END PROGRAM main递归过程递归过
2、程RECURSIVE SUBROUTINE sub()RECURSIVE FUNCTION f()例:例:RECURSIVE INTEGER function fact(n)RESULT(answer)IMPLICIT NONEINTEGER:nIF(n=1)THENanswer=n*fact(n-1)ELSEanswer=1END IF11.1 派生数组类型派生数组类型问题的引入:问题的引入:假设我要写一个程序管理我们这个班级的学生成绩假设我要写一个程序管理我们这个班级的学生成绩涉及到这么一些数据:涉及到这么一些数据:学号、姓名、作业分、实验分、期中成绩、期末成绩学号、姓名、作业分、实验分、
3、期中成绩、期末成绩怎么实现这些数据的存储?怎么实现这些数据的存储?一个办法是:声明一个办法是:声明6个一维数组,分别用来存放这个一维数组,分别用来存放这6个个方面的信息,并且约定下标相同的数组元素代表着方面的信息,并且约定下标相同的数组元素代表着同一个学生的不同信息同一个学生的不同信息另外的办法是我们自定义一种数据类型另外的办法是我们自定义一种数据类型这个数据类型的每个数据应该包含有以上的这个数据类型的每个数据应该包含有以上的6个要素个要素在这种情况下,学号、姓名等等都是某个数据的一在这种情况下,学号、姓名等等都是某个数据的一个方面个方面定义定义定义一个名为定义一个名为student的数据类型
4、的数据类型TYPE:studentCHARACTER(len=10):idCHARACTER(len=8):nameREAL:homeworkREAL:experimentREAL:midtermREAL:final_examEND TYPE student声明变量声明变量因为自定义的数据类型是由内置数据类型组成的,因为自定义的数据类型是由内置数据类型组成的,所以,也称之为派生数据类型所以,也称之为派生数据类型现在我们有了现在我们有了student这种派生数据类型这种派生数据类型如果我想定义一个变量代表某个学生:如果我想定义一个变量代表某个学生:TYPE(student):tom如果要把所有的
5、学生信息都储存起来:如果要把所有的学生信息都储存起来:TYPE(student):course(70)声明有名常量声明有名常量也可以定义派生数据类型的常量:也可以定义派生数据类型的常量:TYPE(student),PARAMETER:PETER=&student(2010123456,Peter,90.,80.,87.5,85.)派生数据类型可以作为其它派生数据类型的元素派生数据类型可以作为其它派生数据类型的元素使用使用想知道想知道tom的期中考试成绩:的期中考试成绩:WRITE(*,*)tom%midterm将第将第5名学生的期末成绩输入:名学生的期末成绩输入:course(5)%final
6、_exam=100所有同学的期末成绩都是所有同学的期末成绩都是100:course%final_exam=100以上都是对派生数据的元素进行操作以上都是对派生数据的元素进行操作如果将派生数据作为一个整体操作,以前所学习的如果将派生数据作为一个整体操作,以前所学习的内置操作符(加减乘除之类)都不再适合内置操作符(加减乘除之类)都不再适合输入与输出输入与输出如前所述,如前所述,student是一个派生数据类型是一个派生数据类型如果我们用这个类型声明一个变量如果我们用这个类型声明一个变量tom:TYPE(student):tom那么:那么:WRITE(*,*)tom会将会将tom的的6个元素按顺序输
7、出个元素按顺序输出当然,也可以格式化输出:当然,也可以格式化输出:WRITE(*,100)tom100 FORMAT(1X,A,/,1X,A,/,4(1X,F4.1,/)至于输入语句至于输入语句READ,类似的,也是按照数据类型,类似的,也是按照数据类型中元素的顺序按类型读入中元素的顺序按类型读入READ(*,*)tom当然,任何时候,都可以对此数据类型的变量进行当然,任何时候,都可以对此数据类型的变量进行单个元素的读写操作单个元素的读写操作例:例:READ(*,*)tom%nameWRITE(*,*)tom%id在模块中声明派生数据类型在模块中声明派生数据类型问题的提出:问题的提出:如果我们
8、希望在程序中使用某种派生数据类型如果我们希望在程序中使用某种派生数据类型那么就不得不在任何使用该类型的过程中重复书写那么就不得不在任何使用该类型的过程中重复书写该类型的定义形式该类型的定义形式为了解决这个问题,通常将一个程序中所有的派生为了解决这个问题,通常将一个程序中所有的派生数据类型定义在一个模块中,然后在需要使用的数据类型定义在一个模块中,然后在需要使用的地方地方use这个模块这个模块在学习数组时,我们用长度为在学习数组时,我们用长度为n的一维数组表示一个的一维数组表示一个n维向量维向量这个一维这个一维n长的数组可以理解成一个有长的数组可以理解成一个有n个相同元素个相同元素的派生数据类型
9、的派生数据类型用数组的处理方法是有一定的局限性的,如果向量用数组的处理方法是有一定的局限性的,如果向量的每个维度不是同一类型,就不适合了的每个维度不是同一类型,就不适合了下面我们从简单的二维向量的处理开始学习如何在下面我们从简单的二维向量的处理开始学习如何在模块中声明派生数据类型以及使用它模块中声明派生数据类型以及使用它问题:创建一个包含二维向量数据类型的模块以及问题:创建一个包含二维向量数据类型的模块以及两个完成向量加减法的函数两个完成向量加减法的函数第一步:第一步:创建一个容纳二维向量的派生数据类型:创建一个容纳二维向量的派生数据类型:TYPE:vectorREAL:xREAL:yEND
10、TYPE vector定义两个函数定义两个函数vector_add和和vector_sub,分别完成加,分别完成加法和减法法和减法这两个函数的返回值应该也是这两个函数的返回值应该也是vector所以我们可以这样写代码:所以我们可以这样写代码:TYPE(vector)FUNCTION vector_add(v1,v2)IMPLICIT NONETYPE(vector),INTENT(IN):v1,v2vector_add%x=v1%x+v2%xvector_add%y=v1%y+v2%yEND FUNCTION vector_addTYPE(vector)FUNCTION vector_sub(
11、v1,v2)IMPLICIT NONETYPE(vector),INTENT(IN):v1,v2vector_sub%x=v1%x-v2%xvector_sub%y=v1%y-v2%yEND FUNCTION vector_sub现在把这前面写的代码放在一个模块中现在把这前面写的代码放在一个模块中MODULE vector_moduleIMPLICIT NONETYPE:vector.END TYPE vectorCONTAINSTYPE(vector)FUNCTION vector_add(v1,v2).END FUNCTION vector_addTYPE(vector)FUNCTION
12、vector_sub(v1,v2).END FUNCTION vector_subEND MODULE vector_module测试程序测试程序PROGRAM testuse module_vectorIMPLICIT NONETYPE(vector):v1,v2WRITE(*,*)Enter the first vector(x,y):READ(*,*)v1%x,v1%yWRITE(*,*)Enter the second vector(x,y):READ(*,*)v2%x,v2%yWRITE(*,100)vector_add(v1,v2)100 FORMAT(1X,The sum of
13、the points is(,F8.2,F8.2,)WRITE(*,110)vector_sub(v1,v2)110 FORMAT(1X,The difference of the points is(,F8.2,F8.2,)END PROGRAM test_vector问题:问题:编写函数计算平面上两点间的距离编写函数计算平面上两点间的距离应用举例应用举例问题:问题:在物理研究中,我们在研究质点运动时,通过间隔在物理研究中,我们在研究质点运动时,通过间隔一个固定时间测量质点在空间的坐标,得到一系一个固定时间测量质点在空间的坐标,得到一系列的数据列的数据编写程序,通过对这些数据进行计算,得到质
14、点在编写程序,通过对这些数据进行计算,得到质点在某个时间点运动的速度和加速度某个时间点运动的速度和加速度问题分析:问题分析:每条测量数据是由时间和三维坐标组成每条测量数据是由时间和三维坐标组成可以用可以用t,x,y,z 表示表示我们可以写成一个自定义的数据类型我们可以写成一个自定义的数据类型TYPE:pos_tREAL:timeREAL:xREAL:yREAL:zEND TYPE pos_tN组测量数据可以放置在一个数组内:组测量数据可以放置在一个数组内:TYPE(pos_t):measure(N)假设测量的时间间隔是假设测量的时间间隔是0.1秒秒采用的计算模型:采用的计算模型:式中,式中,h
15、是每组数据之间的时间间隔是每组数据之间的时间间隔 是相邻的几组测量数据是相邻的几组测量数据MODULE example1IMPLICIT NONE TYPE:pos_tREAL:timeREAL:xREAL:yREAL:zEND TYPE pos_tCONTAINS SUBROUTINE velocity_acceleration(m,n,k)IMPLICIT NONEINTEGER n,kTYPE(pos_t):m(n)INTEGER iREAL:vx,vy,vz,ax,ay,azIF(kn-3)THENWRITE(*,*)Cant calculate this point!RETURNEN
16、D IF vx=m(k-2)%x-8*m(k-1)%x+8*m(k+1)%x-m(k+2)%xvx=vx/12/0.1vy=m(k-2)%y-8*m(k-1)%y+8*m(k+1)%y-m(k+2)%yvy=vy/12/0.1vz=m(k-2)%z-8*m(k-1)%z+8*m(k+1)%z-m(k+2)%zvz=vz/12/0.1ax=11*m(k-1)%x-20*m(k)%x+6*m(k+1)%x+4*m(k+2)%x-m(k+3)%xax=ax/12/0.01ay=11*m(k-1)%y-20*m(k)%y+6*m(k+1)%y+4*m(k+2)%y-m(k+3)%yay=ay/12/0
17、.01az=11*m(k-1)%z-20*m(k)%z+6*m(k+1)%z+4*m(k+2)%z-m(k+3)%zaz=az/12/0.01WRITE(*,(3F16.8)vx,vy,vzWRITE(*,(3F16.8)ax,ay,azEND SUBROUTINE END MODULE11.2 指针指针n数据保存在内存中数据保存在内存中n当我们需要修改数据时,首先要确定数据在内存当我们需要修改数据时,首先要确定数据在内存中的存储位置中的存储位置n变量名就是存储位置的别名n通过变量名我们找到数据存储的位置n使用指针是另外一种确定数据存储位置的方法使用指针是另外一种确定数据存储位置的方法n指针也
18、是变量指针也是变量n指针保存的不是数据,它包含另一变量在内存中指针保存的不是数据,它包含另一变量在内存中的地址的地址n指针变量的声明方式:指针变量的声明方式:REAL,POINTER:p1 这条语句声明一个指向实型的指针变量这条语句声明一个指向实型的指针变量p1TYPE(vector),POINTER:vector_pointer指向派生数据的指针指向派生数据的指针指针可以指向同类型的变量指针可以指向同类型的变量但是,要求被指向的变量被声明为目标变量但是,要求被指向的变量被声明为目标变量例如:例如:声明变量声明变量a为目标变量为目标变量REAL,TARGET:a或或:REAL:aTARGET:
19、a这一点和这一点和C C语言有区别语言有区别Fortran:Fortran:u有些变量会在编译过程中被优化掉有些变量会在编译过程中被优化掉u指针指向一个消失掉的变量会出错指针指向一个消失掉的变量会出错11.2.1 指针赋值语句指针赋值语句pointer=target例如:例如:REAL,POINTER:pREAL,TARGET:a=3.p=a运行结果称指针运行结果称指针p与变量与变量a相关联相关联p p单元内存储的是变量单元内存储的是变量a a在内在内存中的地址存中的地址p=4p=4修改的是修改的是p p指向的内存单元的值指向的内存单元的值等价于等价于a=4a=4REAL,POINTER:p1
20、,p2REAL,TARGET:a=3.,b=4.p1=ap2=p1此时此时p2的值?的值?Pointer1=Pointer2将一个指针的值赋给另一个指针将一个指针的值赋给另一个指针结果是它们指向同一个内存单元结果是它们指向同一个内存单元4.03.0b的地址aba的地址p1p2REAL,POINTER:p1,p2REAL,TARGET:a=3.,b=4.p1=ap2=bp1=p2代码中代码中p1=p2等价于等价于a=b11.2.2 指针关联状态指针关联状态如果如果p没有关联到任意目标变量,那么没有关联到任意目标变量,那么p不能使用不能使用内置逻辑函数内置逻辑函数ASSOCIATED可以判断可以判
21、断p是否关联了某个目标变量是否关联了某个目标变量Status=ASSOCIATED(pointer)如果指针有关联变量,返回逻辑值真值,否则假如果指针有关联变量,返回逻辑值真值,否则假也可以这样用也可以这样用Status=ASSOCIATED(pointer,target)例:例:Status=ASSOCIATED(p,a)如果如果p和和a关联,那么关联,那么Status为真,否则为假为真,否则为假NULLIFY语句语句用来断开指针和变量之间的关联用来断开指针和变量之间的关联例:例:NULLIFY(p1,p2,p3)内置函数内置函数NULL()用来将指针置为空用来将指针置为空例:例:REAL,
22、POINTER:p1=NULL(),p2=NULL()关于ASSOCIATED的补充说明:在具体应用时,我们通常使用NULLIFY和NULL()来确保声明的指针未关联任何一个地址空间,如果不做这个工作,用ASSOCIATED查询指针状态,会返回逻辑真,原因在于此时指针变量有一个不确定的值,它未定义但是有关联到某个地址11.2.3 数组指针数组指针指针也可以指向数组指针也可以指向数组INTEGER,POINTER:ptr1(:)INTEGER,POINTER:ptr2(:,:)不需要指定数组每维的宽度不需要指定数组每维的宽度ptr1可以用来关联任意一个一维数组可以用来关联任意一个一维数组ptr2
23、可以用来关联任意一个二维数组可以用来关联任意一个二维数组例:例:REAL,TARGET:array1(100,200)REAL,POINTER:ptr(:,:)ptr=array INTEGER:iINTEGER,TARGET:info=(/(i,i=1,16)/)INTEGER,POINTER:ptr1(:),ptr2(:),ptr3(:),ptr4(:)ptr1=infoptr2=ptr1(2:2)ptr3=ptr2(2:2)ptr4=ptr3(2:2)WRITE(*,(16I3)ptr1WRITE(*,(16I3)ptr2WRITE(*,(16I3)ptr3WRITE(*,(16I3)p
24、tr4程序运行结果?11.2.4 用指针改善程序性能用指针改善程序性能交换两个数组交换两个数组array1和和array2,代码如下:,代码如下:REAL:array1(100,100),array2(100,100),temp(100,100).temp=array1array1=array2array2=temp代码足够简单,但每代码足够简单,但每条赋值语句要移动条赋值语句要移动10000个实型变个实型变量量,大大降低了效率,大大降低了效率用指针改写:用指针改写:REAL,TARGET:array1(100,100),array2(100,100)REAL,POINTER:p1(:,:),
25、p2(:,:),temp(:,:)p1=array1p2=array2.temp=p1p1=p2p2=temp只需要交换地址,移动两小到可以忽略只需要交换地址,移动两小到可以忽略在进行大量数据交换时,在进行大量数据交换时,交换指向数据的指针交换指向数据的指针通常要比操作数据本身更高效通常要比操作数据本身更高效11.3 动态内存分配动态内存分配我们已经接触过可分配数组(动态数组):我们已经接触过可分配数组(动态数组):REAL,ALLOCATABLE:array(:).ALLOCATE(array(100),STAT=status).DEALLOCATE(array,STAT=status)指针
26、最强大的功能之一:指针最强大的功能之一:在任何需要的时候动态创建变量或数组在任何需要的时候动态创建变量或数组在使用完毕后,释放动态变量或数组所使用的空间在使用完毕后,释放动态变量或数组所使用的空间分配语句和动态数组分配类似:分配语句和动态数组分配类似:ALLOCATE(pointer(size),.,STAT=status)例如:例如:ALLOCATE(p)ALLOCATE(ptr(100)这么做有什么不同?这么做有什么不同?分配语句创建了一个未命名的数据对象分配语句创建了一个未命名的数据对象它有指定的长度和指针类型它有指定的长度和指针类型指针指向该对象指针指向该对象只能通过该指针访问只能通过
27、该指针访问内存泄漏内存泄漏:如果指向该片内存的指针被置为空或和其它目标关联,如果指向该片内存的指针被置为空或和其它目标关联,那么程序就不能再访问这片内存中的数据了,系统也那么程序就不能再访问这片内存中的数据了,系统也不会回收利用,这种无用内存称为不会回收利用,这种无用内存称为“内存泄漏内存泄漏”通过通过DEALLOCATE回收内存,可以将回收回来的内存回收内存,可以将回收回来的内存重新分配使用重新分配使用11.4 指针作为派生数据类型的元素指针作为派生数据类型的元素链表:链表:TYPE:linklistREAL:dataTYPE(linklist),POINTER:nextEND TYPEli
28、nklist这个派生数据类型称为链表这个派生数据类型称为链表它有一个元素它有一个元素next是一个指针指向自身类型的数据是一个指针指向自身类型的数据指针从一个变量指向下一个变量,最后一个变量的指针从一个变量指向下一个变量,最后一个变量的指针是空的指针是空的linklist图示图示12.0next11.0next10.0next13.0空headhead称为链表的头,通过这个头,我们可以访问到链表所有的数据应用问题:应用问题:需要从键盘上接收一些实验数据,并按照从小到大需要从键盘上接收一些实验数据,并按照从小到大的顺序存储起来,事先并不知道有多少个数据的顺序存储起来,事先并不知道有多少个数据分析
29、:分析:因为不知道数据个数,按照以往的做法,我们只能因为不知道数据个数,按照以往的做法,我们只能定义一个足够大的数组来预备存放这些数据定义一个足够大的数组来预备存放这些数据采用链表能比较好的解决问题采用链表能比较好的解决问题算法:算法:WHILE读入数据,并存储到读入数据,并存储到tempIF 读入数据为某个特定值读入数据为某个特定值 EXITALLOCATE新数据项,并保存新数据项,并保存temp插入数据项到链表的合适位置插入数据项到链表的合适位置END OF WHILE11.5.1 程序阅读程序阅读PROGRAM exampleTYPE:ptrREAL,POINTER:p(:)END TY
30、PETYPE(ptr):p1(4)REAL,TARGET:a(4)=(/1.,2.,3.,4./)REAL,TARGET:b(2)=(/5.,6./)REAL,TARGET:c(3)=(/7.,8.,9./)REAL,TARGET:d(5)=(/10.,11.,12.,13.,14./)p1(1)%p=ap1(2)%p=bp1(3)%p=cp1(4)%p=dWRITE(*,(F6.1,/)p1(1)%p(2)+p1(4)%p(4)+p1(3)%p(3)DO i=1,4WRITE(*,(5F6.1)p1(i)%pEND DOEND PROGRAM EXAMPLE11.5.2 程序举例程序举例PR
31、OGRAM exampleIMPLICIT NONEINTEGER:iREAL,TARGET:info(-25:25)=(/(2.1*i,i=-25,25)/)REAL,POINTER:ptr1(:),ptr2(:),ptr3(:)ptr1=info(-25:25:5)ptr2=ptr1(1:2)ptr3=ptr2(3:5)WRITE(*,(A,11F6.1)ptr1=,ptr1WRITE(*,(A,11F6.1)ptr2=,ptr2WRITE(*,(A,11F6.1)ptr3=,ptr3WRITE(*,(A,11F6.1)ave of ptr3=,SUM(ptr3)/SIZE(ptr3)EN
32、D PROGRAM11.5.3 程序举例程序举例前面所讲的链表,可以从表头开始向后查找数据,前面所讲的链表,可以从表头开始向后查找数据,但是困难的是,我如果想从某个数据出发去访问但是困难的是,我如果想从某个数据出发去访问它的前一个数据。它的前一个数据。考虑设计一种双向链表,在派生数据类型的定义中,考虑设计一种双向链表,在派生数据类型的定义中,不仅有指向数据的后一条的指针项,还有指向它不仅有指向数据的后一条的指针项,还有指向它的前一条的指针项。的前一条的指针项。编写程序,输入编写程序,输入20个数,存入这个双向链表中,并个数,存入这个双向链表中,并按输入时的正序和反序分别输出。按输入时的正序和反序分别输出。THE END