• ベストアンサー

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

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

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • ポインタのポインタの関数受け渡しについて

    現在ポインタのポインタを利用したプログラムを作成しています。 main関数で int **dt; と宣言したとして、配列のセットにはset関数を、 表示に関する処理をpt関数で行いたいと思っています。 void set(int ??); void pt(int ???); int main(void){ int **dt; set(??); pt(???); } void set(int ??){ dt = (int**)malloc(sizeof(int*) * k); for(i = 0; i < k; i++){ dt[i] = (int*)malloc(sizeof(int*) * k); } のように配列サイズの動的確保が目的 } void pt(int ???){ 二重forループ{ printf(dt[i][j]); } } ??、???には何を入れるべきかが理解できません。 ご教示のほどよろしくお願いいたします。

  • 動的なメモリ領域の確保

    double型変数5個分のメモリをmalloc関数により確保し,その確保した要素のアドレスを表示するように,プログラムを作る問題で、 (注)に「 %pで表示するためには,double型へのポインタ(double *)をvoid型へのポインタ(void *)にキャストする必要がある.」と書かれていたのですが、どういうことでしょうか? 以下のようでいいのでしょうか? #include<stdio.h> #include<stdlib.h> #define COUNT 5           // 動的に確保するメモリ領域数を示すマクロ定数の定義 int main(void) {  // 動的に確保するメモリ領域のアドレスを保持するポインタ変数の宣言  double * pointer;  int i;                  // for文で使用する変数の宣言  // int型変数5個分のメモリ領域を確保  pointer = (double *)malloc(sizeof(double) * COUNT);  if(pointer == NULL) {        // メモリ領域の確保が失敗した場合   printf("メモリ領域を確保できませんでした.\n");   exit(1);                // プログラムの終了  }  for(i = 0; i < COUNT; i++)   printf("%d番目のアドレスは%pです.\n", i + 1, pointer + i);  free(pointer);            // 確保したメモリ領域の解放  return 0; }

  • 配列表現とポインタ表現

    配列とポインタの2通りの表現で表せる場面によく遭遇します。例えば、pというdouble型の配列に乱数を10個発生させて格納したい時など、 for(i=0;i<10;i++) *(p+i) = (double) rand(); for(i=0; i<10; i++) p[i] = (double) rand(); のように、配列とポインタの2通りの表現が考えられると思いますが、複雑な場合などは特に、見た感じは配列のほうが分かりやすいと思います。 まだ、C言語の初級から中級向けの本しか読んでいないのですが、標準関数の多くがポインタを引数や返り値としていることを知りました。わざわざポインタ表現にすることの意義は、実行速度が上がることと、標準関数の多くがポインタを引数や返り値としているからと理解して良いのでしょうか。

  • 2次元配列とポインタの引数受け渡しについて

    2次元配列を関数に渡すときは、引数に渡す2次元配列と同じサイズを指定、もしくは2次元目のサイズのみ合わせて渡す方法がありますが、両方とも違うサイズで同じ関数を使いたいです。 最初は中身が同じで引数で受け取る2次元配列のサイズだけ、それぞれに合わせた引数を持つ関数を2つ作っていたのですが、なんだか冗長な気がしました。 そこで、2次元配列の先頭ポインタとサイズを受け取るようにすればいいのかと思い、テストとして次のプログラムを作成してみました。 #include <stdio.h> void func(unsigned char *a, int y, int x); int main(void) { unsigned char a[10][10]; func(a, 10, 10); printf("%d\n", a[7][4]); return 0; } void func(unsigned char *a, int y, int x) { int i, j; for (i = 0; i < y; i++) { for (j = 0; j < x; j++) { *(a + i*y + j) = i * j; } } } もちろんこれでも動くのですが、やはりこういう書き方はルールにはないので、コンパイルで警告が出ます。 a.c: In function ‘main’: a.c:10: warning: passing argument 1 of ‘func’ from incompatible pointer type a.c:4: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[10]’ このような書き方はやはりやめたいいのでしょうか。 また、その際はサイズ別に関数を作るしかないのでしょうか。 他にいい方法があれば教えていただけると助かります。

  • ポインタ配列

    "one","two","three","four","five","six","seven","eight","nine","ten" のポインタ配列の文字列を、ASCIIコード順に並べ変えようと思ったのですが、 もうどこが間違っているかさえわからないぐらいになってしまいました。 まだまだはじめたばかりなもので、わからないことだらけなんで、 できるだけわかりやすい説明おねがいします。 関数の引数に問題があるのじゃないかと思ったのですが、 何かいいアドバイスありましたら、お願いします。 #include <stdio.h> /* 関数のプロトタイプ宣言 */ int strmp(char *,char *); void cpy(char *,char *); int main (void) { /* ポインタ配列の定義 */ char *x[10]={"oneee","twooo","three","fourr","fivee","sixxx","seven","eight","ninee","tennn"}; /* ポインタのポインタの定義 */ char **pp=x; char k[100]; char *p=k; int i,t,a,b,c,d; a=0; /* ポインタ配列を自作関数を使って、ASCIIコードの大きいほうからに並び替える */ for(i=0;i<9;i++) { for(t=1;t<10;t++) { a=strmp(*(pp+i),*(pp+t)); if(a<0) { cpy(p,*(pp+i) ); cpy(*(pp+i),*(pp+t) ); cpy(*(pp+t),p); } } } for(i=0;i<10;i++) { printf("%s ,",x[i]); } printf("\n"); return 0; } /* 文字の比較をする関数 */ int strmp(char *x,char *y) { int i; for(i=0;*(x+i)==*(y+i);i++) { if( *(x+i)=='\0') { return 0; } } return *(x+i)-*(y+i); } /* 文字をコピーする関数 */ void cpy(char *a,char *b) { int i; for(i=0;*(b+i)!='\0';i++) { *(a+i)=*(b+i); } *(a+i)='\0'; }

  • 関数へのポインタ渡しでの配列の初期化について

    はじめまして、C言語の基本的な質問をさせてください。 C言語で、外部関数へポインタで引数を渡す場合に、 関数に渡されるのはアドレスですよね? で、渡された関数側でそのポインタの配列の初期化を するときにはアドレスだけの情報だと、要素数がいくつ あるか分からず、領域の破壊をしてしまいそうな気が するのですが?いかがでしょうか? また、関数かなんかで、配列の要素数が分かる関数が あったような気がするのですが、それもアドレスだけ でわかるのでしょうか?

  • 多次元配列のポインタ渡し

    C++を使用しています。 多次元配列を関数の引数として渡したいとき、関数側では void A::Func(int a[10][20][30])~ 呼びだし側では Finc(a); とやればいいのはわかります。 お聞きしたいのは、仮引数として呼び出された配列(上でいうa)をクラスのメンバ変数として保持したい場合の方法です。 aは先頭アドレスなのでそこを差すポインタを受ければいい、っていうことはわかりますが、 この方法ですと、受けたメンバ変数が配列みたいに[]を使ってアクセスできません。 (メンバ変数のポインタは配列じゃないから当然ですよね) これを通常の配列みたいに扱えるようにするにはどうしたらいいでしょうか。

  • C言語 動的なメモリの確保 コンパイル時エラーも警告もないのに実行できない

    配列A[3]を{2, 4, 6}と初期化させ、malloc関数を使いメモリを確保しそこへ先程の配列Aの要素を記憶させ、ポインタ*Nを使い確保したメモリの要素を表示するプログラムです。 ********************************************* #include <stdio.h> #include <stdlib.h> int main(void) {   int A[3] = {2, 4, 6};   int i;   int *N;   N = (int *) malloc (3);   for(i = 0; i < 3; i++){     N[i]= A[i];   }   for(i = 0; i < 3; i++){     printf("%d", N[i]);   }   free(N);   return 0; } ********************************************* VCを使いF10のデバッグテストでは正常に動くのですが、コマンドラインからではエラー報告画面がでます。なぜなのでしょうか?

  • 関数ポインタについて

    関数ポインタを使用する際に、 指定する先の関数でデフォルト引数が指定されている場合、 関数ポインタを用いて、その引数を省略して呼び出すことはできるのでしょうか? 以下例とします。 void function(int a = 10){}; typedef void (*test)(int a); test a = &function; a(); //このように呼び出したい

専門家に質問してみよう