• ベストアンサー

配列ポインタの関数中のメモリ領域

C初心者です。 関数中で配列ポインタを宣言する場合についての質問です。 たとえばDouble型の2次元のローカルな配列ポインタを用いる場合、 その配列要素数が100である場合は void 関数名(引数1,引数2,...){ int i; double *a[2]; for(i=0;i<2;i++){ a[i] = (double*)malloc(100*sizeof(double)); } for(i=0;i<2;i++){ free(a[i]); } } またこの値を引数1とする場合、引数1をoutとすると void 関数名(double *out,....) とし、 for(i=0;i<2;i++){ out[i] = a[i]; } とすればよいのでしょうか? もしこれがあっているとすると、つぎのような現象で困っています。 配列要素数を50000個ぐらいとし、複数の関数で、同様に mallocを用いて、配列ポインタのローカルでメモリ領域を確保しようとした場合、コンパイルは成功するのですが、その後実行すると、エラーが発生したというメッセージとともにコマンドウィンドが強制終了します。 コンパイラはVisual C++ EXpress Edition 2008です。 データサイズを小さくすると、エラーは起きません。 malloc関数で確保するメモリサイズは、関数の入力引数で定義された変数を用いて計算しており、データサイズに応じて変更されます。 よろしくお願いいたします。

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

  • ベストアンサー
回答No.5

func2の実装が分からないので、コメントアウトして動作確認しました。 下記の数カ所を直したら、こちらの環境では問題なく動作しましたよ。 もし下記の部分を修正してもエラーになるようなら、func2内に問題があります。 for (i=0; i<2; i++);{ out[i]=(double*) malloc(datasize*sizeof(double)); } お礼の方に書かれたコードではセミコロンを挟んだ形になってますが、元のソースコードもこの書き方になっていないか確認して下さい。(ここ以外にも数カ所ありました) 何もしないforループと、無意味な中括弧という解釈でコンパイルが通ってしまうんですね。 C++で書いてるとまずあり得ないミスなのでこういう事もあるんだなと、逆に勉強になった感じです。 >Expressの使い方のインストラクションはWEBで転がってますでしょうか? すみません、申し訳ないですがちょっと分からないですね。Express Edition自体使わないものですから。

bonzomania
質問者

お礼

大変ありがとうございました

その他の回答 (5)

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

Express Edition でもデバッガはあるはず. あと, 「配列ポインタ」っていわれると「配列へのポインタ」と思われるかもしれないのでそのような表現はやめた方がいい. 素直に「ポインタの配列」といえば誰にでも通じるのに, なんでこんなけったいな表現が使われるんだろう. 「int の配列」を「配列int」っていう人はいないと思うのになぁ.

回答No.4

2次元配列をどう宣言するかで、2種類の方法があります。 一つは連続した領域を一度に確保する(*a)[MAXSIZE]。 もう一つは2回に分けて別々の領域を確保する*(a[2]) == *a[2]。 後の方法では、アドレスを覚えておくための領域が2個必要です。 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #define MAXSIZE 20 typedef double WORD; //void func(WORD (*b)[MAXSIZE]){ void func(WORD *b[2]){ int i, j; for(i=0;i<2;i++){ for(j=0;j<MAXSIZE;j++) printf("%f ", b[i][j]); puts(""); } } int main(void){ int i, j; // WORD (*a)[MAXSIZE]; WORD *a[2]; // a = malloc(sizeof(WORD)*MAXSIZE*2); a[0] = malloc(sizeof(WORD)*MAXSIZE); a[1] = malloc(sizeof(WORD)*MAXSIZE); for(i=0;i<2;i++){ for(j=0;j<MAXSIZE;j++){ printf("%p ", &a[i][j]); a[i][j] = j; } puts(""); } func(a); // free(a); free(a[0]); free(a[1]); return 0; }

bonzomania
質問者

お礼

大変ありがとうございました。

回答No.3

>データサイズを小さくすると、エラーは起きません。 メモリの食い過ぎですね。 おそらくfreeが実行されてない為にエラーが起きると思われます。 つまり、関数1でmalloc→関数2でmalloc→・・・→関数2でfree→関数1でfree、という順番に実行されるとメモリの食い過ぎでエラーになります。 もしくはfreeで渡すポインタを間違えている、freeを書き忘れているか、計算ミスであり得ない量のメモリを確保している、という可能性も。(まー、可能性としては低いと思いますけど) で、原因の確認方法なんですが、デバッグモードをお勧めします。 Express Editionにあったか忘れてしまいましたが、メニュー項目のデバッグ→デバッグ開始で実行するモードの事です。 このモードで実行すると、エラーが起きた行で停止して教えてくれます。今回の場合、malloc関数内でエラーが起きてると思われるので、malloc関数内の意味不明な所で停止するかもしれません。でも、そんな時は関数の呼び出し履歴があるのでそれを辿ると、どこでmallocを呼び出したかが確認出来ます。 これ凄く便利な機能なので、もし使えるようなら積極的に活用されると良いと思います。

bonzomania
質問者

お礼

今のコードの構造はざっとこんな感じなのですが、 #include <stdio.h> #include <stdlib.h> //大域変数宣言 int i,k; //関数宣言 void func1(double *out[], double *in[], int datasize); void func2(double *local[],int datasize); void main(){ double *in[2] double *out[2] double in1[] = {1.3,2.3,3.3,4.3,5.3,6.3,7.3,8.3,9.3,10.3}; double in2[] = {1.3,2.3,3.3,4.3,5.3,6.3,7.3,8.3,9.3,10.3}; int datasize = 10; for (i=0; i<2; i++);{ out[i]=(double*) malloc(datasize*sizeof(double)); } in[0] =&in1[0]; in[1] =&in2[0]; func1(out,in,datasize); for (i=0; i<2; i++);{ free(out[i]); } } void func1(double *out[], double *in[], int datasize){ double *local[2]; for (i=0; i<2; i++);{ local[i] = (double*) malloc(datasize*sizeof(double)); } func2(local,datasize)<-localはこの関数から出力されます。 for (i=0; i<2; i++){ for (k=0; k<datasize; k++){ *(out[i]+k)=*(local[i]+k)*(*(in[i]+k)); } } for(i=0;i<2;i++){ free(local[i]);<-デバックの結果によるとここでヒープ領域エラーとなってます?? } } なぜエラーになるのか、解説をお願いいたします。

bonzomania
質問者

補足

デバックモードは使ってませんでした。 使い方が良くわからず、もっぱらコンパイルと「デバックなしで実行」を使ってます。 Expressの使い方のインストラクションはWEBで転がってますでしょうか? よろしくお願いいたします。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

その > void 関数名(引数1,引数2,...) > void 関数名(double *out,....) の関連がよくわからないのですが、 /* func1(確保したポインタの配列(double **), 確保する大きさ) */ void func1(double *out[2], int length) {  int i;  double *a[2];  /* length個のdoubleの領域を確保 x 2 */  for(i=0;i<2;i++){   a[i] = (double*)malloc(length *sizeof(double));  }  /* 確保した領域のアドレスをoutに格納 */  for(i=0;i<2;i++){   out[i] = a[i];  }  /* 確保した領域を解放 */  for(i=0;i<2;i++){   free(a[i]);  } } /* 呼出側 */ void main(){  double *in[2] ;  func1( in, 100 ) ; } ...ってことですか? その「関数名」関数の中で確保した領域を、関数の外で使用したいなら、関数内でfreeで解放するのは間違いです。 呼び出し側は既に解放された領域のアドレスを受け取ることになります。解放された領域にアクセスしたら何がおこるかわかりません。小さいデータサイズで動いたのはたまたまです。 解放は呼び出し側で行います。 /* func2(確保したポインタの配列(double **), 確保する大きさ) */ void func2(double *out[2], int length) {  int i;  /* length個のdoubleの領域を確保 x 2 */  for(i=0;i<2;i++){   out[i] = (double*)malloc(length *sizeof(double));  } } /* 呼出側 */ void main(){  double *in[2] ;  func2( in, 100 ) ; ...  /* in を使用した処理が終了したら領域を解放 */  for(i=0;i<2;i++){   free(in[i]);  } } あと >void 関数名(double *out,....) では >out[i] = a[i]; でエラーになりませんか?(すくなくとも、手元のgccではエラーになります) out[i]はdouble型 a[i]はdouble型のポインタ ですから

bonzomania
質問者

補足

すいません。まず、double *outではなくdouble *out[]でした。 メモリは関数内で開放しなければならないのだと思っていたのですが、確かに、メモリの領域を確保して、それと関連付けられた変数を呼び出し先でも用いているとすると、呼び出し先でメモリ開放しないと、データの参照先がないことになりますね。 やってみます。 ありがとうございました。

  • koi1234
  • ベストアンサー率53% (1866/3459)
回答No.1

状況が良く把握できないのですが 問題の発生する最低限のソースを貼り付けられませんか? またエラー内容はどのようなものなのでしょうか? とりあえず気になったところで >out[i] = a[i]; 代入元になっているa[i]は内部変数とか言わないですよね? またmallocでエラーになっていることはありませんか? >またこの値を引数1とする場合、引数1をoutとすると 扱い方に問題なければ極論言えばどんな書き方でもOKです (書き方に問題あるならコンパイルでエラーになります) またコンパイルでエラーが起こらない=実行するのに問題ない ということではありません

関連するQ&A