2012-04-10 3 views
9

この場合の説明が必要です。結果変数は関数の最初の行から定義されていますか?

私のテストによると、結果変数は、 Boolean = False、Integer = 0、String = ''、Object = nilなどと定義されています。 しかし、これについての公式の参照は見たことがありません。 これはヒントを与えるので意味があります。

[DCC警告] Unit1.pas(35):「TForm1.Test」に割り当てられH2077値は

function TForm1.Test: Boolean; 
begin 
    Result := False; 
    // Some arbitrary code here 

    Result := True; 
end; 

しかし、私は、最初の行をコメントアウトし、例外が前にどこかに存在する場合に起こるを使用したことがありません最後の行ですか?結果=偽ですか?

Resultが定義されていない場合、これは、常には、後で例外が発生した場合にResultを定義することによってすべての機能を開始する必要があることを意味します。そして、これは私にとっては意味がありません。

+5

例外が発生したときに「結果」にどのような価値があるかは重要ですか? –

+0

+1からミカエル。私もこの問題を見ることができません。 –

+2

可能な重複:デルファイの結果のデフォルト値(http://stackoverflow.com/q/5336863/576719)。 –

答えて

5

いいえ、Resultには(保証された)デフォルト値はありません。値を与えない限り、定義されていません。これは、関数が結果に値または 関数名を割り当てずに終了した場合、その関数の戻り値は未定義である

を述べthe documentation、によって暗示されます。

私はちょうど

function test: integer; 
begin 
    ShowMessage(IntToStr(result)); 
end; 

を試してみましたが、テキスト35531136とのメッセージが表示されました。

+0

ありがとう、ドキュメントへのリンクは私にとっては十分です。 –

10

あなたの状態:

結果が定義されていない場合、これは私はいつも後から例外の場合の結果を定義することによって、すべての機能を開始しなければならないことを意味します。

関数が例外を発生させた場合、関数の戻り値が未定義であることが懸念されます。しかし、それは問題ではありません。機能fnの体はコールバックサイトで、xを割り当てるべきではありませんし、例外が発生した場合

x := fn(); 

:次のコードを考えてみましょう。論理上記ワンライナーは、2つのライナーとして考えることができる。

  1. コールfn()
  2. 割り当て戻り値x

に例外が1行目に上昇している場合、行2決してxは決して割り当てられません。

Resultに割り当てる前に例外が発生した場合、関数が例外を発生させた場合、関数の戻り値を決して使用しないでください。


実際に気になるべきことは、関連する問題です。 Resultに割り当てた場合、例外が発生するとどうなりますか? Resultに割り当てた値を関数外に伝播することは可能ですか?悲しいことに、答えは「はい」です。

Resultに割り当てた値は、多くの結果タイプ(たとえば、整数、ブール値など)に対して、その関数が例外を発生させた場合、関数の外部に伝播しません。ここまでは順調ですね。

しかし、いくつかの結果の型(文字列、動的配列、インタフェース参照、バリアントなど)には、問題を複雑にする実装の詳細があります。戻り値は、varパラメータとして関数に渡されます。そして、関数の外から戻り値を初期化できることが分かりました。このように:fnの本体が実行を開始すると

s := 'my string'; 
s := fn(); 

Resultは値'my string'を持っています。

procedure fn(var Result: string); 

そして、これはあなたがResult変数に割り当てると、あなたの関数が、その後例外が発生した場合でも、コール・サイトでの変更を見ることができることを意味しますfnは次のように宣言されているかのようです。それを回避するためのきれいな方法はありません。あなたができることは、関数のローカル変数に代入して、関数の最終行為としてのみResultを代入することです。

function fn: string; 
var 
    s: string; 
begin 
    s := ... 
    ... blah blah, maybe raise exception 
    Result := s; 
end; 

return文が強く、ここで感じられるCスタイルの欠如。


上記の問題の影響を受けやすい結果変数の種類を正確に記述することは驚きです。当初は、問題がマネージタイプに影響したと思っていました。しかし、Arnaud氏は、レコードやオブジェクトも影響を受けるというコメントを述べている。レコードまたはオブジェクトがスタックに割り当てられている場合は、そうです。グローバル変数またはヒープ(例えばクラスのメンバー)が割り当てられている場合、コンパイラはこれを別々に扱います。ヒープ割り当てレコードの場合、暗黙のスタック割り当て変数が関数結果を返すために使用されます。関数が戻るときだけ、これはヒープ割り当て変数にコピーされます。コールサイトで関数結果変数を割り当てる値は、関数自体のセマンティクスに影響します。私の意見で


これはout意味を持つとは対照的に、関数の戻り値はvar意味を持っているため、それは、言語設計で、恐ろしい間違いだった理由のすべての非常に明確な説明図です。

+0

"私の意見では、これは、関数の戻り値がセマンティクスを持つのではなく、varセマンティクスを持つことが、言語設計上の間違いである理由を明確に示しています。これは私の意見でもあります。これは、速度とコードサイズの理由から、これがDelphiの最初のバージョンで行われたと思われます。「out」パラメータは呼び出し元によって初期化されなければなりません。現時点では、コードサイズが問題になりました(16ビットの世界)。さて、互換性の理由から、たとえ.exeのサイズがそれ以上重要ではなくても(空のDelphiアプリケーションは巨大です)、私たちは同じ意味に固執しています... :( –

+0

@Arnaud私の推測では、例えば、Delphi 2の長い文字列 –

+0

あなたは正しい:Delphi 2で追加された長い文字列...しかし、shortstringの返された変数はDelphi 1で既に 'var'として渡されていました。これはTP + Delphiの思い出です1年前に 'string = shortstring'とか' good old 'と書いてあったのですが、関数の 'shortstring'の結果は、呼び出し元スタックに初期化せずに割り当てられていました。デルファイXE2。;) –

15

official Delphi documentationにより述べたように、結果はどちらかである:

  • CPUレジスタ(S)(AL/AX/EAX/RAX/EAX:EDX)レジスタに含まれている順序値および要素について、
  • FPUレジスタ(st(0)/ XMM1);
  • 最新のパラメータとして追加の変数が渡されました。

一般的なルールは、デフォルトで結果値が定義されていないことです。それを設定する必要があります。コンパイラーは、欠落している結果セットについて警告します。文字列、動的配列、メソッドポインタ、またはバリアント結果の

、 効果は、関数の結果が宣言されたパラメータ以下 追加varパラメータとして宣言された場合と同じです。他の ワードでは、呼び出し側は関数結果を返す変数 を指す別の32ビットポインタを渡します。

正確であると、varパラメータには、管理タイプのため、だけ呼び出す前に、スタックに割り当てられrecordまたはobject結果、のためだけではありませんので、同じ動作の対象となっています。

たとえば、結果がstringの場合、追加のvarパラメータとして渡されます。したがって、デフォルトでは呼び出し前の値が含まれます。最初は''になりますが、関数を何回か呼び出すと、前の値が入ります。

function GetString: string; 
// is compiled as procedure GetString(var result: string); 
begin 
    if result='' then 
    result := 'test' else 
    writeln('result=',result); 
end; 

function GetRaise: string; 
// is compiled as procedure GetRaise(var result: string); 
begin 
    result := 'toto'; 
    raise Exception.Create('Problem'); 
end; 

var s: string; 
begin 
    // here s='' 
    s := GetString; // called as GetString(s); 
    // here s='test' 
    s := GetString; // called as GetString(s); 
    // will write 'result=test' on the console 
    try 
    s := GetRaise; // called as GetRaise(s); 
    finally 
    // here s='toto' 
    end; 
end; 

だから私のアドバイスは、次のとおりです。

  • が未設定の結果についての警告すべてのコンパイラを修正してください。
  • 結果文字列が ''に初期化されているとは仮定しないでください(最初は可能ですが、2番目の呼び出しでは使用できません)。varパラメータとして渡され、outパラメータとして渡されません。
  • どれexceptionはつまり、通常のとしてを処理され、実行中の流れは次のfinallyまたはexceptブロックにジャンプします - が、あなたはvarパラメータとして送信された結果を持っている場合、何かがすでにresultに割り当てられています値が設定されます。
  • ほとんどの場合、設定されていない結果の順序値(ブール値など)は0です(戻り値の直前のasmコードではEAX = 0なので)、次回になることになっていますそのような設定されていない結果変数のために顧客側で:それはほとんどの時間で動作し、時にはコードが失敗する...);
  • 新しいバージョンのDelphiでは、exit()構文を使用して値を返すことができます。
関連する問題