一般に、式の評価順序は定義されていません。(C と C++ は同じ方法です。Java は常に左から右に評価されます。) コンパイラはそれを制御しません。2 つの式を特定の順序で評価する必要がある場合は、コードを別の方法で記述します。コードの行数はあまり気にしません。回線は安いです。必要な数だけ使用します。このパターンを頻繁に使用していることに気付いた場合は、すべてをラップする関数を作成します。
function GetFirstIfAvailable(DB: TDatabaseObject; const FieldName: string): Integer;
begin
if DB.First = 0 then
Result := DB.ReturnFieldI(FieldName)
else
Result := 0;
end;
評価の順序が異なっていたとしても、元のコードはおそらくあなたが望んでいたものではなかったでしょう。ゼロに等しくDB.First
なかったとしても、への呼び出しReturnFieldI
は引き続き評価されます。すべての実パラメータは、それらを使用する関数を呼び出す前に完全に評価されます。
とにかく Math.pas を変更しても役に立ちません。実際のパラメーターがどの順序で評価されるかは制御しません。実際のパラメーターが評価されるまでに、それらはブール値と整数にまで評価されています。それらはもはや実行可能な式ではありません。
呼び出し規約は評価の順序に影響を与える可能性がありますが、それでも保証はありません。パラメータがスタックにプッシュされる順序は、それらの値が決定された順序と一致する必要はありません。実際、stdcall または cdecl で目的の評価順序 (左から右) が得られる場合は、渡された順序とは逆の順序で評価されています。
Pascalの呼び出し規則では、引数はスタック上で左から右に渡されます。つまり、一番左の引数がスタックの一番下にあり、一番右の引数が一番上、つまり戻りアドレスのすぐ下にあるということです。関数がその呼び出し規則を使用した場合IfThen
、コンパイラがそのスタック レイアウトを実現できるいくつかの方法があります。
ご想像のとおり、各引数が評価され、すぐにプッシュされます。
push (DB.First = 0)
push DB.ReturnFieldI('awesomedata1')
call IfThen
引数を右から左に評価し、プッシュされるまで一時的に結果を保存します。
tmp1 := DB.ReturnFieldI('awesomedata1')
tmp2 := (DB.First = 0)
push tmp2
push tmp1
call IfThen
最初にスタック領域を割り当て、都合のよい順序で評価します。
sub esp, 8
mov [esp], DB.ReturnFieldI('awesomedata1')
mov [esp + 4], (DB.First = 0)
call IfThen
は 3 つのケースすべてで同じ順序で引数値をIfThen
受け取りますが、関数は必ずしもその順序で呼び出されるとは限りません。
デフォルトのレジスタ呼び出し規約も引数を左から右に渡しますが、適合する最初の 3 つの引数はレジスタに渡されます。ただし、引数を渡すために使用されるレジスタは、中間式を評価するために最も一般的に使用されるレジスタでもあります。の結果をDB.First = 0
EAX レジスタに渡す必要がありましたが、コンパイラは と を呼び出すためにそのレジスタも必要としていReturnFieldI
ましFirst
た。次のように、最初に 2 番目の関数を評価する方がおそらく少し便利でした。
call DB.ReturnFieldI('awesomedata1')
mov [ebp - 4], eax // store result in temporary
call DB.First
test eax, eax
setz eax
mov edx, [ebp - 4]
call IfThen
指摘すべきもう 1 つのことは、最初の引数が複合式であることです。関数呼び出しと比較があります。これら 2 つの部分が連続して実行されることを保証するものは何もありません。First
コンパイラは、最初にandを呼び出して関数呼び出しを邪魔にならないようにし、その後、戻り値をゼロReturnFieldI
と比較する場合があります。First