[SAP ABAP開發技術總結]Form(subroutine)、Function參數傳值傳址


這節也是ABAP學習的關鍵所在,Form、Function、Method的參數定義都差不多,弄懂一個,其他都好辦。參數傳遞涉及傳值、傳址問題,這是其也語言也有的問題,要學好他,你得要仔細想想

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

FORM subr [ 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 ) |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 (p2)|p2 } […] ]  

[ RAISING {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 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

image024[4]

1.10.2.1.        Function Group 結構

當使用 Function Builder 創建函數組時,系統會自動創建 main program 與相應的 include 程序:

image025[4]

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 所共享使用

image026[4]

image027[4]

image028[4]

image029[4]

1.10.2.2.        Function 參數傳值、傳址

image030[4]

image031[4]

image032[4]

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM