SetLengthの位置

解決


GEN  2010-02-18 20:22:16  No: 37683

先ほどに続きたびたびすいません。
やはり解決できてませんでした。

SetLengthをFor文の前でSetすると問題なくSetできます。
しかしFor文のあとにSetLengthを行うと0しかSetできません。
なぜこうなってしまうのかわかりません。

ただこのFor文によって得た値をSetしたいのでどうしてもFor文の後におきたいです。

なにが原因でこうなってしまうのでしょうか?


ttt  2010-02-18 20:29:40  No: 37684

普通そんなことは起こらないので、実際のコードを見せてもらわないと原因なんてわかりません。
どこか配列の範囲外の領域に書き込んでメモリ破壊しているとかじゃないの?

>0しかSetできません
というのはどうやって確認したんでしょうか? 0以外だとエラーでも出るのですか?


GEN  2010-02-18 20:43:18  No: 37685

CountNo :=100;
SetLength(T1,CountNo);
SetLength(T2,CountNo);
SetLength(T3,CountNo);
 ←ここに入れた場合は問題ありません。
for i:= 0 to CountNo do
begin
 T1[i] := Test[i].T1;
 T2[i] := Test[i].T2;
 T3[i] := Test[i].T3;
end;

 AllCount := T1Max * T2Max;
 SetLength(T4,AllCount ); ←ここにいれるとだめです。
 このAllCountを0にした場合はエラーになりませんでした。
 AllCountを使わずに1や2などの数値にしてもだめでした。

エラーになってしまうというのは、コンパイルは通るのですが、実行すると
しばらくだんまり状態になってAccess Violation  Errになります。

よろしくお願いします。


ttt  2010-02-18 21:28:00  No: 37686

えーと、動的配列は0から始まるので、
例えば SetLength(T1, 3) としたら使える要素は3つ。
すなわち T1[0], T1[1], T1[2] です。T1[3] は使えません。
実際に使える要素は 0 から CountNo-1 までなんです。

一方、Delphiのfor文は
for i:= 0 to 3 do
と書いたら、0, 1, 2, 3 の4回ループします。
つまり、範囲外の T1[3] に書き込んでしまうということになります。


通りすがり  2010-02-18 21:28:49  No: 37687

for i:= 0 to CountNo do

for i:= 0 to CountNo-1 do
では?


GEN  2010-02-18 21:59:03  No: 37688

わかりにくい表現ですいません。
書き直します。

Var
 T1 : Array of Integer;
 T2 : Array of Integer;
 T3 : Array of Integer;
 T4 : Array of Integer;

begin
 CountNo :=100;
 SetLength(T1,CountNo);
 SetLength(T2,CountNo);
 SetLength(T3,CountNo);
 SetLength(T4,○○);←ここに入れた場合は問題ありません。
※○○はなんでもよい

 for i:= 0 to CountNo-1 do
 begin
  T1[i] := Hoge[i];
  T2[i] := Hoge2[i];
  T3[i] := Hoge3[i];
 end;

 AllCount := T1Max * T2Max;  //T1のMax値とT2のMax値をかけてT4の配列数
 SetLength(T4,AllCount ); ←ここにいれるとだめです。

T4のSetLengthを上記のように場所を変えるとだめになります。
問題はそこだけです。
さきほどFor文の中はわかりづらい表現でした。
申し訳ありません。


通りすがり  2010-02-18 22:39:15  No: 37689

procedure TForm1.Button1Click(Sender: TObject);
var
  Hoge1, Hoge2, Hoge3: array of Integer;
  T1, T2, T3, T4: array of Integer;
  CountNo, AllCount, I, T1Max, T2Max: Integer;
  t4Log: TStringList;
begin
  // initialize
  SetLength(Hoge1,100);
  SetLength(Hoge2,100);
  SetLength(Hoge3,100);
  for I:=0 to 100-1 do
  begin
    Hoge1[I] := I+1;
    Hoge2[I] := I+2;
    Hoge3[I] := I+3;
  end;
  //
  // main process
  T1Max := 0;
  T2Max := 0;
  CountNo := 100;
  SetLength(T1,CountNo);
  SetLength(T2,CountNo);
  SetLength(T3,CountNo);
  //SetLength(T4,CountNo);
  for I:=0 to CountNo-1 do
  begin
    T1[I] := Hoge1[I]; if T1[I]>T1Max then T1Max := T1[I];
    T2[I] := Hoge2[I]; if T2[I]>T2Max then T2Max := T2[I];
    T3[I] := Hoge3[I];
  end;
  //
  // final process
  AllCount := T1Max*T2Max; ShowMessage( 'AllCount='+IntToStr(AllCount) );
  SetLength(T4,AllCount);
  for I:=0 to AllCount-1 do
    T4[I] := I;
  //
  // debug out
  t4Log := TStringList.Create;
  try
    for I:=Low(T4) to High(T4) do
      t4Log.Add(IntToStr(I)+':'+IntToStr(T4[I]));
    t4Log.SaveToFile('t4Log.log');
  finally
    t4Log.Free;
  end;
end;
こんなテストをしてみましたが、エラーが出ませんね。


ふむ  2010-02-19 07:11:33  No: 37690

For文の後でSetLength(T4, 100)のように、変数AllCountでなく
具体的数値を入れて実行してもT4[0]以外の入力は不可ですか?

AllCountの値をブレークポイント設定あるいはShowMessageで確認してみたらいかがですか?


ttt  2010-02-19 07:17:02  No: 37691

ついでにコンパイルオプションで範囲チェックを有効にして試してみて。


GEN  2010-02-19 18:11:38  No: 37692

みなさありがとうございます。
通りすがりさん
テストありがとうございます。
なんで私のはでてしまうのかわかりません。
ただ配列にDouble型でかなりの文字数を入れているのでメモリが足りないのかな?と思っていますが、対処方もわかりませんし、確信はありません。

ふむさん
具体的な数値を入れてもだめです。
具体的な数値は0であれば問題ありませんが。
AllCountの値は15000くらいです。

tttさん
チェックいれてやってみました。これをするとどのような変化があるのでしょうか?

私なりに動的配列を静的にしました。
これをすると問題なくいきますが、なぜかその後のFor文(その配列を使用したほかの処理)でまた同じようにしばらくだんまってソフトがおちてしまいます。
Access violation errがでます。
この対処方を知りたいですが、このままでは進まないのでほかの方法も現在検討しています。


GEN  2010-02-19 18:24:26  No: 37693

tttさん
今範囲チェックを有効にしたら、1たりないものに入れてたりと範囲チェックのエラーが出て、それをつぶしていったら出来ました!!
ただチェックを有効にしないといままでそこでは止まらなかったですね。
普通は止まると思うのですが。


ttt  2010-02-19 18:43:28  No: 37694

> ただチェックを有効にしないといままでそこでは止まらなかったですね。
> 普通は止まると思うのですが

チェックを有効にすると、配列の範囲をはみ出していないかDelphiがいちいちチェックしてくれます。(その分オーバーヘッドがかかります)
無効にした場合、範囲をはみ出さないよう自己責任で管理しなくてはいけません。

配列の範囲をはみ出して書き込もうとした場合
*運が良ければ* そこが保護されたメモリ領域で、すぐにエラーが出ます。
*運が悪かったら* たまたまそこにあったメモリの内容(他の変数など)を書き換えてしまいますが、
OSから見れば「書き込み可能なメモリに書き込んだ」だけなので何事もなく先に進みます。
後に進んで、上書きされた変数の値を使ったりするうちに
だんだん矛盾が積み重なって、やがて異状が発覚します。

エラーの原因と実際の発生箇所が離れているので、こういうバグは潰すのがそうとう厄介です。
範囲のはみ出しには注意しましょう。


GEN  2010-02-19 22:33:20  No: 37695

tttさん

tttさんが言うようにあとで変なところでエラーになったのはそのせいだったのですね。
今回のエラーはすべて範囲のはみ出しのせいだったということがわかります。
これからはちゃんとチェックをいれていてエラーをその都度出すようにしておきます。

解決とともにとても勉強になりました。
ありがとうございます


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

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






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