全部博文(287)
分类: 系统运维
2011-02-14 14:00:34
一、概念
1. 什么是指针?
指针是数据在内存或磁盘上的物理地址。因为OS400是统一寻址指令系统,即物理内存与磁盘是统一寻址,所以,指针没有区分内存指针,还是磁盘指针,都是统一的指针。
2. 指针的表示
在OS400下,指针(pointer)是一个16byte,即16字节的符号,是OS自动分配指针和其内容。
3. 指针在计算机语言中的应用范围
指针用在除CL之外的高级开发语言中,如C/C++、RPGLE等。
4. 如何查看指针地址带出的数据?
在RPGLE debug方式下,在命令行对命名的指针,如Ptr,键入:
Eval Ptr :C 100
C表示是字符型方式显示指针带出的数据;X表示16进制显示指针地址带出的数据。
100表示显示数据的长度。顺便说一下,os400下,debug用F11显示程序变量的长度,默认值是1024字节。如果需要查看1024之后的数据,用eval方式,比如字符变量C_Var;eval C_Var : C 8096,在debug方式下,os400就开辟一个8096字节区域存放变量C_Var的显示内容。
5. 如何确定指针是否有效?
在debug方式下,用eval 或F11显示命名的指针,比如Ptr,如果显示为:
PTR = SPP:FC27C1E4F206E1B0,说明这个指针是有效的,否则,OS400会报无效指针信息。
二、指针在RPGLE中的应用
1. 指针在RPGLE程序中的定义
指针在rpgle d表中的定义用符号‘*’进行定义。
在RPGLE中,指针是通过D表进行定义,被指针定义的对象范围较广,通常在RPGLE中用指针定义指向一个数据结构,DS;或字符型变量。比如:
D D_MyDs ds likeds(RefDs)
D based(Ptr)
*
D Ptr s *
或者
D C_String s 1024 varying based(Ptr_2)
D Ptr_2 s *
值得注意的是,在D表用based到指针的数据结构或变量,不能用INZ键字,即不能用程序变量初始赋值键字。
2. 指针的赋值
在rpgle中,指针的赋值可以直接指针间的直接赋值,比如
Ptr = Ptr_2 ;
也可以通过rpgle的专门赋值building function %addr进行赋值比如:
d C_Var2 s 10 inz('ABC')
d Ptr s *
*
/free
Ptr = %addr(C_Var2) ;
这时指针Ptr指向数据位字符长度为10,内容为“ABC”的数据。
3. 指针的应用范围
在rpgle中,通常都是把指针用在程序间传递参数上。
早期的rpg程序基本上都是通过plist对每一个具体参数进行定义。这样的定义对项目联调、修改和代码最终定版,都造成非常大的难度,增加项目的实际开发难度。
我们的成功经验:
在程序间每一个程序代码都定义统一的参数格式,比如
C *entry plist
C parm Ptr_1
C Parm Ptr_2
C Ptrm Ptr_3
这段rpgle代码放在统一的copybook中,在每一程序代码中只要定义:
c/copy QTXTSRC,CMS_CD_PLT
其中,Ptr_1只能用在应用项目的系统变量范围,比如交易日,时间等;
Ptr_2只能用在应用项目的输出参数范围,比如PGMA调用PGMB时,PGMA对PGMB传递的参数只能用PTR_2带出;Ptr_3只能用在返回结果参数范围,即PGMB对PGMA的返回值。
对三个Ptr指针引入的参数结构,都可以放在统一的copybook中,在程序代码中直接进行copy定义。这样做的好处,一个应用项目的数据结构是唯一的。
4. 指针在QAPI中的应用
指针在QUSPTRUS,QAPI中的编程,是把调用的QAPI引入的信息放在一个数据空间中,再通过专门的QAPI把这些数据用过指针引用。
5. 用指针编偏移取数据
通常情况下,最简单的指针应用就是一个指针直接指向数据,比如:
D D_inDs ds likeds(D_Mark1Ds)
D D_outDs ds likeds(D_Mark2Ds) based(Ptr_3)
当程序代码进行调用后,
/free
….
D_inDs.C_fld = ‘ABC’ ;
….
//把数据结构D_Mark1Ds的地址赋值给指针Ptr_2。
Ptr_2 = %addr(D_Mark1Ds) ;
Callp CMSFTZ03FR(ptr_1:ptr_2:ptr_3) ;
调用CMSFTZ03FR之后,Ptr_3就直接把返回参数引入到程序数据结构D_outDs中了,直接引用。
如果程序的返回结果是包括一个以上的数据结构,比如通过Ptr_3返回的参数如下:
D D_outDs ds based(Ptr_3)
D D_DataDs1 likeds(RefDataDs1)
D D_DataDs2 likeds(RefDataDs2)
这时,在程序中就要这样定义和用指针偏移读取数据结构的数据:
D D_DataDsA ds likeds(RefDataDs1) based(Ptr_A)
D D_DataDsB ds likeds(RefDataDs2) based(Ptr_B)
D Ptr_A s *
D Ptr_B s *
/free
….
Callp CMSFTZ03FR(ptr_1:ptr_2:ptr_3) ;
Ptr_A = Ptr_3 ; // 返回数据结构的数据D_DataDs1已经放入
//D_DataDsA中了。
Ptr_3 = Ptr_3 + %len(D_DataDsA); //把指针Ptr_3偏移到指向
//第二个数据结构的起始地址。
Ptr_B = Ptr_3 ; //返回数据结构的数据D_DataDs2已经放入
//D_DataDsB中了
三、深度探讨指针用法
1. 指针层次的概念和用法
在RPGLE中指针是可以嵌套的,是有层次概念的,比如
d D_Ds1 ds based(P_Ptr1)
d D_ParmDs1 likeds(RefDs1)
d P_PtrA *
*
d D_Ds2 ds likeds(RefDs2)
*
d Ptr1 s *
d PtrA s *
/free
……
P_PtrA = %addr(D_Ds2)
Callp Proc(P_Ptr1) ;
这时,在代码中表示的指针层次是两层。在V5R4下,指针的层次最多可以达15层。
2. 多层指针结构下,注意事项
在多层指针结构下,要注意如下事项:
1) 指针引入的数据结构DS,最好立即用新的数据结构Ds进行转移保护,如
d D_DsIn ds based(Ptr3)
d P_PtrA *
d D_DsParmDs ds likdds(RefDs) based(P_PtrA)
d D_DsInBak ds likdds(RefDs)
*
d P_Ptr3 s *
d P_PtrA s *
/free
…..
Callp Proc(Ptr1:Ptr2:Ptr3) ;
//判断Ptr3数据结构中的返回码
……
//如果返回码没有错误,立即备份返回数据结构
D_DsInBak = D_DsParmDs ;
2) 在指针引入复杂结构中,实数据放在前面,变长数据结构放在复杂结构后部,如:
d D_InDs ds based(PtrA) qualified
d c_String 10
d s_Digit 5 0
d D_ArrDsSet likeds(D_ArrDs)
d P_PtrA *
d P_PtrB *
*
d D_ArrDs ds qualified
d s_count 3s 0 dim(2)
d a_ymlst 10a dim(500)
d a_mmlst 10a dim(500)
*
d D_DsA ds based(P_PtrA)
d c_fld 30
d p_fld 12 5
*
d D_DsB ds based(P_PtrB)
d s_Atrr 3 0 dim(20)
d c_String 1024 varying
3. 指针的迁移
在程序之间用指针进行传参调用,因为指针仅仅把参数集的起始指针进行传递。很多情况下,因为程序运行时,OS400在ASP中给每一个程序都分配一个程序运行临时区,当程序获取下一级程序返回的临时区域的变量(参数)指针地址时,本身的程序运行临时区,OS400就会自动进行调整,这时,可能会在调整过程中清除掉进入程序的运行临时区的指针引入的内容。
针对上述情况,采取保护措施是必要的。保护方法有:
1) 指针中设置选项用const;
2) 对输入指针引入的内容进行同类数据结构转移保护。
4. 用指针传递多个数据结构的常用两种方法
1) 指针分层;
d D_Ds ds based(Ptr)
d PtrA *
d PtrB *
*
d D_Ds1 ds likeds(RefDs1)
d D_Ds2 ds likeds(RefDs2)
*
/free
……
PtrA = %addr(D_Ds2) ;
PtrB = %addr(D_Ds1) ;
2) 归集多个数据结构,采用一个指针进行传递。
d D_Ds ds based(Ptr)
d D_Ds1 likeds(RefDs1)
d D_Ds2 likeds(RefDs2)
或者,
d D_Ds1 likeds(RefDs1)
d D_Ds2 likeds(RefDs2)
*
d c_String s 10000 varying based(Ptr)
/free
…..
c_String = D_Ds1 + Ds2 ;
用指针分层方法的风险,因为这种方法是多个指针同层传参,即每一个指针引入一个数据结构,或复杂数据结构,比多个数据结构进行归集,然后用一个指针进行传参,前者的数据安全性比后者差。特别情况下,如果经过多级调用,且都是通过指针进行结果返回,可能出现数据丢失。
用数据归集方法的风险,存在前面数据结构中有变长字段。
四、违背常规指针的特殊用法
AS400 RPG高级语言已经经历几代的变更,实现技术也从开始的不规范,比如非同类属性数据可以相互传递,RPG语言逐渐趋于完善。但是在遗留下的RPG编程代码还有一些不规范的东西,比如在QAPI调用中,参数用指针引入的数据结构不规范。
这里总结非规范指针用法,用于参考。
1) 在QAPI输入结构中,用一个字节定义替代指针类型的定义
我们在实际编程中发现,完全按照IBM文档对调用QAPI进行编程,有的地方很难通过,通过网上其它代码的参考,在D表中用一个字节定义替代指针类型的定义,在C表中用一个数据结构直接DS替代,调用QAPI的编程才能通过。比如:
调用QMHRTVM qapi,IBM文档是这样表述的:
Required Parameter Group:
1 |
Message information |
Output |
Char(*) |
2 |
Length of message information |
Input |
Binary(4) |
3 |
Format name |
Input |
Char(8) |
4 |
Message identifier |
Input |
Char(7) |
5 |
Qualified message file name |
Input |
Char(20) |
6 |
Replacement data |
Input |
Char(*) |
7 |
Length of replacement data |
Input |
Binary(4) |
8 |
Replace substitution values |
Input |
Char(10) |
9 |
Return format control characters |
Input |
Char(10) |
10 |
Error code |
I/O |
Char(*) |
Optional Parameter Group:
11 |
Retrieve Option |
Input |
Char(10) |
12 |
CCSID to convert to |
Input |
Binary(4) |
13 |
CCSID of replacement data |
Input |
Binary(4)
|
如果在rpgle程序代码中用这个数据结构对PR进行定义,对这个数据结构的第一项就很难编译通过。
在实际编程中,如果调用QMHRTVM 的PR如果是这样定义,即非规范指针定义,可以达到调用QMHRTVM的目的,代码如下:
dGetMsg pr extpgm('QMHRTVM')
d Receiver 1
d SizRcv 10i 0 const
d Format 8 const
d MsgID 7 const
d Msgf 20 const
d RplData 1 const
d SizRplDta 10i 0 const
d RplSubVal 10 const
d RtnCtls 10 const
d ErrCod 10i 0 const
这里对PR定义的第一项,不是技术文档的指针类型,而是一个字符的类型替代,再在D表中进行定义,C表中的处理是这样的:
dGetSize ds
d GetBytRtn 10i 0
d GetBytAvl 10i 0
/free
……
MsgId = 'E000001' ;
// How much storage is needed for everything?
callp GetMsg( GetSize
:%size(GetSize)
:'RTVM0400' :MsgID
:'CMMSGF RUN08141 '
:' ' :0
:'*NO' :'*NO'
:0) ;
//Allocate it and then call the API again
FmtPtr = %alloc(GetBytAvl) ;
callp GetMsg(Fmt0400 :GetBytAvl
:'RTVM0400' :MsgID
:'CMMSGF RUN08141 '
:' ' :0
:'*NO' :'*NO'
:0) ;
这里,数据结构“GetSize”直接使用PR定义的一个字节描述的“Receiver”,起到替代文档中的“Message information Output Char(*)”项。
后续的调用再用数据结构“Fmt0400”,直接使用“Receiver”项,起到相同的作用。