C言語関係掲示板

過去ログ

No754 大きな容量のデータを扱う場合

[戻る] [ホームページ]
No.9376

static float宣言について
投稿者---しろ(2003/09/21 16:14:37)


大きな容量のデータを扱う場合
float fdata[300000][300000]
としますと,実行時に
 Segmentation Fault: core dump
とエラーが出ます.

容量の割り当てが問題かと思いましたが,
static float fdata[300000][300000]
としますとプログラムが無事実行されます.

staticは静的にメモリ割り当てを行うと思いますが,このような
使い方(大容量データに対して)は正しいものでしょうか?




No.9377

Re:static float宣言について
投稿者---物見遊山(2003/09/21 18:08:48)


>staticは静的にメモリ割り当てを行うと思いますが,このような
>使い方(大容量データに対して)は正しいものでしょうか?

正しいかそーでないかは
そのfdataという配列がどのような使われ方をしているかに
依存することでは?

ま、
・コンパイルができて
・エラーもワーニングも出なくて
・思ったとおり(あるいは仕様どおり)に動作すれば
それでいーんじゃない?とも思うが。

とにもかくにもfdataの使われ方を見ねーと何とも言えません。

No.9378

Re:static float宣言について
投稿者---しろ(2003/09/21 18:24:32)


>正しいかそーでないかは
>そのfdataという配列がどのような使われ方をしているかに
>依存することでは?

仕様どおり動くかどうかをきちんと確認する以前に,動作原理を
理解した方がいいと思い投稿した次第です.
一見うまく動いている「動作すればよいプログラム」でも,おかしなつくりで
あれば実行環境の変化で結果がおかしくなってしまいます.


>とにもかくにもfdataの使われ方を見ねーと何とも言えません。

fdataは関数の中で使う一時的な配列データです.関数内の処理が終われば,
特に必要ありません(この時点でstaticはいらないと思うのですが,,).

小容量の処理の時はstaticをはずして処理可能ですが,大容量になると,
エラーが出るのです.


No.9379

Re:static float宣言について
投稿者---たか(2003/09/21 18:43:10)


static変数はmain()関数が実行を開始する前に0で初期化されます。
領域の場所は処理系によってまちまちですが、主にヒープ領域に取られ
ます。ヒープ領域は処理系にもよりますがかなり大きな領域を利用でき
ます。

それに反してauto変数は大抵スタック領域に取ります。その方が何かと
都合がいいのです。ただしこれも処理系に依存し、ヒープ領域にauto
変数を確保する処理系も数は少ないけどあります。

また、auto変数をスタック領域に確保する場合も、スタック領域が足り
なくなってくると自動的にスタック領域を広げてくれる処理系と、コン
パイル時にスタック領域の大きさが決定されてしまう処理系があります。
今回のケースでは後者に当たり、決められたスタック領域に二次元配列
が収まらないのでスタック領域を破壊してコアダンプに至ったと考えら
れます。それか、自動的にスタック領域を広げてくれるとしても、それ
にも限界があり、それでコアダンプに至ることもあります。

処理系によってはスタック領域を破壊する前に領域が確保されるかチェ
ックを行い、大きすぎたらスタックオーバーフローエラーを吐く物もあ
ります。その代わりわずかながら実行効率が低下します。

No.9381

Re:static float宣言について
投稿者---しろ(2003/09/21 19:02:35)


たかさん

詳しい説明をありがとうございました.色々参考書を探していましたが大容量時の
問題としてはなかなか見つかりませんでした.書かれました内容に関して,スタック領域
やヒープ領域などでもう少し勉強してみます.

No.9380

Re:static float宣言について
投稿者---物見遊山(2003/09/21 18:51:22)


>小容量の処理の時はstaticをはずして処理可能ですが,大容量になると,
>エラーが出るのです.

実行時にOSから割り当てられるメモリについては
たかさんがレスしているのでそちらを見てください。

>fdataは関数の中で使う一時的な配列データです.関数内の処理が終われば,
>特に必要ありません(この時点でstaticはいらないと思うのですが,,).

malloc(3)を使うのはどーでしょう?

No.9382

Re:static float宣言について
投稿者---しろ(2003/09/21 19:12:08)


>malloc(3)を使うのはどーでしょう?

アドバイスありがとうございます.

mallocの使用も考えておりましたが,mallocの場合は下記式の左辺のようなデータ
位置参照となるため,コードが長くなり見た目分かりにくいと思い使用はさけて
おりました.

*(fdata + ypos*XSIZE + xpos) = ftmp[ypos][xpos] * ftmp2[ypos][xpos];

確保する配列の大きさが変動する場合はmallocで対処しておりますが,見た目の
理解のしやすさから,固定配列の場合は二次元配列として宣言をしております.





No.9383

Re:static float宣言について
投稿者---物見遊山(2003/09/21 19:37:48)


float[300000][300000]を動的に確保することができませんでした・・・。
ゴメンなさい。

> *(fdata + ypos*XSIZE + xpos) = ftmp[ypos][xpos] * ftmp2[ypos][xpos];
>
>確保する配列の大きさが変動する場合はmallocで対処しておりますが,見た目の
>理解のしやすさから,固定配列の場合は二次元配列として宣言をしております.

さて。以下のソースがお役に立てればいいなぁ・・・
と思いつつ書いてみました。

#include <stdio.h>
#include <stdlib.h>

int
main(void)
{
    int ret = 1;
    float (*pfdata)[300] = NULL;

    pfdata = calloc(300U * 300U, sizeof(float));
    if (pfdata == NULL) goto end;

    // こんな書き方もできるのよー
    pfdata[0][0] = 1.5;
    pfdata[299][299] = 3.14;

    printf("%f\n", pfdata[0][0]);
    printf("%f\n", pfdata[299][299]);
    ret = 0;
end:
    free(pfdata);
    return ret;
}



No.9384

Re:static float宣言について
投稿者---しろ(2003/09/21 19:50:59)



こういう使い方があったのですね.非常に勉強になりました.
見やすい(理解しやすい)コードがこれで作れそうです.

お手数おかけいたしました.

No.9385

Re:static float宣言について
投稿者---たか(2003/09/21 20:18:09)


そうですね、私もある関数だけで必要な大きな配列を使う時はmalloc()、
C++ではnewを使っています。特にC++はclassのコンストラクタが走り、
deleteでデストラクタが走るのでヒープ領域の使用が便利になっていま
す。

ウィルスの中にはこのように簡単に破壊できるスタック領域の脆弱さを
利用して自分のコードを強制的に走らせる悪質なものがあるので、C言語
でCGIを書くときは特に注意が必要です。

また、コアダンプはスタック領域の破壊と同時には出ない事もあり、その
場合バグの追求が困難になります。なぜかというと初期化をしない配列だ
けの定義ですと配列の先頭アドレスを定義するだけとなり、実際に配列に
アクセスして初めてコアダンプを起こす場合もあるからです。

組み込み系のOSですとコアダンプの仕組みがないものが多く、そのまま
暴走してとても厄介です。

No.9386

Re:static float宣言について
投稿者---かずま(2003/09/21 22:50:07)


> 容量の割り当てが問題かと思いましたが,
> static float fdata[300000][300000]
> としますとプログラムが無事実行されます.
>
> staticは静的にメモリ割り当てを行うと思いますが,このような
> 使い方(大容量データに対して)は正しいものでしょうか?
300000 * 300000 * 4 = 360000000000 ≒ 335.3 * 1024 * 1024 * 1024

335ギガバイトのものメモリを割り当てることができるコンピュータだと
正しいでしょう。

あなたの環境を教えてください。

CPU は?
OS は?
コンパイラは?
ハードディスクの容量は?



No.9387

Re:static float宣言について
投稿者---たいちう(2003/09/22 09:45:26)


> 335ギガバイトのものメモリを割り当てることができるコンピュータだと
> 正しいでしょう。
>
> あなたの環境を教えてください。

私も気になります。ものすごい特殊な環境でないなら、
独立な900億個のfloat型を用意できないと思います。
プログラムが無事実行されているというのはたまたまでしょう。

ちなみにmallocの1つめの引数はsize_t型なので、一度にmallocで取ることは不可能。

No.9389

Re:static float宣言について
投稿者---物見遊山(2003/09/22 12:14:16)


>ちなみにmallocの1つめの引数はsize_t型なので、一度にmallocで取ることは不可能。

仰るとおりでございます。物見がタコでございました。
(1バイトの10万×10万でもダメなんだ・・・)

しろさんをマズい方向に誘導してしまったかもしれません(汗)。

御指摘ありがとうございました。

No.9392

Re:static float宣言について
投稿者---たか(2003/09/22 16:12:18)


>300000 * 300000 * 4 = 360000000000 ≒ 335.3 * 1024 * 1024 * 1024

>335ギガバイトのものメモリを割り当てることができるコンピュータだと
>正しいでしょう。

>あなたの環境を教えてください。

うおっと、そんなに大きかったのか。こりゃstaticで定義してエラー無し
というのは怪しい処理系ですぜ、旦那。

No.9398

Re:static float宣言について
投稿者---A(2003/09/22 18:37:16)


>うおっと、そんなに大きかったのか。こりゃstaticで定義してエラー無し
>というのは怪しい処理系ですぜ、旦那。

試してみたら、bcc32でコンパイルは通りました。
初期化無しの場合、Win32ですとBSSに置く処理系が多いようです。
実行時に死んでるのか、どうやら配列にアクセスすると死ぬようです(それも無言で)。

static float fdata[300000][300000] = {0.0f};
こうすると通りませんね。

スカラーマシンか何かを使っているのかもしれませんね。


No.9400

Re:static float宣言について
投稿者---しろ(2003/09/22 20:38:26)


皆様ご迷惑おかけいたしました.


プログラム内での使用サイズを勘違いしておりました.
実際のプログラム内での宣言は下記のとおりとなります.

float fdata[30000][4000]

なお環境は下記のとおりでございます.

CPU: Celeron 1.0GHz
OS: Redhat linux 8.0
メモリ: 512MB
HDD: 10GB
Cコンパイラ: gcc


たか様,物見遊山様にはとくにお手数をおかけしております.




No.9402

Re:static float宣言について
投稿者---たか(2003/09/22 21:42:25)


>試してみたら、bcc32でコンパイルは通りました。
>初期化無しの場合、Win32ですとBSSに置く処理系が多いようです。
>実行時に死んでるのか、どうやら配列にアクセスすると死ぬようです(それも無言で)。
>
>static float fdata[300000][300000] = {0.0f};
>こうすると通りませんね。

BSSという事は初期化されないデータですか?うーんいつ初期化して
るんだろう。

static float fdata[300000][300000];

int main(void)
{
  fdata[0][0] = 1.;
  
  return 0;
}


上のプログラムをBCC5.6.4で実行してみると無事走り、デバッグモード
でCPUで見てみますとmain()に入った時にはすでに初期化されているかの
ようなコードを吐いていました。BCCの場合BSSはヒープ領域の直前にあり
ますからもしかしたらBSSの領域を計算する過程でオーバーフローを起こし
て、普通はそこでエラーを吐かなければならないのですが、バグなのか
なぜか通してしまうみたいです。

ちなみにgcc3.3.1(MinGW)では

C:/MinGW/Learn/Learn3/very_large_array.c:1: error: size of array `fdata' is too large

C:/MinGW/Learn/Learn3/very_large_array.c:1: error: storage size of `fdata' isn't known

ときちんと止めてくれます。