这节也是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. 、 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
FORM [ TABLES t1 [{ TYPE itab_type}|{ LIKE itab}|{ STRUCTURE struc}]
t2 […]
]
[ USING { VALUE ( p1 ) |p1 } [ { TYPE generic_type }
| {
LIKE
<generic_fs>|generic_para }
|
{
TYPE
{[
LINE OF
] complete_type}|{
REF
TO
type} }
| {
LIKE
{[
LINE OF
] dobj} | {
REF
TO
dobj} }
|
STRUCTURE
struc]
{ VALUE ( p2 ) ]
[ CHANGING { VALUE ( p1 ) |p1 } [ { TYPE generic_type }
| { LIKE <generic_fs>|generic_para }
| {
TYPE
{[
LINE OF
] complete_type} | {
REF
TO
type} }
| {
LIKE
{[
LINE OF
] dobj} | {
REF
TO
dobj} }
|
STRUCTURE
struc]
{ VALUE ]
[ {exc1| RESUMABLE ( exc1 )} { exc2| RESUMABLE ( exc2 )} ... ] .
generic _type :为通用类型
complete_type :为完全限制类型
<generic_fs> :为字段符号变量类型,如下面的 fs 形式参数
generic_para :为另一个形式参数类型,如下面的 b 形式参数
DATA
:
d
(
10
)
VALUE
'11'
.
FIELD-SYMBOLS
:
<fs>
LIKE
d
.
ASSIGN
d
TO
<fs>
.
PERFORM
aa
USING
<fs> d d
.
FORM
aa
USING
fs
like
<fs>
a
like
d
b
like
a
.
WRITE
:
fs
,
/ a
,
/ b
.
ENDFORM
.
如果没有给形式参数指定类,则为 ANY 类型
如果 TABLES 与 USING 、 CHANGING 一起使用时,则 一定要 按照 TABLES 、 USING 、 CHANGING 顺序声明
值传递中的 VALUE 关键字只是在 FORM 定义时出现,在调用时 PERFORM 语句中无需出现 ,也就是说,调用时值传递和引用传递不存在语法格式差别
DATA
:
i
TYPE
i
VALUE
100
.
WRITE
: /
'frm_ref===='
.
PERFORM
frm_ref
USING
i
.
WRITE
: /
i
.
"200
WRITE
: /
'frm_val===='
.
i
=
100
.
PERFORM
frm_val
USING
i
.
WRITE
: /
i
.
"100
WRITE
: /
'frm_ref2===='
.
"
不能将下面的变量定义到
frm_ref2
过程中,如果这样,下面的
dref
指针在调用
frm_ref2
后,
指向的是
Form
中局部变量内存,为不安全发布
,运行会抛异常,因为
From
结束后,它所拥有的所有变量内存空间会释放掉
DATA
: i_frm_ref2
TYPE
i
VALUE
400
.
i
=
100
.
DATA
: dref
TYPE
REF
TO
i
.
get
REFERENCE
OF
i
INTO
dref.
PERFORM
frm_ref2
USING
dref .
"
传递的内容为地址,属于别名引用传递
WRITE
: /
i
.
"4000
field
-
SYMBOLS
: <fs>
TYPE
i
.
ASSIGN
dref->*
to
<fs>.
"
由于
frm_ref2
过程中已修改了
dref
的指向,现指向了
i_frm_ref2
变量的内存空间
WRITE
: / <fs>.
"400
WRITE
: /
'frm_val2===='
.
i
=
100
.
DATA
: dref2
TYPE
REF
TO
i
.
get
REFERENCE
OF
i
INTO
dref2.
PERFORM
frm_val2
USING
dref2 .
WRITE
: /
i
.
"4000
ASSIGN
dref2->*
to
<fs>.
WRITE
: / <fs>.
"4000
FORM
frm_ref
USING
p_i
TYPE
i
.
"
C++
中的引用参数传递
:
p_i
为实参
i
的别名
WRITE
: / p_i.
"100
p_i =
200
.
"p_i
为参数
i
的别名,所以可以直接修改实参
ENDFORM
.
FORM
frm_val
USING
value
(p_i).
"
传值
:
p_i
为实参
i
的拷贝
WRITE
: / p_i.
"100
p_i =
300
.
"
由于是传值,所以不会修改主调程序中的实参的值
ENDFORM
.
FORM
frm_ref2
USING
p_i
TYPE
REF
TO
i
.
"p_i
为实参
dref
的别名,
类似
C++
中的引用参数传递
(传递的内容为地址,并且属于别名引用传递)
field
-
SYMBOLS
: <fs>
TYPE
i
.
"
现在
<fs>
就是实参所指向的内存内容的别名,代表实参所指向的实际内容
ASSIGN
p_i->*
to
<fs>.
WRITE
: / <fs>.
"100
<fs> =
4000
.
"
直接修改实参所指向的实际内存
DATA
: dref
TYPE
REF
TO
i
.
get
REFERENCE
OF
i_frm_ref2
INTO
dref.
"
由于
USING
为
C++
的引用参数
,所以这里修改的直接是实参所存储的地址内容,这里的
p_i
为传进来的
dref
的别名,是同一个变量,所以实参的指向也发生了改变
(
这与
Java
中传递引用是不一样的,
Java
中传递引用时为地址的拷贝,即
Java
中永远也只有传值,但
C/C++/ABAP
中可以传递真正引用——别名)
p_i = dref.
"
此处会修改实参的指向
ENDFORM
.
FORM
frm_val2
USING
VALUE
(p_i)
TYPE
REF
TO
i
.
"p_i
为实参
dref2
的拷贝,
类似
Java
中的引用传递
(虽然传递的内容为地址,但传递的方式属于地址拷贝——值传递)
field
-SYMBOLS : <fs>
TYPE
i
.
"
现在
<fs>
就是实参所指向的内存内容的别名,代表实参所指向的实际内容
ASSIGN
p_i->*
to
<fs>.
WRITE
: / <fs>.
"100
<fs> =
4000
.
"
但这里还是可以直接修改实参所指向的实际内容
DATA
: dref
TYPE
REF
TO
i
.
get
REFERENCE
OF
i_frm_ref2
INTO
dref.
"
这里与过程
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 参数传值、传址
function
fuc_ref .
*"
-------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*"
REFERENCE
(I_I1) TYPE I
REFERENCE
(
别名
)
为参数的默认传递类型
*"
VALUE
(I_I2) TYPE I
定义时勾选了
Pass Value
选项才会是
VALUE
类型
*" REFERENCE(I_I3)
TYPE
REF TO
I
*" VALUE(I_I4)
TYPE REF
TO I
*" EXPORTING
*" REFERENCE(E_I1) TYPE I
*" VALUE(E_I2) TYPE I
*" REFERENCE(E_I3) TYPE REF TO I
*" VALUE(E_I4) TYPE REF TO I
*" TABLES
*" T_1 TYPE ZJZJ_ITAB
*" CHANGING
*" REFERENCE(C_I1) TYPE I
*" VALUE(C_I2) TYPE I
*" REFERENCE(C_I3) TYPE REF TO I
*" VALUE(C_I4) TYPE REF TO I
*"-------------------------------------------------------------------
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
.
assign
i_i3->*
to
<fs>.
"
由于
i_i3
存储的是地址,所以先要解引用再能使用
write
: / <fs>.
"
同上面,
REFERENCE
的
IMPORTING
类型的参数不能被修改:这里即不能修改实参的指向
"GET REFERENCE OF 30 INTO i_i3."
虽然不可以修改实参的指向,但可以修改实参所指向的实际内容
<fs> =
30
.
assign
i_i4->*
to
<fs>.
"i_i4
存储也的是地址,所以先要解引用再能使用
write
: / <fs>.
"
虽然
i_i4
是输入类型的参数,但不是引用类型,所以可以修改,只会修改函数中的局部参数
i_i4
的指向,但并不会修改实参的指向
get
reference
of
40
into
i_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_i1
TYPE
i
VALUE
1
,
i_i2
TYPE
i
VALUE
2
,
i_i3
TYPE
REF
TO
i
,
i_i4
TYPE
REF
TO
i
,
c_i1
TYPE
i
VALUE
111
,
c_i2
TYPE
i
VALUE
222
,
c_i3
TYPE
REF
TO
i
,
c_i4
TYPE
REF
TO
i
,
t_1
TYPE
zjzj_itab
WITH
HEADER
LINE
.
DATA
: i_i3_
TYPE
i
VALUE
3
.
GET
REFERENCE
OF
i_i3_
INTO
i_i3.
DATA
: i_i4_
TYPE
i
VALUE
4
.
GET
REFERENCE
OF
i_i4_
INTO
i_i4.
DATA
: c_i3_
TYPE
i
VALUE
333
.
GET
REFERENCE
OF
c_i3_
INTO
c_i3.
DATA
: c_i4_
TYPE
i
VALUE
444
.
GET
REFERENCE
OF
c_i4_
INTO
c_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
江正军