Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1272995
  • 博文数量: 287
  • 博客积分: 11000
  • 博客等级: 上将
  • 技术积分: 3833
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-16 08:43
文章分类
文章存档

2013年(15)

2012年(17)

2011年(17)

2010年(135)

2009年(85)

2008年(18)

分类: 系统运维

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下,debugF11显示程序变量的长度,默认值是1024字节。如果需要查看1024之后的数据,用eval方式,比如字符变量C_Vareval 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时,PGMAPGMB传递的参数只能用PTR_2带出;Ptr_3只能用在返回结果参数范围,即PGMBPGMA的返回值。

 

    对三个Ptr指针引入的参数结构,都可以放在统一的copybook中,在程序代码中直接进行copy定义。这样做的好处,一个应用项目的数据结构是唯一的。

 

4.       指针在QAPI中的应用

指针在QUSPTRUSQAPI中的编程,是把调用的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 + %lenD_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.       指针的迁移

在程序之间用指针进行传参调用,因为指针仅仅把参数集的起始指针进行传递。很多情况下,因为程序运行时,OS400ASP中给每一个程序都分配一个程序运行临时区,当程序获取下一级程序返回的临时区域的变量(参数)指针地址时,本身的程序运行临时区,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 qapiIBM文档是这样表述的:

  

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”项,起到相同的作用。

阅读(2485) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~