免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 7545 | 回复: 10

【原创】指针纵横 [复制链接]

论坛徽章:
0
发表于 2011-02-13 11:42 |显示全部楼层
本帖最后由 passthru 于 2011-02-28 09:48 编辑

指针纵横

一、概念
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(Ptr1tr2tr3) ;
      //判断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(
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”项,起到相同的作用。

论坛徽章:
0
发表于 2011-02-13 11:45 |显示全部楼层
这里发布的《指针纵横》由于符号显示问题,无法修正。原文已经放入我的blog中passthru.cublog.cn,
http://blogold.chinaunix.net/u1/46034/showart_2505024.html

论坛徽章:
0
发表于 2011-02-14 13:43 |显示全部楼层
本帖最后由 passthru 于 2011-02-14 14:04 编辑

CU blog怎么会自动删除文章呢?我的《指针纵横》已经被自动删除两次了。CU blog的bug!

我的blog中《指针纵横》文新的链接地址:
http://blogold.chinaunix.net/u1/46034/showart_2505794.html

论坛徽章:
0
发表于 2011-02-14 15:09 |显示全部楼层
学习 学习。

论坛徽章:
0
发表于 2011-02-15 12:36 |显示全部楼层
支持原创!!!

论坛徽章:
1
操作系统版块每日发帖之星
日期:2015-08-03 06:20:00
发表于 2011-02-18 19:34 |显示全部楼层
很强大

论坛徽章:
0
发表于 2011-02-22 17:26 |显示全部楼层
太精屁啦

论坛徽章:
0
发表于 2011-02-22 22:17 |显示全部楼层
哇塞,楼主整理的非常好啊。

论坛徽章:
0
发表于 2011-02-27 23:36 |显示全部楼层
厉害啊。。
谢谢楼主!学习了

论坛徽章:
0
发表于 2011-04-14 16:49 |显示全部楼层
PASS老大 既然DBG的时候能够能够查看指针指向的值。。。那么如何在RPG 中获取指针指向变量的值呀。。有什么API或者什么命令可以实现吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP