メンバとローカル変数のパフォーマンスについて

このQ&Aのポイント
  • メンバとローカル変数のパフォーマンスについて実験を行いました。実験では、2つの異なる関数パターンを比較し、後者の方が速い結果が得られました。アセンブリ出力を見た結果、前者ではthisポインタ経由で操作が行われ、後者ではローカル変数がスタックのアドレスを使って操作されていることが分かりました。しかし、このオプティマイゼーションは全てのコンパイラで行われるわけではなく、スタックの消費量や関数の呼び出しに影響を与える可能性があるため、注意が必要です。
  • この実験では、メンバとローカル変数の違いがパフォーマンスにどのような影響を与えるかを調査しました。実験結果から、ローカル変数を使用する方が高速な結果が得られたことが分かりました。アセンブリ出力を見ると、前者ではthisポインタ経由での操作が行われ、後者ではローカル変数が使用されていることが分かりました。ただし、この最適化がすべてのコンパイラで行われるわけではなく、スタックの消費量や関数の呼び出しに影響を与える可能性があるため、注意が必要です。
  • メンバとローカル変数のパフォーマンスについて検証を行いました。実験では、2つの異なる関数パターンを比較し、後者の方が高速な結果が得られました。アセンブリ出力を見ると、前者ではthisポインタ経由での操作が行われているのに対し、後者ではローカル変数を使用して操作が行われていることが分かりました。ただし、このオプティマイゼーションはすべてのコンパイラで行われるわけではなく、スタックの消費量や関数の呼び出しに影響を与える可能性があるため、注意が必要です。
回答を見る
  • ベストアンサー

メンバとローカル変数のパフォーマンスについて

実験用に以下のstructを作りました。 struct Widget { double data[32]; int a, b, c; Widget(); __forceinline int abs_(int) const; Void func( double* ); }; /////////ソース///////// Widget::Widget() : a(1), b(2), c(52000) { for ( int i=32;i--; ) data[i] = 0.8649 * ( i & 1 ? -i : i ); } __forceinline int Widget::abs_( int i ) const { return i < 0 ? -i : i; } そしてfuncの内容について Void Widget::func( double* const d ){ for ( int i = 32; i--; ){ c -= int( data[i] ); b = -int( abs_(b)*0.999 + 1.5 ); d[i] = a * d[i] + d[i&3] / b + c * data[i]; } } のパターンと Void Widget::func( double* const d ){ const double* const data_ = data; const int a_ = a; int c_ = c; int b_ = b; for ( int i = 32; i--; ){ c_ -= int( data_[i] ); b_ = -int( abs_(b_)*0.999 + 1.5 ); d[i] = a_ * d[i] + d[i&3] / b_ + c_ * data_[i]; } c = c_; b = b_; } のパターンを使い (同じコンパイラ・コンパイルオプションならどちらのfuncでも数値は必ず同じになります。(のはず)) それぞれ100万回呼び出したところ 比較的安定して0.01~0.02秒程度の差が発生しました。 それは後者の方が速いという意味です。 アセンブリ出力を見てみると (アセンブリの行数的にはもちろん前者の方が短いです) 全部読み解いてはいませんが ざっと見た感じ 前者では ; _this$ = ecx というコメントがあり これを mov edi, ecx mov eax, edi などとした後に edi やeaxレジスタを使っているように見えました。 対して後者は ebpレジスタを使っているように見えました。 要するに 前者では毎回thisポインタ経由でやってて 後者ではローカル変数なのでスタックのアドレスを使って操作してる という感じだと思うのですが 確かにコードからこれは妥当だとも思うのですが 上記の場合、abs_のインライン(だいたいconstメンバ関数ですし)を除き 別途関数を呼び出したりしていません。 つまり、今回の実験においては、このfunc関数実行中 func関数外でメンバが書き変わるということはないという事になっています。 こういう場合 前者の書き方でも最適化によって後者の効果が得られる という事があると便利な場合もあるんではないかと思うのですが ・スタックの消費量が意図せず膨張すると困るかもしれない ・関数の呼び出しの確認などが広範に必要で、コンパイラの負荷がかなり増えるかもしれない ・そういえばマルチスレッドを考えると、同期がプログラマ委託である限り不可能かな(これは最難関?) などの「それは難しすぎる」という理由も考えられます。 現状では thisポインタ経由を自動的にスタック操作に書き換える といったことはどのコンパイラでも行わないのでしょうか? 仮に、そうなのであれば やはり「ここぞという多大な演算の最深部」の関数とかでは スタックの消費が異常にならないような配慮はしつつ こういう風にローカル変数に読み直した方がいいと考えて良いでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

ん~, そこまでの最適化はできないんじゃないですかねぇ.... 言われるように, マルチスレッドを考えると無理. そうでなくても c -= int( data[i] ); における -= の副作用は ; の直後で確実に適用していなきゃならない. この場合の「副作用」は「メンバ変数の値を変更する」ことだから, この文が終わった時点でメンバ変数の値が変わっていないとおかしい.

LongSecret
質問者

お礼

Tacosanさんもご回答ありがとうございます♪ >c -= int( data[i] ); における -= の副作用は ; の直後で確実に適用していなきゃならない なるほど、そういうことであれば やるコンパイラがあったらそれがおかしいと考えてよさそうですね。 ところが今回下の実験でまた謎なことにw んでも コンパイルオプションのせいだったのかもしれないと思い /O2 /GL /D "WIN32" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /FD /EHsc /MDd /Yu"stdafx.h" /Fp"Debug\AlgorithmExper.pch" /FA /Fa"Debug\\" /Fo"Debug\\" /Fd"Debug\vc90.pdb" /W3 /nologo /c /Zi /TP /errorReport:prompt プラス /MP の状態に変更しました。 さらに、前回のコンパイルオプション(どこをどういじったのか曖昧ですが、確か「リンク時の最適化あたりが変わってると思います」) において で、非staticの非インラインのメンバ関数の__stdcallで 移し替えなしバージョンが勝りましたが 今回の条件で再度試してみると 4つの呼び出し規約 における 移し替えバージョン 移し替えなしバージョン の8パターン全てにおいて 「呼び出し規約の変更」 では出力アセンブリは変化しませんでした。 また、今回の条件では 移し替えバージョン優勢に逆転したっぽいです。 double data[32]; の添え字やループ回数を 32→10000ぐらいにしてみたら その差はさらに顕著になりました。 ところが もしも「多大な演算の最深部」で「連続して頻繁に呼び出される」ような関数があったら 普通は「インライン展開」の可能性を考慮してみるべき ということになるはずでした なのでfuncにも__forceinlineを付けて実験してみると 移し替えなしバージョン > 移し替えバージョン にさらに逆転しました。 コンパイルオプションを変えると少し事情が変わってくることもありましたが 少なくともこの状況では __forceinlineでインライン展開された場合でも 呼び出し規約の明示的な指定で変化はありませんでした。 (むしろインライン展開では呼び出し規約は関係ないと思うので、変わるほうが変な気がしますが) と・こ・ろ・が その時間を見てみると インライン版 よりも 非インライン版 の方が、いずれも速いです。 移し替え→○ 移し替えなし→× インライン→i で、速い順に ○ > × > ×i > ○i こんな感じです。 × と ×iは僅差ですが ○iはひどいです。 ○ と ×もそこそこの差があります。 呼び出し側のコードはこんな感じです。 void ssss(Widget*, double*); int main(void){ double d[10000] = {777}; //チョット危なげですがあくまで実験なので Widget a; { Debug::Performance z; //詳細はhttp://oshiete.goo.ne.jp/qa/7262790.html ssss( &a, d ); } for ( int i = 32; i--; ) Debug::f( d[i] ); //確認及び万が一最適化で消えるのを防ぐ用 return 0; } void ssss(Widget* w, double* d ){ //ここでのfuncがインラインになるかどうかというだけの差のはず for ( int i = 10000; i--; ) w->func( d ); //10000要素に変えたのでこっちは少なく } どういうキャッシュの持ち方してたらこんな結果になるのか謎ですがw う~ん、もうひとつ何か欲しいところですね。 「この検証に対してはコンパイルオプションが不完全」とかだと一番楽なんですが

LongSecret
質問者

補足

さらなる経過報告です。 http://codezine.jp/article/detail/420 このあたりを参考にしながら EXEファイルの解析 を行ったり、全体の大よその配置をつかんだあと ちょこっとだけ書き変えて機械語の該当箇所のアドレスを割り出したり キャッシュの勉強をしたり、とにかく色々しました。 で、コンパイルオプションの、出力ファイルのところを アセンブリ コード、コンピュータ語コード、ソース コード (/FAcs) に変えてじっくり観察してみると 「ある仮説」が浮上してきましたw (といっても、あくまで仮説なので全く的外れかもしれませんが) 私のCPUは L1キャッシュが 2 x 64 KiB で 2-way set associative キャッシュラインサイズは確か64Byte な感じだと思うのですが double data[10000]; に関しては 8*10000で80,000バイト、これはCPUガンガンに使ってるときは、おそらくはL1だけで何とか収まってるか あるいは、L2が使われることになるとしても そのあたりの挙動はあんま変わってない可能性が高めと思うのです。 で ○ > × > ×i > ○i この図式なんですが よーく機械語(というか関数開始位置からの相対アドレス)とアセンブリとコメントで出てくる元のソースを見比べてみて キャッシュの気持ちを読み取ろうと思ってみると 2-way set associativeなんで自由のはずですが とりあえずループのジャンプ先(ラベル)の箇所を起点にL1命令キャッシュを持っておく というのはどうかなと。 そのとき「最も外側にあるjneと、そのジャンプ先のラベルの、アドレスを見比べてみると」 (↓10進表記で、コンパイル後の関数の先頭からの相対アドレスです) ○ 70 153 差:83 × 41 145 差:104 ×i 38 165 差:127 ○i 41 179 差:138 こんな風になってました。 で、ですよ 試しに インライン化して尚且つ無駄に二重ループにしてみました。 void ssss(Widget* w, double* d ){ for ( int a = 90; a--; ) for ( int i = 100; i--; ) w->func( d ); } 実行時間は計測上、これでようやく、×iと○iの中間ぐらいです funcの実行回数は実に1000回(1割)も減っているわけですが。 このときのアドレスの関係ですが 51 225 差:174 2重ループにしたので、また離れてます。 これだったら 64Byteのラインの 2-way set 1つでは確実に無理ですよね。 これらのことから、もしかすると ・キャッシュラインのセットへプリフェッチを行う際ループがある場合、外側のループのラベルを基点にしてやってる ・関数が別途呼び出される場合は別のキャッシュラインのセットへ別途読み込まれる 可能性はないでしょうか? もしこれが正しいのならば、やっぱりまさにアーキテクチャ依存ではあるけども n-way set associativeが一般的で かつ、もし このループに差し掛かった場合のキャッシュの持ち方も比較的一般的な方 であれば ・インライン化で必ずしも高速化するとは限らない。 ・キャッシュラインとジャンプ距離とかのことを考慮して、大きすぎるようなら読み替えなしの手もありかもしれない。 ・といっても、元々それほどは大差というわけでもないので、対象のCPUがかなり限定できない状況では、ある程度その場の気分でもいいかもしれない。 ・将来(未知のCPU)のことを仮定するなら余計「読み替え」は「最終的な、奥の手」的な感じでもいいかもしれない。 ・既に書き変えてしまっている場合は、とりあえず、現状はそのままでもいいかな。 ということになってきそうですね。 まぁ、全く間違ってるかもしれませんがw 個人的には調べまくった結果相当色々勉強になったので、現時点で解決で良いんですがw 「上記解釈は明らかに間違ってる」っていう場合は、分かる方いらっしゃいましたら教えてください。 その点で、すぐ締め切らない方が良いと思うので しばらく待たせてください。

その他の回答 (1)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.1

プロセッサのアーキテクチャやコーリングコンベンション、そして最適化性能によります。

LongSecret
質問者

お礼

どうもjactaさん、ご回答ありがとうございます♪ なるほど 非staticなメンバ関数は デフォではthiscallになってて、thiscallはコンパイラ依存 ってことでしたが メンバ関数って__stdcallとかって指定していいんでしたっけ? __cdecl、__stdcall、__fastcallを明示的に指定してアセンブリ出力してみたのですが いずれの場合も 上の、移し替えなしの方法では スタックのアドレス操作とスタックレジスタを使う方法に 最適化によって自動的に切り替わっていることはありませんでした。 http://www.thinkridge.com/modules/tinyd1/rewrite/tc_2.html にも書いてあるように この部分は最適化でそう書き変えるのは 私が質問文で書いたように、整合性が取れなくなる可能性があると思うので 実際にはないんじゃないかと思ったのですが (つまり、言語仕様に近い問題…?) プロセッサのアーキテクチャによってはあり得るってことなのでしょうか? ところが 確かにスタックレジスタは使ってないものの __stdcall で上の方法を使った時 出力されたアセンブリは最短の行数となり かつ測定結果、最速となってしまいました。 __stdcallを指定し 移し替えるという方法をとった場合も ほぼ同じ速度になりますが それでも若干移し替えなしの方が勝っている感じもします。 全体としては速い順に stdcall > fastcall > thiscall > cdecl こんな感じです。 アセンブリを完全に読み切れば全貌が理解できるのかもしれませんが 別のソースで同じようになるのかもわかんない現状では 個人的には「?」って感じです また、x64だとfastcallがどうのこうのっていう話を ほんの少し聞いたような気もしなくもないのですが WindowsXP以降用で 何かこう「現状これが良さ気」って言う決定打ってないものでしょうか。

LongSecret
質問者

補足

おっと メンバ関数って__stdcallとかって指定していいんでしたっけ? ↓ 非staticなメンバ関数って__stdcallとかって指定していいんでしたっけ? です。

関連するQ&A

  • グローバル変数について

    ◎1--------------------------------- #include<stdio.h> void func(void); int glb; int main(void) { int a=20; glb=30; printf("main a=%d glb=%d\n",a,glb); func(); return 0; } void func(void) { int b=88; printf("func b=%d glb=%d\n",b,glb); } ------------------------------------- ◎1の実行結果----------------------- main a=20 glb=30 func b=88 glb=30 ------------------------------------- ◎2--------------------------------- #include<stdio.h> void func(void); int glb; int main(void) { int a=20; func(); printf("main a=%d glb=%d\n",a,glb); return 0; } void func(void) { int b=88; int glb=30; printf("func b=%d glb=%d\n",b,glb); } ------------------------------------- ◎2の実行結果----------------------- func b=88 glb=30 main a=20 glb=0 ------------------------------------- 以上2つのプログラムで、◎1は参考書を参考に作成したものです。 ◎1のプログラムで、グローバル変数glbの値をmain( )関数内で設定していたので、次に◎2のようにfunc( )という関数プロトタイプ内で、グローバル変数glbの値を設定し、main( )関数内のprintf文でも表示させようと思ったら、「glb=0」となってしまいました。 なぜこのようになってしまうか、教えてもらえたら嬉しいです。

  • メンバ変数が変更されそうなconstメンバ関数

    c++のconstメンバ関数についての質問です。 以下のコードの様に、constメンバ関数で メンバ変数を変更しているように見える場合、 本来はどのように記述すべきか? class foo { public: foo(){} foo(int i){ d = i; } void hoge(foo *dest0, foo *dest1 ) const { dest0->d = d*2; dest1->d = d*4; } int d; }; int main() { foo f(1); foo p,q; f.hoge(&p,&q); std::cout << f.d << "\n"; std::cout << p.d << "\n"; std::cout << q.d << "\n"; f.hoge(&f,&q); std::cout << f.d << "\n"; std::cout << q.d << "\n"; return 0; } ちなみに出力期待値は 1 2 4 2 4 とします。 上のコードは 1 2 3 2 8 と出力されますが、このようなことが無いように実装するには どうすべきかという質問です。 dest0->d = d*2; dest1->d = d*4; の所を int i=d; dest0->d = i*2; dest1->d = i*4; とすべき? それともhogeの最初に if( (this == dest0) || (this == dest1) || (dest0== dest1) ){ throw "同じオブジェクトじゃだめ"; } とすべき? どんな書き方が安定でしょうか?

  • プログラミング教えてください!!!お願いします。

    プログラミング教えてください!!!お願いします。 次の文が実行されると何がどのようにプリントされるか。何もプリントされない時は「なし」と記せ。 また、途中に「ブランク」が入る場合は、”b”と記せ。 (1) int func1(), func2(); int data = 100; main() { int w = 1; static x =10; printf("** %d, %d, %d\n" ,w,x,data); func1(); printf("** %d, %d, %d\n" ,w,x,data); } int func1(){ int w = 2; static int x = 20: printf("*** %d, %d, %d\n", w, x, data); x += 10; func2(); printf("*** %d, %d, %d\n" , w,x,data); x *= 2; data = data - x; } int func2() { int w = 3; static int x = 30; printf("**** %d, %d, %d\n" ,w,x,data); data -= x; } (2) int func1(char *, char *, char *); int func2(char *, char *, char *); main() { char sta[20], stb[20], stc[20], std[20]; int i=0; func1("abc","xyz",sta); printf("%d -- %s\n" ,++i,sta); func1("123","456",stb); printf("%d -- %s\n" ,++i,stb); func1(sta,stb,stc); printf("%d -- %s\n" ,++i,stc); func2(sta,stb,std); printf("%d -- %s\n" ,++i,std); } int func1(char *a, char *b, char *c){ while(*a) *c++ = *a++; while(*b) *c++ = *b++; * c = 0x00; } int func2(char *a, char *b, char *c){ int i = 0; while(*b){ if(i%2 == 0) *c++ = *a++; else *c++ = *b++; i++; } *c = 0x00; }

  • 関数の定義

    度々すみません、教えてください。。。 func(int a,int b,int c,int d); func(int a,int b); func(int a); っていう関数を使いたい場合、関数の定義(宣言?)はどのように書けばよいのですか?エラーになってしまいます・・・。

  • C言語から質問です。

    C言語から質問です。 /* main関数の中で初期化した配列 data[10]={60,30,70,25,20,9,92,55,20,10}; を関数 keisan() に引数として渡して、関数keisan()内で 平均値、最大値,最小値 を求め、その結果をmain関数に戻し、main関数内で 平均値、最大値,最小値を表示させるプログラムを作成せよ。 int keisan(const int data[], int data_kosuu, double answer[]) { return 0; } とする。 (注) プロトタイプ宣言を用いよ。 ヒント:平均値,最大値,最小値の3つの値を main関数に戻すやり方として、配列answer[]を使うとよい。 data_kosuu は、配列の要素数を与えるものです。 ヒント: (int型の変数)/(int型の変数)=int型の値です。 int型同士の割り算の答えANSWERをdoubleにしたい場合は、  ANSWER=(double)(int型の変数)/(int型の変数);    と(double) キャストという操作をする必要がある */ #include <stdio.h> int keisan(const int data[], int data_kosuu, double answer[]); int main(void) { int i; int a[10]={60,30,70,25,20,9,92,55,20,10}; // この値を使ってください double ans[3]; keisan(a,10); /* keisan関数に配列と配列要素数を引数で与える */ for(i=0; i<10 ; i++) printf("a[%d]=%d\n",i,a[i]); printf("平均=%lf 最小値=%d 最大値=%d\n",ans[0],ans[1],ans[2]); return 0; } /* 合計・最大値・最小値を求める関数 */ int keisan(const int data[], int data_kosuu, double answer[]) { int i; int sum; int min,max; min=10; /* min の初期化 */ max=10; /* max の初期化 */ sum=0; /* 合計値の初期化 */ for (i=0; i<10 ; i++){ sum = sum+data[i]; if(data[i] > max) max=data[i]; if(data[i] < min) min=data[i]; } sum=sum/10; answer[0]=sum; answer[1]=min; answer[2]=max; } エラー error C2198: 'keisan' : 呼び出しに対する引数が少なすぎます。 とでて先に進めません。 教科書などを見ても間違いがわかりません; 虫食いになってるところを自分なりにやってみたため、 根本的に間違ってるかもしれませんが、 どうかアドバイスや指摘などをお願いします。

  • Cのローカル変数でstatic以外の使い方?

    C言語の課題について教えてください [課題] 以下の関数がある。各関数の引数、変数は自由に設定していい ・int main() ・void func() ・Point *get() { /* 構造体のアドレスを返す */ } ・構造体 typedef struct { int x; int y; int h; int w; }Point; 問題 main関数から、func関数を経由して、get関数を経由し値を取得し、表示する 以下が考えたソースになりますが、これだと、 ローカル変数でstaticを使っているので、get関数が固定値ではなく、 取得のたびに値が変わるような場合には、だめだといわれました。 考えたのですがよくわからないので、どういう場合に駄目なのかと、 どのように修正すればいいのか教えてください。 #include <stdio.h> typedef struct { int x; int y; int h; int w; }Point; void func(Point **); Point *get(); int main(void){ Point *get; func(&get); printf("get.x:[%d]\n",get->x); printf("get.y:[%d]\n",get->y); printf("get.h:[%d]\n",get->h); printf("get.w:[%d]\n",get->w); return 0; } void func(Point **pw){ *pw = get(); printf("Wrapper: pw==%p\n",pw); } Point *get(void) { static Point pget; pget.x = 2; pget.y = 2; pget.h = 30; pget.w = 40; return &pget; }

  • 関数でエラー

    #include<stdio.h> int func(int, int); int main() { int a, b, c; a = 10; b = 20; c = func(a, b); printf("%d x %d" = %d\n", a, b, c) return 0; } int func(int a, int b) { int c; c = a * b; return c; } エラーが出てしまうのですが、どうしてエラーが出るのか教えて頂けないでしょうか? エラー一覧です。 ------------------------------------------------------------------------------------------------------------------------------- 配列型 const char[8]を割り当てることはできません。 14行目 ;がreturnの前にありません              16行目 式は変更可能な左辺値である必要があります      14行目 ;が必要です                     16行目 よろしくお願いします。

  • クラス内の関数内static変数について

    クラス内の「staticではないメンバ関数内で定義される」static変数の初期化タイミングはいつでしょうか? 自分としてはクラスのインスタンス生成時に初期化されるものだと思っていたのですが、どうもそうでは無さそうだという現象に出会ったもので。 例えば以下のようなサンプルプログラムがあるとします。 --------------------------------------- class TA { public: void func(int i); }; void TA::func(int i) { static int d=0; d += i; std::cout << d << std::endl; } int main() { for(int i=1; i < 3;i++) { TA ta; ta.func(i); ta.func(i); ta.func(i); } } --------------------------------------- これを実行した時、自分としては 1 2 3 2 4 6 という結果を期待していた訳ですが、実際には 1 2 3 5 7 9 という結果になりました。 ということは、もしかしてメンバ変数ではなくともクラス内に現れるstatic変数はstaticなメンバ変数と同等ということなのでしょうか? 実際、上記ソースのforループ内にもう一つclass TAのインスタンスtbを追加してみると、 --------------------------------------- for(int i=1; i < 3;i++) { TA ta; ta.func(i); ta.func(i); ta.func(i); TA tb; tb.func(i); tb.func(i); tb.func(i); } --------------------------------------- 1 2 3 4 5 6 8 10 12 14 16 18 となりました。 (まぁstaticではないメンバ変数に置き換えれば一応解決するのですが、個人的に何か凄く気持ち悪く感じて・・・)

  • プログラム

    アドバイスをもとにいろんなサイトを見てみたんですが、なんとなくはわかるんですがこれから先どうしたら良いかわかりません。a,bを入力する画面まではいくんですが、それから先の結果がでないんです。何がいけないんですか? #include <stdio.h> double func(double x){ double y; y=x*x*x-3*x*x+9*x-8; return(y); } int main(){ double EPS=0.00005; double a, b, c; char t; int i=0; do{ printf(" a = "); scanf("%lf%c", &a, &t); printf(" b = "); scanf("%lf%c", &b, &t); if(func(a)*func(b) >= 0){ printf(" f(a)*f(b)>0\n\n"); } }while(func(a)*func(b) >= 0); if(b-a<0){ c=a; a=b; b=c; } while(b-a>EPS){ c=a-((b-a)/(func(b)-func(b)))*func(a); if(func(c)*func(a)<0){ b=c; }else{ a=c; } i++; printf(" %d\t%20.15f\n",i,c); }

  • 配列の受け渡し

    全く原因が分からないので質問させていただきます. 次のようなプログラムがあります. メイン関数で配列を宣言して, これを関数funcに渡して,func内で処理を行う. その後,メイン関数でprintf()を使って配列の内容を表示させる. 私の書いたソースは次のようになっています. int main(void) { double data[]={1.0, 2.0, 3.0, 4.0}; for(i=0; i<4; i++) { printf("%f\n", data[i]); } func(data, 4); for(i=0; i<4; i++) { printf("%f\n", data[i]); } } void func(double *f, int n) { 配列に処理を行う } メイン関数の中で, 一つ目のprintf()はちゃんと配列dataの初期値を表示します. ところが,二つ目のprintf()の手前で, 「例外」とエラーがでてきます. (コンパイルは通っています.) これが何故なのか,さっぱり分かりません. 関数func()の最後にprintf()を書いてみたところ, ちゃんと処理を行った後の正しい値が表示されます. なので,関数func()自体にバグはないように思うのです. 原因が分かる方,いらっしゃいましたら, どうぞよろしくお願いします.

専門家に質問してみよう