yvgoodman 发表于 2015-9-19 07:54:37

[SAP ABAP开发技术总结]Form(subroutine)、Function参数传值传址


声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4263707.html
这节也是ABAP学习的关键所在,Form、Function、Method的参数定义都差不多,弄懂一个,其他都好办。参数传递涉及传值、传址问题,这是其也语言也有的问题,要学好他,你得要仔细想想
1.10.      Form、Function.13
1.10.1.    FORM..13
1.10.2.    FUNCTION..15
1.10.2.1.          Function Group结构...15
1.10.2.2.          Function参数传值、传址...18
1.10.    Form、Function
Form、Function中的TABLES参数,TYPE与LIKE后面只能接标准内表类型或标准内表对象,如果要使用排序内表或者哈希内表,则只能使用USING(Form)与CHANGING方式来代替。当把一个带表头的实参通过TABLES参数传递时,表头也会传递过去,如果实参不带表头或者只传递了表体(使用了[]时),系统会自动为内表参数变量创建一个局部空的表头
不管是以TABLES还是以USING(Form)非值、CHANGE非值方式传递时,都是以引用方式(即别名,不是指地址,注意与Java中的传引用区别:Java实为传值,但传递的值为地址的值,而ABAP中传递的是否为地址,则要看实参是否是通过Type ref to定义的)传递;但如果USING值传递,则对形参数的修改不会改变实参,因为此时不是引用传递;但如果CHANGE值传递,对形参数的修改还是会改变实参,只是修改的时机在Form执行或Function执行完后,才去修改
Form中通过引用传递时,USING与CHANGING完全一样;但CHANGING为值传递方式时,需要在Form执行完后,才去真正修改实参变量的内容,所以CHANGING传值与传引用其结果都是一样:结果都修改了实参内容,只是修改的时机不太一样而已
1.10.1.          FORM
FORMsubr[TABLES t1 [{TYPEitab_type}|{LIKEitab}|{STRUCTUREstruc}]
t2 […]]
[USING {VALUE(p1)|p1 } [ {TYPEgeneric_type}
| {LIKE<generic_fs>|generic_para }
|{TYPE{ complete_type}|{REF TOtype} }
| {LIKE{ dobj} | {REF TOdobj} }
|STRUCTUREstruc]
{VALUE(p2)|p2 } […]]
[CHANGING{VALUE(p1)|p1 } [ {TYPEgeneric_type}
| {LIKE<generic_fs>|generic_para }
| {TYPE{ complete_type} | {REF TOtype} }
| {LIKE{ dobj} | {REF TOdobj} }
|STRUCTUREstruc]
{VALUE(p2)|p2 } […]]
.
generic_type:为通用类型
complete_type:为完全限制类型
<generic_fs>:为字段符号变量类型,如下面的fs形式参数
generic_para:为另一个形式参数类型,如下面的b形式参数
DATA:d(10)VALUE'11'.
FIELD-SYMBOLS:<fs>LIKEd.
ASSIGNdTO<fs>.
PERFORMaaUSING<fs> d d.
FORMaaUSING fs like<fs> a liked b likea.
WRITE:fs,/ a,/ b.
ENDFORM.
如果没有给形式参数指定类,则为ANY类型
如果TABLES与USING、CHANGING一起使用时,则一定要按照TABLES、USING、CHANGING顺序声明
值传递中的VALUE关键字只是在FORM定义时出现,在调用时PERFORM语句中无需出现,也就是说,调用时值传递和引用传递不存在语法格式差别

DATA:i TYPE i VALUE 100.
WRITE: /'frm_ref===='.
PERFORMfrm_refUSING i.
WRITE: /i."200

WRITE: /'frm_val===='.
i=100.
PERFORMfrm_valUSING i.
WRITE: /i."100

WRITE: /'frm_ref2===='.
"不能将下面的变量定义到frm_ref2过程中,如果这样,下面的dref指针在调用frm_ref2后,指向的是Form中局部变量内存,为不安全发布,运行会抛异常,因为From结束后,它所拥有的所有变量内存空间会释放掉
DATA: i_frm_ref2TYPE i VALUE 400.
i=100.
DATA: drefTYPE REF TO i.
get REFERENCE OF i INTOdref.
PERFORMfrm_ref2USINGdref ."传递的内容为地址,属于别名引用传递
WRITE: /i."4000


field-SYMBOLS: <fs>TYPE i.
ASSIGNdref->*to<fs>."由于frm_ref2过程中已修改了dref的指向,现指向了i_frm_ref2变量的内存空间
WRITE: / <fs>."400

WRITE: /'frm_val2===='.
i=100.
DATA: dref2TYPE REF TO i.
get REFERENCE OF i INTOdref2.
PERFORMfrm_val2USINGdref2 .
WRITE: /i."4000
ASSIGNdref2->*to<fs>.
WRITE: / <fs>."4000

FORM frm_ref USINGp_iTYPE i."C++中的引用参数传递:p_i为实参i的别名
WRITE: /p_i."100
p_i =200."p_i为参数i的别名,所以可以直接修改实参
ENDFORM.

FORM frm_val USINGvalue(p_i)."传值:p_i为实参i的拷贝
WRITE: /p_i."100
p_i =300."由于是传值,所以不会修改主调程序中的实参的值
ENDFORM.
FORM frm_ref2USINGp_i TYPE REF TO i."p_i为实参dref的别名,类似C++中的引用参数传递(传递的内容为地址,并且属于别名引用传递)
field-SYMBOLS: <fs>TYPE i.
"现在<fs>就是实参所指向的内存内容的别名,代表实参所指向的实际内容
ASSIGNp_i->*to<fs>.
WRITE: /<fs>."100
<fs> =4000."直接修改实参所指向的实际内存

DATA: drefTYPE REF TO i.
get REFERENCE OFi_frm_ref2INTOdref.
"由于USING为C++的引用参数,所以这里修改的直接是实参所存储的地址内容,这里的p_i为传进来的dref的别名,是同一个变量,所以实参的指向也发生了改变(这与Java中传递引用是不一样的,Java中传递引用时为地址的拷贝,即Java中永远也只有传值,但C/C++/ABAP中可以传递真正引用——别名)
p_i = dref."此处会修改实参的指向
ENDFORM.

FORM frm_val2USING VALUE(p_i) TYPE REF TO i."p_i为实参dref2的拷贝,类似Java中的引用传递(虽然传递的内容为地址,但传递的方式属于地址拷贝——值传递)
field-SYMBOLS : <fs>TYPE i.
"现在<fs>就是实参所指向的内存内容的别名,代表实参所指向的实际内容
ASSIGNp_i->*to<fs>.
WRITE: /<fs>."100
<fs> =4000."但这里还是可以直接修改实参所指向的实际内容

DATA: drefTYPE REF TO i.
get REFERENCE OFi_frm_ref2INTOdref.
"这里与过程frm_ref2不一样,该过程frm_val2参数的传递方式与java中的引用传递是原理是一样的:传递的是地址拷贝,所以下面不会修改主调程序中实参dref2的指向,它所改变的只是拷贝过来的Form中局部形式参数的指向
p_i = dref.
ENDFORM.
1.10.2.          FUNCTION

1.10.2.1.      Function Group结构
当使用Function Builder创建函数组时,系统会自动创建main program与相应的include程序:

l <fgrp>为Function Group的名称
l SAPL<fgrp>为主程序名,它将Function Group里的所有Include文件包括进来,除了INCLUDE语句之外,没有其他语句了
l L<fgrp>TOP,里面有FUNCTION-POOL语句,以及所有Function Module都可以使用的全局数据定义
l L<fgrp>UXX,也只有INCLUDE语句,它所包括的Include文件为相应具体Function Module所对应Include文件名:L<fgrp>U01、L<fgrp>U02、...这些Include文件实际上包含了所对应的Function Module代码(即双击它们进去就是对应的Function,而显示的不是真正Include文件所对应的代码)
l L<fgrp>U01和L<fgrp>U02中的01、02编号对应L<fgrp>UXX中的“XX”,代表其创建先后的序号,例如L<fgrp>U01和L<fgrp>U02是头两个被创建的函数,在函数组中创建出的函数代码就放在相应的L<fgrp>UXX(这里的XX代表某个数字,而不是字面上的XX)Include头文件中
l L<fgrg>FXX,用来存一些Form子过程,并且可以被所有的Function Modules所使用(不是针对某个Function Module的,但一般在设计时会针对每个Function Module设计这样单独的Include文件,这是一个好习惯),并且在使用时不需要在Function Module中使用INCLUDE语句包含它们(因为这些文件在主程序SAPL<fgrp>里就已经被Include进来了)。另外,L<fgrg>FXX中的F是指Form的意思,这是一种名称约束而已,在创建时我们可以随便指定,一般还有IXX(表示些类Include文件包括的是一些PAI事件中调用的Module,有时干脆直接使用L<fgrg>PAI或者L<fgrg>PAIXX),OXX(表示些类Include文件包括的是一些PBO事件中调用的Module,有时干脆直接使用L<fgrg>PBO或者L<fgrg>PBOXX)。注:如果Form只被某一函数单独使用,实质上还可直接将这些Form定义在Function Module里的ENDFUNCTION语句后面

当你调用一个function module时,系统加将整个function group(包括Function Module、Include文件等)加载到主调程序所在的internal session中,然后该Function Module得到执行,该Function Group一直保留在内存中,直到internal session结束。Function Group中的所定义的Include文件中的变量是全局,被所有Function Module共享,所以Function Group好比Java中的类,而Function Module则好比类中的方法,所以Function Group中的Include文件中定义的东西是全局型的,能被所有Function Module所共享使用




1.10.2.2.      Function参数传值、传址



functionfuc_ref .
*"-------------------------------------------------------------------
*"*"Local Interface:
*"IMPORTING
*"    REFERENCE(I_I1) TYPEIREFERENCE(别名)为参数的默认传递类型
*"    VALUE(I_I2) TYPEI       定义时勾选了Pass Value选项才会是VALUE类型
*"   REFERENCE(I_I3)TYPE REF TOI
*"   VALUE(I_I4)TYPE REFTOI
*"EXPORTING
*"   REFERENCE(E_I1) TYPEI
*"   VALUE(E_I2) TYPEI
*"   REFERENCE(E_I3) TYPE REF TOI
*"   VALUE(E_I4) TYPE REF TOI
*"TABLES
*"      T_1 TYPEZJZJ_ITAB
*"CHANGING
*"   REFERENCE(C_I1) TYPEI
*"   VALUE(C_I2) TYPEI
*"   REFERENCE(C_I3) TYPE REF TOI
*"   VALUE(C_I4) TYPE REF TOI
*"-------------------------------------------------------------------
write: / i_i1."1
"由于i_i1为输入类型参数,且又是引用类型,实参不能被修改。这里i_i1是以C++中的引用(别名)参数方式传递参数,所以如果修改了i_i1就会修改实际参数,所以函数中不能修改REFERENCE的IMPORTING类型的参数,如果去掉下面注释则编译出错
"i_i1 = 10.

write: / i_i2."2
"虽然i_i2是输入类型的参数,但不是引用类型,所以可以修改,编译能通过但不会修改外面实参的值,只是修改了该函数局部变量的值
i_i2 =20.

field-symbols: <fs>type i.
assigni_i3->*to<fs>.
"由于i_i3存储的是地址,所以先要解引用再能使用
write: / <fs>.
"同上面,REFERENCE的IMPORTING类型的参数不能被修改:这里即不能修改实参的指向"GET REFERENCE OF 30 INTO i_i3."虽然不可以修改实参的指向,但可以修改实参所指向的实际内容
<fs> =30.

assigni_i4->*to<fs>.
"i_i4存储也的是地址,所以先要解引用再能使用
write: / <fs>.
"虽然i_i4是输入类型的参数,但不是引用类型,所以可以修改,只会修改函数中的局部参数i_i4的指向,但并不会修改实参的指向
get reference of 40 intoi_i4.
"虽然不能修改实参的指向,但可以直接修改实参的所指向的实际内容
<fs> =400.

WRITE: / c_i1."111
"c_i1为实参的别名,修改形参就等于修改实参内容
c_i1 =1110.

WRITE: / c_i2."222
"c_i2为实参的副本,所以不会影响实参的内容,但是,由于是CHANGING类型的参数,且为值传递,在函数正常执行完后,还是会将该副本再次拷贝给实参,所以最终实参还是会被修改
c_i2 =2220.
ENDFUNCTION.

调用程序:
DATA: i_i1TYPE i VALUE 1,
      i_i2TYPE i VALUE 2,
      i_i3TYPE REF TO i,
      i_i4TYPE REF TO i,
      c_i1TYPE i VALUE 111,
      c_i2TYPE i VALUE 222,
      c_i3TYPE REF TO i,
      c_i4TYPE REF TO i,
      t_1TYPEzjzj_itabWITH HEADER LINE.

DATA: i_i3_TYPE i VALUE 3.
GET REFERENCE OFi_i3_INTOi_i3.
DATA: i_i4_TYPE i VALUE 4.
GET REFERENCE OFi_i4_INTOi_i4.
DATA: c_i3_TYPE i VALUE 333.
GET REFERENCE OFc_i3_INTOc_i3.
DATA: c_i4_TYPE i VALUE 444.
GET REFERENCE OFc_i4_INTOc_i4.

CALL FUNCTION 'FUC_REF'
EXPORTING
    i_i1 = i_i1
    i_i2 = i_i2
    i_i3 = i_i3
    i_i4 = i_i4
TABLES
    t_1 = t_1
CHANGING
    c_i1 = c_i1
    c_i2 = c_i2
    c_i3 = c_i3
    c_i4 = c_i4.
WRITE: / i_i2."2
WRITE: / i_i3_."30
WRITE: / i_i4_."400
WRITE: / c_i1."1110
WRITE: / c_i2."2220
页: [1]
查看完整版本: [SAP ABAP开发技术总结]Form(subroutine)、Function参数传值传址