2016-03-10 4 views
9

Delphiでプロシージャや文字列を操作する際に問題が発生しました。実際には、出力文字列 "1S2S3S4S5S6S"が表示されると予想されますが、実際の出力は "1234S5S6"です。デバッグプロセス中、S1、S2、S3、S6の文字列変数は初期化されていません(S1、S2、S3、S6は文字列、S4とS5は値 'S'を持ちます)。誰かが私にこれを説明することができますか?ここでは、コードです:文字列パラメータを使用したDelphiプロシージャ

program StringTest; 

{$APPTYPE CONSOLE} 

procedure MyProcedure(S1: String; const S2: String; var S3: String; 
         S4: String; const S5: String; var S6: String; 
         out S7: String); 
begin 
    S7 := '1' + S1 + '2' + S2 + '3' + S3 + '4' + S4 + '5' + S5 + '6' + S6; 
end; 

procedure Work; 
var 
    S: String; 
begin 
    S := 'S'; 
    MyProcedure(S, S, S, S, S, S, S); 
    writeln(S); 
end; 

begin 
    Work; 
    readln; 
end. 
+4

注意: 'const'でパラメータを宣言すると、コンパイラはその関数の実行中にパラメータが変更されることを期待しないように指示します。その約束を守るのはあなたの責任です。コンパイラはあなたのためにそれをチェックすることはできません。この場合、 'S7'を介して' S'を変更していると同時に、 'S2'と' S5'は変更しないと主張しています。 –

答えて

17

あなたS7パラメータはoutパラメータとして宣言されているので、関数が呼び出されたときに、コンパイラは、空白の文字列に渡された変数を設定します。同じS変数を出力パラメーターを含むすべてのパラメーターに渡すので、パラメーター値が関数内で使用される前に、Sの値がメモリーから消去されます。手順はS1 .. S3は、CPUレジスタ(それぞれ、EAX、EDX、およびECX)及びS4に渡されregister呼び出し規約を使用して、さらに詳しく説明する

.. S6代わりにスタックに渡されます。変数stringは、現在値がS4S5S3S6は変数へのポインタにすぎません)のスタックにプッシュされ、値がS1S2に割り当てられる前に消去されます。だから、S1S2最後までゼロ、S4S5はワイプ前の元'S'データへのポインタを含み、S3S6を拭いたstring変数を指しています。

デバッガは、このすべてを実際に表示できます。あなたはMyProcedure()が呼び出された行にブレークポイントを置き、その後、CPUビューを開く場合は、以下のアセンブリ命令が表示されます。

StringTest.dpr.17: MyProcedure(S, S, S, S, S, S, S); 
00405A6C 8B45FC   mov eax,[ebp-$04] // [ebp-$04] is the current value of S 
00405A6F 50    push eax   // <-- assign S4 
00405A70 8B45FC   mov eax,[ebp-$04] 
00405A73 50    push eax   // <-- assign S5 
00405A74 8D45FC   lea eax,[ebp-$04] 
00405A77 50    push eax   // <-- assign S6 
00405A78 8D45FC   lea eax,[ebp-$04] 
00405A7B E8B0EDFFFF  call @UStrClr  // <-- 'out' wipes out S! 
00405A80 50    push eax   // <-- assign S7 
00405A81 8D4DFC   lea ecx,[ebp-$04] // <-- assign S3 
00405A84 8B55FC   mov edx,[ebp-$04] // <-- assign S2 
00405A87 8B45FC   mov eax,[ebp-$04] // <-- assign S1 
00405A8A E8B9FEFFFF  call MyProcedure 

これを修正するには、出力を受け取るように別の変数を使用する必要があります:

function MyFunction(S1: String; const S2: String; var S3: String; 
         S4: String; const S5: String; var S6: String): String; 
begin 
    Result := '1' + S1 + '2' + S2 + '3' + S3 + '4' + S4 + '5' + S5 + '6' + S6; 
end; 

procedure Work; 
var 
    S: String; 
begin 
    S := 'S'; 
    WriteLn(MyFunction(S, S, S, S, S, S)); 
end; 

procedure Work; 
var 
    S, Res: String; 
begin 
    S := 'S'; 
    Proc(S, S, S, S, S, S, Res); 
    WriteLn(Res); 
end; 

あるいは、その代わりResultoutパラメータの使用を介して新しいStringを返す関数に手順を変更します

+0

そのアドバイスをいただきありがとうございますが、私はまだS4とS5に値 'S'があり、他のものはそうではないことを理解していません。どうしましたか? – Alexander

+0

OK、私は今それを取得しています:)最後の質問、なぜこのような順序で起こりますか?私は、S4、S5の値が最初にスタックにプッシュされ、次にS1、S2、S3がレジスタにプッシュされることを意味します。 – Alexander

+5

レジスタパラメータが最初に@Alexanderレジスタに格納された場合、これらのレジスタはコンパイラがスタックパラメータの値を計算するために使用できなくなります。コンパイラは、任意の順序でパラメータ値を計算できることを知っているので、コンパイラにとって便利な順序を選択します。 –

関連する問題