レコード型の可変部分について

解決


タルト  2012-10-20 05:03:50  No: 43220

DELPHI4です。
可変部分からなるレコード型を
     data :    RECORD CASE  INTEGER  OF
                    1    :    ( dWord        : WORD     ) ;
                    2    :    ( dSmallInt    : SmallInt ) ;
               END ;
と宣言して
     data.dWord := $FFFF ;
     Label1.Caption := IntToStr( data.dSmallInt ) ;
     Label2.Caption := IntToStr( data.dWord ) ;

     data.dWord := $FFFF ;
     Label3.Caption := IntToStr( data.dWord ) ;
     Label4.Caption := IntToStr( data.dSmallInt ) ;
を実行すると
Label1 : -1
Label2 : -1
Label3 : 65535
Label4 : 65535
の表示が得られます。

期待しているのは
Label1 : -1
Label2 : 65535
Label3 : 65535
Label4 : -1
なのですが、理解の誤りをご教示ください。


monaa  2012-10-20 06:52:08  No: 43221

XE2だと期待通りです。


オチは・・・  2012-10-20 07:09:40  No: 43222

>期待しているのは
Label2とLabel3の位置が入れ替わってるのに気が付かなかった・・・ というオチを期待しちゃダメ?


タルト  2012-10-20 07:46:56  No: 43223

ご回答ありがとうございます。

monaaさん
残念ですがXE2はありません。
DELPHI5でも4と同じ結果でした。私が知らないだけで、
きちんと説明がつくことかと思って質問いたしました。

オチは・・・さん
どきっとして見直しましたが入れ替わってはいませんでした。


Nov  2012-10-20 12:42:55  No: 43224

説明はつきませんが、符号なし整数にはUIntToStrを使うようにヘルプ(XE2)に記載があります。
ところで、SをString型として、Str(data.dWord, S)とStr(data.dSmallInt, S)の結果はどうなりますか?
期待通りに変換されれば、共用体の問題ではなく、IntToStrの問題と考えられます(まぁ、注意書きがあるので使わない方が無難ですが)。


タルト  2012-10-20 15:56:09  No: 43225

Novさん、ありがとうございます。
     S    :    String    ;
を追加して
     data.dWord := $FFFF ;
     Str( data.dSmallInt, S ) ;
     Label1.Caption := S ;
     Str( data.dWord, S ) ;
     Label2.Caption := S ;

     data.dWord := $FFFF ;
     Str( data.dWord, S ) ;
     Label3.Caption := S ;
     Str( data.dSmallInt, S ) ;
     Label4.Caption := S ;
を実行してみましたが、結果は
Label1 : -1
Label2 : -1
Label3 : 65535
Label4 : 65535
で同じでした。


Quest  2012-10-20 19:28:49  No: 43226

タルトさん、こんにちは。

Delphi2007でも同じ現象になったので、XE2とコンパイル結果を比較してみました。

Delphi2007では
  data.dWord := $FFFF ;
で、定数$FFFFを一旦16ビットのSIレジスタにセットしたのを使いまわし
  Label1.Caption := IntToStr( data.dSmallInt ) ;
この行のIntToStrを呼ぶときに
   movsx edi,si  ←16ビットから32ビットへ符号付拡張
   mox eax,edi   ←拡張結果をIntToStrの引数(eaxレジスタ)にセット
としていますが
   Label2.Caption := IntToStr( data.dWord ) ;
この行でIntToStrを呼ぶときにも
先ほど32ビットへ拡張した結果を使いまわし
   mox eax,edi
だけを実行しています。
つまり、符号付の32ビット整数をIntToStrに渡していることになります。

一方、XE2では
   Label2.Caption := IntToStr( data.dWord ) ;
この行でIntToStrを呼ぶときに
  movzx eax,[ebp-$06]  ;[ebp-$06]はdataの変数領域(2007とちょっと違い、変数領域から直接変換)
と、movzx(16ビットから32ビットへ符号なし拡張)命令を使って
符号なし整数にしてから呼び出していました。
ですので、当然結果も-1でなく65535になるわけです。

で、なんでこの違いが出るのか比較してみたら
最適化を行うかどうかでこの違いが出ることが分かりました。
2007では最適化を行う設定になっていたので試しに
最適化のチェックをはずして再構築したところ
2007でも期待通りの結果になりました。(アセンブラでもmovzx命令が挿入されています)
それではと、XE2で最適化をONにして再構築すると
望ましくない結果となりました。
アセンブラコードも2007と同じように、最初のLabel1.Captionへの代入のときに
ediレジスタにmovsxで格納して、Label2.Captionへの代入のときにediレジスタを
使いまわすようになりました。

結論としては、Delphiの最適化の不具合のようです。
XE2ではデフォルトで最適化なしになっていたので期待通りの結果になっただけで
Delphi2007(おそらくそれ以前のDelphi、もちろん4でも)では最適化がONになっていたため
この不具合に遭遇したということではないでしょうか。
Delphi4でも最適化をしない設定にして再構築してみてください。
多分、期待通りの結果になるんではないかと思います。


KHE00221  2012-10-20 19:36:01  No: 43227

var
  W:WORD
begin
  W := -1;
  Label1.Caption := IntToStr(W);

でどうなる?


KHE00221  2012-10-20 19:37:50  No: 43228

更新されてたのか・・・


KHE00221  2012-10-20 20:19:14  No: 43229

data を外に出せば最適化してても期待通りになるよ XE2 だけど

var
   data: TData;  <--- 外に出す

procedure TForm1.Button1Click(Sender: TObject); 
begin
   data.dWord := $FFFF;
   Label1.Caption := IntToStr( data.dWord ) ;
   Label2.Caption := IntToStr( data.dSmallInt ) ;
   data.dWord := $FFFF;
   Label3.Caption := IntToStr( data.dSmallInt ) ;
   Label4.Caption := IntToStr( data.dWord ) ;
end;


Quest  2012-10-20 21:21:36  No: 43230

>data を外に出せば最適化してても期待通りになるよ XE2 だけど
XE3、Delphi2007、Delphi6でも同じでした。
ローカル変数で定義するとダメのようです。


タルト  2012-10-21 01:48:48  No: 43231

大変お世話になっております。

Questさん、詳しい解析をしていただきましてありがとうございます。
  レジスタのことはまだ十分に理解できていませんので、
勉強したいと思います。
  とりあえず{$O-}で最適化をオフにすることによって所期の結果を
得ることができました。

KHE00221さん、dataをグローバル変数にすることで、所期の結果がでる
ことを確認しました。
  W:WORD

  W := -1;
の代入は範囲外でコンパイルエラーとなりました。
  ちなみに
     data.dSmallInt := -1 ;
     Label1.Caption := IntToStr( data.dSmallInt ) ;
     Label2.Caption := IntToStr( data.dWord ) ;

     data.dSmallInt := -1 ;
     Label3.Caption := IntToStr( data.dWord ) ;
     Label4.Caption := IntToStr( data.dSmallInt ) ;
も試してみましたが、最適化ありでローカル変数のままでは
やはり
Label1 : -1
Label2 : -1
Label3 : 65535
Label4 : 65535
となりました。

  今回の質問がさほどtrivialなことではなかったようで、
提起した甲斐があったと思っております。

  ご回答いただいた皆様にあらためて御礼申し上げます。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加