• ベストアンサー

2次元配列の動的確保について

共分散行列を2次元配列に格納しようとしているのですが、 その要素は、左下半分と右上半分が同じになるため 対角要素と、どちらか半分だけを格納してメモリを節約したいと考えています。 以下のように動的確保することでメモリは節約できているでしょうか? if( (a = (double **)malloc(sizeof(double *) * (N) ))==NULL ){ fprintf(stderr, "error malloc for a\n"); exit(0); } for( i=0; i<NN; i++ ){ if( ( a[i] = (double *)malloc(sizeof(double) * (i+1) ))==NULL ){ fprintf(stderr, "error malloc for a\n"); exit(0); } } *節約しない場合は、i+1 が N になります。 確保できているのなら、どのように参照すればいいのでしょうか?データの並び(?)は、a[0][0],a[1][0],a[1][1],a[2][0],a[2][1],a[2][2],,,というように並んでいるでしょうか? 例えば、a[0][1]を参照しようとすると、シグメンテーションフォルトなど起こりうるでしょうか。必要であれば、上プログラム内Nは、3000程度と考えてください。 そして、もし他にメモリを節約する上で良い方法があれば、ご教授していただけたらと思います。 よろしくお願い致します。

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

  • ベストアンサー
  • ranx
  • ベストアンサー率24% (357/1463)
回答No.5

> a[0][0]の次にa[1][0]が、a[1][1]の次にa[2][0]が、、、、a[i][i]の次に、a[i+1][0]が並んでいると考えてよろしいですか? ということですが、 inthefloiさんの > データの並びは、予想しているとおりに確保されています。 については、私は意見が異なります。ここら辺私が勉強したのはずい分と前の話ですから ひょっとすると最近は違うのかもしれませんが、 私が見たmalloc()のコードでは、一回のアロケーション毎に管理データを作っていました。 ですから、このアルゴリズムでは、  a[0] = malloc(...)  a[1] = malloc(...)  a[2] = malloc(...)    : という具合に呼んだら  |a[0]管理データ|a[0][0]|a[1]管理データ|a[1][0]|a[1][1]|a[2]管理データ|a[2][0]|a[2][1]|a[2][2]| とう具合に配置されると思います。 したがって、単純にデータのみが並んでいると考えることはできないということになります。 情報が古い分、「自信なし」です。

malaika
質問者

お礼

ご回答ありがとうございます! 上のinthefloiさんのご回答でも、そのようにおっしゃってますので、 自信持っていただいてよさそうです? 管理データの長さは、、と考えるよりも 素直に1次元配列に入れた方が良さそうですね。 勉強になりました。

その他の回答 (5)

noname#30727
noname#30727
回答No.6

>データの並びについてですが、a[0][0]の次にa[1][0]が、a[1][1]の次にa[2][0]が、、、、a[i][i]の次に、a[i+1][0]が並んでいると考えてよろしいですか? すいません。並びの部分をちゃんと読まずに、コードを見て、予想どおりと書いてしまいました。 malloc を繰り返した場合、それぞれが確保したメモリは連続しません。連続している事を前提とされるのでしたら、最初に ranx さんが書かれている通り、要素数 N*(N+1)/2 で malloc するしかありません。 その後、あえてテーブルを作るのであれば、参照するときのインデックス計算をあらかじめするだけです。 t = (double *)malloc(sizeof(double) * (N * (N + 1) / 2)); a = (double **)malloc(sizeof(double *) * N); for (i = 0; i < N; i++) { a[i] = &t[i * (i + 1) / 2]; }

malaika
質問者

お礼

三度の登場ありがとうございます! 一度にたくさんの質問を詰め込んでしまったので そのような誤解をされるのも不思議ではありませんね。。 以後気を付けたいと思います。 後半部のコードは参考にさせていただきます。 また、質問出してたらよろしくお願いします!

noname#30727
noname#30727
回答No.4

>#define COVXY(a,x,y)((x<=y?a[y][x]:a[x][y])) >では、まずいでしょうか? これは、私の趣味の問題かもしれません。 COVXYを左辺値としても有効にしたかったという事です。 右辺値としてだけ使うのならば、それでいいと思います。 >もし参照した場合はどうでしょうか。 OSやコンパイラによって結果は違うでしょうが、参照する場所が、OSから見て参照できない場所であれば、OSに怒れれるでしょう。ランタイムに悪影響があるか、何事もないか、別の場所が書き換えられなど、何が起こるかはわかりません。 >その他の疑問に関しては肯定していただけたと考えてよろしいですか? データの並びは、予想しているとおりに確保されています。 メモリの節約という意味では微妙です。malloc の仕様や、OSの仕様によりますが、ほとんどの場合、節約はできているでしょう。 ranx さんが書かれているように、1度で確保すれば、確実に節約できます。

malaika
質問者

お礼

再度ご回答くださいましてありがとうございます! 確かに、おっしゃる定義の仕方の方が便利ですね。 勉強になりました。 データの並びについてですが、a[0][0]の次にa[1][0]が、a[1][1]の次にa[2][0]が、、、、a[i][i]の次に、a[i+1][0]が並んでいると考えてよろしいですか? 補足になるのですが、配列aを、並列プログラム内で、Bcast(全ジョブに転送)しようとしているのですが、その時に、先頭アドレスと、要素数を引数として与えなければなりません。この場合、データの並びが考えている通りだとすれば、要素数は、N*(N+1)/2で良いと思うのですが、いかがでしょうか。 もしよろしければ、もう少しお付き合いしていただけたら感激です。

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.3

最後の部分のみ訂正。 a = (double*)malloc(sizeof(double)*n*(n+1)/2)) #define COVXY(a,x,y) ((x<=y)?a[(x)*((x)-1)/2+(y)]:a[(y)*((y)-1)/2+(x)])

malaika
質問者

お礼

ご回答ありがとうございます。 お礼が遅くなり申し訳ありませんでした。 分かっていただけていたらいいのですが、 for文内のNN⇒Nに訂正です。 ここで、ranxさんがおっしゃるnはNと考えていいですね。 一次元配列でとる方が安全ですか。 プロフェッショナルな方は、 2次元配列はあまり用いないのでしょうか?

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.2

とりあえず、Cとして(C++ではないものとして)お答えします。 > メモリは節約できているでしょうか? 一般的に言えば節約できていると推測されますけれど、アロケーションのアルゴリズムに依存するので、 確定的なことは言えないと思います。 > どのように参照すればいいのでしょうか? inthefloiさんのやり方が合理的だと思います。 > データの並び(?)は、a[0][0],a[1][0],a[1][1],a[2][0],a[2][1],a[2][2],,,というように並んでいるでしょうか? a[2][0],a[2][1],a[2][2]などはこの順に並んでいます。が、a[1][1]の次にa[2][0]が来るという保証はありません。 > a[0][1]を参照しようとすると、シグメンテーションフォルトなど起こりうるでしょうか 起こりえます。 > もし他にメモリを節約する上で良い方法があれば a = (double**)malloc(sizeof(double)*n*(n+1)/2)) #define COVXY(a,x,y) a[(x)*((x)-1)/2+(y)] なんてやり方も考えられますね。

noname#30727
noname#30727
回答No.1

>a[0][1]を参照しようとすると パフォーマンスの低下は仕方ないですが、アクセスの仕方を工夫してやればいいのじゃないかと思います。 #define COVXY(a, x, y) (*(x <= y ? &a[y][x] : &a[x][y])) COVXY(a, 10, 32) = 1.0;

malaika
質問者

お礼

ご回答ありがとうございます。 この場合、 #define COVXY(a,x,y)((x<=y?a[y][x]:a[x][y])) では、まずいでしょうか? おっしゃるように工夫をすればa[0][1]などを参照せずに済むとは思うのですが、 もし参照した場合はどうでしょうか。 その他の疑問に関しては肯定していただけたと考えてよろしいですか?

関連するQ&A

  • 2次元配列を動的に確保する方法

    /*ネットの*/ w = (double **)malloc(sizeof(double*) * h); w[0] = (double *)malloc(sizeof(double) * v * h); for(i=0; i<h; i++) w[i] = w[0] + i *v; /*自分の*/ w = (double **)calloc(h, sizeof(double *)); for(i = 0; i < h; i++){ w[i] = (double *)calloc(v, sizeof(double)); 自分は初心者です。自分は下のようなコードで書いていたのですが ネットで見つけたコードで上のようにデータの領域を全部確保してから 2番目以降のポインタを割り当てるようなやり方をしていました。 そのやり方は初めて見たのですが、こっちのほうが一般的なやり方なんでしょうか? それと何かメリットはあるんでしょうか? 一度に確保した方が確実に連続領域に割り当てられるとかかなぁとか予想してるんですが・・・

  • 関数内での多次元配列のメモリの動的確保について

    関数内で、参照渡しをして配列の動的なメモリの確保をしようとしているのですが、うまくいきません。 はじめに、main関数内で、 int main(void){ double *testdata1; testdata1 = (double*)malloc( sizeof(double) * 10 ); if( testdata1 == NULL ){ printf( "ERROR:testdata1" ); exit(0); } testdata1[3] = 20.4; printf( "test = %g\n", testdata1[3] ); } を実行したところうまくいきました。 そこで、 int main(void){ double *testdata1; Kakuho( &testdata1 ); printf( "test = %g\n", testdata1[3] ); } void Kakuho( double **testdata2 ){ *testdata2 = (double*)malloc( sizeof(double) * 10 ); if( *testdata2 == NULL ){ printf( "ERROR:testdata2" ); exit(0); } *testdata2[3] = 20.4; } としましたが、成功してくれません。 コンパイルは通りますが実行するとエラーが発生して落ちます。 (上記のprintfのERRORではありません。) 動作環境はXPのVCC7です。よろしくお願いします。

  • 2次元配列の動的確保

    ある画像を読み込むため、その画像を格納できる幅、高さを持った配列を動的に確保しようと考えています。 幅をxsize、高さをysizeで次のように記述しました。 unsigned char **src; int i; src = (unsigned char**)malloc(sizeof(unsigned char*) * ysize); src[0] = (unsigned char*)malloc(sizeof(unsigned char) * xsize * ysize); for(i=1; i<ysize; i++) src[i] = (src[0] + i * xsize); わざわざポインタのポインタを使用したのは、動的に確保した配列を2次元的なアクセスをしたかったためです。 画像の読み込み時は fread(src[0], sizeof(unsigned char), xsize * ysize, fp); としています。 上に記述したソースは問題なく動作しました。 しかし、上の場合だと全ての配列を連続して確保することができません。つまりsrcでmallocを一回、src[0]でmallocを一回使っているため、ポインタの配列の直後に配列を確保する保障がありません。そこでいっぺんに確保することを考えました。 unsigned char **src; int i; src = (unsigned char **)malloc(sizeof(unsigned char *) * ysize + sizeof(unsigned char) * xsize * ysize); for(i=0; i<ysize; i++) src[i] = (unsigned char *)(src + sizeof(unsigned char *) * ysize + i * xsize); このように組み上げ、読み込み時は上のfreadと同様に記述したところエラーが出てしまいました。 やはり一行目のmallocで無理やりsizeof(unsigned char *) * ysize + sizeof(unsigned char) * xsize * ysize分確保するのは失敗だったのでしょうか?

  • qsortと動的確保の2次元配列

    C言語で以下のようなソートのあるプログラムを作ろうとしているのですが、良い方法が思いつきません。。。。 どなたか,知恵を貸していただけないでしょうか? ・複数人の身長と体重がcsvファイルに2列に入っている。 人 身長 体重 1 158.9 50.5 2 161.2 72.3 3 150.4 42.8 4 170.7 80.4 5 165.0 59.9 ・ ・ ・ ・ ・ ・ ・↑このように身長も体重もランダムに並んでいる状態 ・身長・体重をプログラムで読み込んだら 身長の低い順にソートする。この時体重も身長に対応して並び換わってほしい。 (わかりやすいかと思い人の番号列を設けましたが、人の番号は考えなくて良いです) この問題に対して,データ数が不特定かつ多いため 動的確保の2次元配列を使ったクイックソートで対応を考えます。 qsortについてあれこれ調べていたのですが,動的確保でのqsort例が無く困っています。。。 どなたかちょっとアドバイスをいただけないでしょうか? よろしくお願いします。 #include <stdio.h> #include <stdlib.h> enum {HIGHT, WEIGHT, COLUMN}; int comp (const void *a, const void *b) //比較関数 { return (int) (((double *)a)[HIGHT] - ((double *)b)[HIGHT]) ; } int main(void) { ////////////////////////////////////////////////////////// 私情により、ソート以前の処理で使用するため あらかじめ .csv ファイルから fget で値を読み込んで コンマで分割しx[]y[]に格納してある。 &データ数をカウントしている 今は省略  仮定として以下のように5つのデータ数を読み込んでいたとする double x[5] = {158.9,161.2,150.4,170.7,165.0}; double y[5] = {50.5,72.30,42.8,80.4,59.9}; int n=5; //データカウント数 /////////////////////////////////////////////////////////// int i; double **list; list = (double**)malloc(sizeof(double)*5); for (i=0 ; i< 5 ; i++) { list[i] = (double*)malloc(sizeof(double)*COLUMN); if (list[i]==NULL) return 1;/* 領域確保に失敗したか */ } for(i=0;i<n;i++) { list[i][HIGHT]=x[i]; list[i][WEIGHT]=y[i]; } for(i = 0; i < n; i ++) printf("%lf %lf\n", list[i][HIGHT], list[i][WEIGHT]); puts("Sort"); qsort(list,n, sizeof(double [COLUMN]), comp); for(i = 0; i <n; i ++) printf("%lf %lf\n", list[i][HIGHT], list[i][WEIGHT]); scanf("%d",i); return 0; }

  • アドレス格納のための二次元配列のメモリ動的確保

    アドレス格納のための二次元配列のメモリ動的確保 二次元配列のためにメモリを動的確保しなければならないのですが、 その配列に格納したいものが 「DATA型のポインタ」です。(DATA型はtypedefした構造体です。) プログラム実行中にmallocで確保した、数あるDATA型の構造体の、その先頭アドレスを リストアップするための配列です。 この場合、どのような形でmallocすればよいのでしょうか? 教えていただけるとありがたいです。よろしくお願いいたします。 -- たとえば m×n のint型の配列は、 ◆ int *i; ◆ i = (int *)malloc( m * n * sizeof(int) ); となりますよね。 この要領がでやるのが一般的にわかりやすいものだとするならその方法でやりたい (後発の人が自分のソースコードを読む可能性があるため)のです。 -- 同様にm×nの「DATA型のポインタを格納するための二次元配列」を動的確保したい場合、 ◆ DATA *d; ◆ d = (DATA *)malloc( m * n * sizeof(DATA) ); この文にどのように付け加えたら良いのでしょう? もうあと一歩な気がするのですが(笑)。しかし参考書等で勉強しましたがわかりませんでした・・・。 わかる方、どなたかよろしくお願いいたしますm(_ _)m あとこれだけ通ればコンパイルが通るんです!!!!! たぶん(笑)

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

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #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));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • バイナリファイルの読み込み(C言語)

    raw(音楽ファイル)データを配列rawに読み込みたいのですが,バイナリファイルの読み込み方がわかりません. サンプルで以下のようなソース(途中略)があるのですが, ・なぜrawの型としてshortを使っているのか ・データ数の半分(file_size = ftell(fp) / 2)しか読み込んでいない ・fgetc(fp) << 8 あたりの意味がわからないので教えて下さい. -------------------------------------------------------- short *raw; if((fp=fopen(argv[1], "rb")) == NULL){ fprintf(stderr, "can't open %s.\n", argv[1]); exit(1); } fseek(fp, 0, SEEK_END); file_size = ftell(fp) / 2; fseek(fp, 0, SEEK_SET); raw = (short *)malloc((size_t)(file_size * sizeof(short))); if(raw == NULL){ fprintf(stderr, "malloc error\n"); exit(1); } for(i=0;i<file_size;i++) raw[i] = (short)((fgetc(fp) << 8) | fgetc(fp)); -----------------------------------------------------

  • 2次元配列の動的確保&配列の添え字の書式について

    (1)2次元配列の動的確保について 1次元配列の動的確保は理解できたのですが、次のプログラムの 文(ⅰ)~(ⅱ)の意味を理解できていないので教えてください。 int **a, j, k, nrows=3, ncolumns=4 ; a = (int **)malloc(nrows * sizeof(int *)); a[0] = (int *)malloc(nrows * ncolumns * sizeof(int)); …(ⅰ) //先頭アドレスに… for(j = 1; j < nrows; j++) a[j] = a[0] + j * ncolumns; …(ⅱ)   //この行が特にわかりません。 (2)配列の添え字について この書き方はどういう意味になりますか? out[X_SIZE * (i) + (j)] 分かる方、回答お願いします。

  • 配列の動的確保を関数化

    main()内でいくつかの配列を動的確保するとごちゃごちゃするので、 関数化してみました。しかし、下のプログラムではうまい事いきませ んでした。 #include<stdio.h> #include<stdlib.h> void array1d(int *box,int n) { int i; box=(int *)malloc(n * sizeof(int)); for(i=0; i<n; i++) { box[i]=0; } } main() { int *pol,i,n; printf("N pol\nN = "); scanf("%d",&n); array1d(pol,n); for(i=0; i<n; i++) { printf("[%d]=%d\n",i,pol[i]); } } 実行結果は [0]=-14646387 [1]=-1819410433 [2]=-224 array1d内では0を格納していますが、このようになりました。 どのようにすれば 0になるでしょうか? 回答よろしくお願いします。

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

    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; }