• 締切済み

メモリ領域確保に関して

C言語を始めて3ヶ月の初心者です。 下記のような定義で、領域確保をしたいのですが、 うまい方法がわかりません。 ご存知の方いらっしゃいましたら、 御知恵をお貸し下さいませんでしょうか? <test.h> ================================== #define SIZE_A (5) /* 親構造体 */ typedef struct { int testInt; testSmallStructT *testSmall; // 7バイト構造体の配列 char *testChar; // SIZE_A分の領域*配列数 } testBigStructT; /* 7バイト構造体 */ typedef struct { char str1[3]; char str2[4]; } testSmallStructT; /* メンバ変数 */ testBigStructT gTest[10]; ================================== ここで、あらかじめ全体の領域サイズを算出して、 mallocにてエリア確保を行う方法を求めてます。 また、多数にmallocを使用するとメモリ確保失敗時に、 それまで確保したエリアの開放を行わなくてはいけなくなる懸念から、 できるだけ使用しないようにしたいのです。 メンバ変数gTestを10の配列で持ち、構造体testBigStructTの、 要素testSmallとtestCharを可変の配列として扱いたくポインタ定義をしており、 更に、testCharにSIZE_A(5byte)の領域を確保しようとしております。 最終的には、下記のような使い方をしたいのですが、 メモリ確保の方法がわかりません。 =================================== (EX:) strcpy(gTest[0].testSmall[0].str1,"aaa"); strcpy(gTest[3].testSmall[2].str2,"bbb"); strcpy(gTest[6].testChar[3],"cccc"); =================================== 開放は下記の記述で問題ないと思っております。 free(gTest); 大変申し訳御座いませんが、 ご指摘・ご指導願いませんでしょうか? どうか宜しくお願い致します。

みんなの回答

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.4

> ここで、あらかじめ全体の領域サイズを算出して、 > mallocにてエリア確保を行う方法を求めてます。 > また、多数にmallocを使用するとメモリ確保失敗時に、 > それまで確保したエリアの開放を行わなくてはいけなくなる懸念から、 > できるだけ使用しないようにしたいのです。 #3 さんも書かれていますが,プログラミングの手間という観点で考えると, あまり楽になるとは思いません.解放の処理が楽になる代わりに, #2 に書いたコードのように,確保する処理は複雑になります. しかし,多数の (小さい) 領域をまとめて確保すると, 次のような利点があると思います. ・メモリの利用効率が良くなる.  malloc が割り当てる領域のサイズは,malloc が保証するアラインメント  (32ビットCPUの場合,8バイトまたは16バイトが多いと思われる) の  倍数に切り上げて管理されることが多い.  また,malloc は確保した領域ごとに,数バイト~十数バイト程度の  管理領域を設ける必要がある. ・メモリの断片化が起きにくくなる. そういうわけで,私はまとめて malloc するということをよくやります. 例えば可変長文字列の配列を確保する場合,普通は文字列へのポインタ配列 (char *array[]) と文字列本体 (char[]) を別々に確保しますが, 私は (Cの構造体風に書けば) 次のような領域をまとめて確保することが よくあります. struct {  // 文字列へのポインタ配列  // string[i] は,文字列本体 stringBody<i> を指す.  char *string[N];  // 文字列本体用領域  char stringBody<0>[可変長];  char stringBody<1>[可変長];  :  :  char stringBody<N-1>[可変長]; };

  • t_nojiri
  • ベストアンサー率28% (595/2071)
回答No.3

>また、多数にmallocを使用するとメモリ確保失敗時に、 >それまで確保したエリアの開放を行わなくてはいけなくなる懸念から、 >できるだけ使用しないようにしたいのです。 ここだけ反応。 失敗しようがしまいが、allocしたポインタは必ずfreeする必要が有るので、一緒。 スタックやリスト等使って、freeするアドレス管理した方が、きっと単純になります。 解放(開放じゃないよ)問題は、皆さん書いてるのであえて省略。

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.2

可変長となるのは,gTest[*].testSmall と gTest[*].testChar 用の 配列ですから,これらをまとめて確保した後,各 gTest[*].testSmall と gTest[*].testChar に配分すればいいことになります. gTest[i].testSmall の要素数が質問に明記されていないので, とりあえず gSmallCount[i] としておきます. また,↓の「配列数」というのは testSmall と同じ要素数という意味でしょうか? > char *testChar; // SIZE_A分の領域*配列数 確信が持てないので,一般性を持たせて gCharCount[i] としておきます. gSmallCount[*] の合計を totalSmallCount,gCharCount[*] の合計を totalCharCount とすると,確保すべきメモリ領域は次のような構造体に相当します. typedef char testCharT[SIZE_A]; struct VariableStruct {  testSmallStructT testSmall[totalSmallCount];  testCharT testChar[totalCharCount]; }; (totalSmallCount と totalCharCount は変数なので, 上記のような構造体宣言をソースとして書くことはできません. これと同じ構造のメモリ領域を確保する,という話です.) VariableStruct を確保・解放するコードは次のようになります. (インデントが潰れるのを防ぐため,半角空白を全角空白に置換しています.) //-------------------------------------------------------------------------- #include <assert.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #define OK  0 #define FAIL (-1) #define NTESTS 10 // gTest[] の要素数 #define SIZE_A 5 typedef char testCharT[SIZE_A]; // 7バイト構造体 typedef struct {  char str1[3];  char str2[4]; } testSmallStructT; // 親構造体 typedef struct {  int testInt;  testSmallStructT *testSmall; // 配列要素数:gSmallCount[i]  testCharT *testChar;     // 配列要素数:gCharCount[i] } testBigStructT; // 大域変数 ---------------------------------------------------------------- testBigStructT gTest[NTESTS]; testSmallStructT *gVariableStruct = NULL; // VariableStruct を指すポインタ // gSmallCount[i] は gTest[i].testSmall[] の要素数に初期化されているものとする. unsigned gSmallCount[NTESTS]; // gCharCount[i] は gTest[i].testChar[] の要素数 に初期化されているものとする. unsigned gCharCount[NTESTS]; /*-------------------------------------------------------------------------- 機能 :VariableStruct を確保し,各 gTest[*].testSmall および     gTest[*].testChar に配分する. 戻り値:成功ならば OK,失敗ならば FAIL. --------------------------------------------------------------------------*/ int VariableStructAlloc(void) {  unsigned totalSmallCount = 0; // gSmallCount[*] の合計  unsigned totalCharCount = 0; // gCharCount[*] の合計  unsigned i;  size_t size;  // totalSmallCount および totalCharCount を求める.  for(i = 0; i < NTESTS; i++) {   totalSmallCount += gSmallCount[i];   totalCharCount += gCharCount[i];  }  { // VariableStruct のサイズ size を計算する.   size = sizeof(testSmallStructT) * totalSmallCount;   // 注意:VariableStruct::testChar[*] は char 型の配列なので今回は不要だが,   //    [unsigned] char 型 (の配列) 以外の場合はここで size のアラインメント   //    調整が必要になる.   //    shinemik さんが初心者ということなので,ここではこれ以上深入りは   //    しませんが,[unsigned] char 型以外で同様のことをしたい場合は   //    アラインメントについて勉強してください.   //    (でも,その前にポインタと malloc/free をちゃんと使えるように   //     ならなきゃ.#1 さんのおっしゃるように,free(gTest) なんて   //     やってちゃだめですよ~.(笑))   size += sizeof(testCharT) * totalCharCount;  }  // VariableStruct を確保する.  if((gVariableStruct = malloc(size)) == NULL) return FAIL;  { // VariableStruct を各 gTest[*].testSmall   // および gTest[*].testChar に配分する.   testSmallStructT *p = gVariableStruct;   testCharT *q = (testCharT*)&gVariableStruct[totalSmallCount];   for(i = 0; i < NTESTS; i++) {    gTest[i].testSmall = p;    gTest[i].testChar = q;    p += gSmallCount[i];    q += gCharCount[i];   }   assert(p == &gVariableStruct[totalSmallCount]);   assert((char*)q == (char*)gVariableStruct + size);  }  return OK; } /*-------------------------------------------------------------------------- 機能 :確保していた VariableStruct を解放する. --------------------------------------------------------------------------*/ void VariableStructFree(void) {  if(gVariableStruct != NULL) {   free(gVariableStruct);   gVariableStruct = NULL;  } } /*-------------------------------------------------------------------------- テスト用 main() --------------------------------------------------------------------------*/ int main(void) {  unsigned i, j, k;  int c;  // gSmallCount[] と gCharCount[] をテキトーに初期化する.  for(i = 0; i < NTESTS; i++) {   gSmallCount[i] = i + 1;   gCharCount[i] = 2 * (i + 1);  }  // メモリを確保する.  if(VariableStructAlloc() == FAIL) {   fprintf(stderr, "Error: %s\n", strerror(errno));   return EXIT_FAILURE;  }  // 文字列を設定する.  for(i = 0; i < NTESTS; i++) {   k = i % 10;   for(j = 0; j < gSmallCount[i]; j++) {    c = j % 26;    sprintf(gTest[i].testSmall[j].str1, "%u%c", k, 'A' + c);    sprintf(gTest[i].testSmall[j].str2, "%u%c%c", k, 'a' + c, 'a' + c);   }   for(j = 0; j < gCharCount[i]; j++) {    sprintf(gTest[i].testChar[j], "%u-%02u", k, j % 100);   }  }  // 設定した文字列を表示する.  for(i = 0; i < NTESTS; i++) {   for(j = 0; j < gSmallCount[i]; j++)    printf("gTest[%u].testSmall[%u]: str1=\"%s\" str2=\"%s\"\n",        i, j, gTest[i].testSmall[j].str1, gTest[i].testSmall[j].str2);  }  for(i = 0; i < NTESTS; i++) {   for(j = 0; j < gCharCount[i]; j++)    printf("gTest[%u].testChar[%u]: \"%s\"\n", i, j, gTest[i].testChar[j]);  }  // メモリを解放する.  VariableStructFree();  return EXIT_SUCCESS; }

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.1

動的なメモリ確保自体が理解できていないようですね。 >開放は下記の記述で問題ないと思っております。 >free(gTest); gTest はスタック上の配列の先頭位置を指しているので free した途端にクラッシュしそう。 gTest[i].testSmall が動的に確保したメモリ位置を指すようにしたいなら、配列の要素毎に testSmallStructT 分のメモリを malloc して結果を gTest[i].testSmall に格納するしかないのでは? そして、利用がおわったら gTest[0].testSmall を順次 free する。 メモリ管理を testBigStructT にカプセル化したいなら C を諦めて C++ でコンストラクタ/デストラクタを書くしか。

関連するQ&A

  • メモリ領域の確保の仕方

    あまりうまく書けなかったのですがよろしくお願いします。 ファイルから二次配列を読み込むプログラムを作ろうとしました。 /*****行列Aに値を入れる********/ for(i = 0; i < Qap->iN; i++){ for(j = 0; j < Qap->iN; j++){ fscanf(fp, "%d", &Qap->matrixA[i][j]); } } /*****行列Bに値を入れる********/ for(i = 0; i < Qap->iN; i++){ for(j = 0; j < Qap->iN; j++){ fscanf(fp, "%d", &Qap->matrixB[i][j]); } } のように書くために、二次配列の領域を確保しようとしたとき、 #define SIZE 100 //行列のサイズ typedef struct hairetu{ int iN; int matrixA[SIZE][SIZE]; int matrixB[SIZE][SIZE]; }hairetu; としてmatrixA[SIZE][SIZE]のようにした場合は Qap = malloc(sizeof(hairetu));//hairetu分の大きさの領域を確保 とすれば領域を確保できたのですが、 typedef struct hairetu{ int iN; int **matrixA; int **matrixB; }hairetu; のように二次配列を**matrixAと表そうとして、 fscanf(fp, "%d", &Qap->iN); のようにファイルから行列のサイズを読み込もうとした場合、 Qap = malloc(sizeof(hairetu));//hairetu分の大きさの領域を確保 とやってもコンパイルはできるのですがアプリケーションエラーが出てしまいます。 何故だかよくわかりません。 二次配列を**matrixAのように表そうとした場合の領域確保の仕方を教えて下さい。 わかりづらくて本当に申し訳ありませんがお願いします。

  • mallocによる確保外の領域への参照

    構造体をmallocにより動的確保を行っていたのですが、例えば typedef struct _point{ int x, y; } point; point *pelem_point; pelem_point = (point *)malloc(sizeof(point)*5); このように、point型の構造体を5つ確保するとします。 しかし、 (pelem_point+100)->x = 1; (pelem_point+100)->y = 2; printf("%d\n", (pelem_point+100)->x); printf("%d\n", (pelem_point+100)->y); とやったら、確保していない100個先のところも構造体として利用できました。 なぜなのでしょうか。 自分の考えではこのようになりました。 mallocによりヒープ領域から適当な空いているメモリのアドレスが渡されるため、そこからはヒープ領域より先に、限りがあるまで進めてしまうために確保外のサイズにアクセスしても使えてしまっている。 また、mallocにより確保した場合は使用中のラベルがはられるため他に侵されることはないが、先の例のようにmallocによって確保してない場合はいくら使用できたとしても、空いているとコンピュータでは認識されるため、何かヒープ領域を使う場合に勝手に上書きされてしまう可能性がある。 しかし、この考えでも、なぜ確保外の領域が構造体のサイズ分ずつ区切られているのか納得いきません。 わかる方いましたらよろしくお願いします。

  • 基本的な領域確保の仕方について

    下記のような構造体が宣言されている場合、 A.c.e ←を配列扱いにし、 A.c.e[0].g.h ←を配列扱いにし、 A.c.e[0].g.h[0].iにデータを設定するには、 どのように領域を確保すれば良いのでしょうか? eee型はポインタ宣言のみされていて配列宣言されて いません。(Max10配列) typedef struct { int len; char *i; } hhh; typedef struct { int number; hhh *h; } ggg; typedef struct { fff f; ggg g; } eee; typedef struct { int number; eee *e; } ccc; typedef union { aaa a; bbb b; ccc c; ddd d; } A;

  • 領域の確保について

    callocやmallocで領域の確保が出来ると思いますが、領域が確保できなかったりする事があると思います。 char test[100000]; とか配列を宣言した場合も領域が確保されると思いますが、 これは確保する事が出来ない事とかはあるのでしょうか? また確保できなかった場合はどうなるのでしょうか? すいません、なんか勘違いしているかも知れませんが、宜しくお願いします。

  • mmap()した領域の領域確保

    mmapで特定のデバイスのIOメモリを仮想空間に割り付けて, その領域に対してユーザーからデータを書き込むことを考えています. ユーザープログラム側でその領域上に,ある構造体の双方向リスト を作りたいのですが,ユーザー空間の特定のアドレス範囲から空き メモリ領域を動的に確保する方法はないでしょうか void *vm_malloc(start_address, end_adress, nbyte); とすると"仮想アドレスstart_address~end_adressの範囲から空き領域を探し出し, nbyteバイトの領域を確保する"みたいなイメージです 一連の流れとしては,struct hogeをIOメモリに書き込む場合,  ・mmap()でIOメモリを仮想空間にマップ  ・struct hoge* addr = vm_malloc();でマップされた領域からsizeof(struct hoge)だけ領域を確保  ・*addr = .....;  ・vm_malloc, データの書き込みの繰り返し  ・fsync() のようなことをしたいと考えています.

  • メモリ動的確保について

    こんにちはです。 メモリの動的確保なのですが、 typedef struct DATA{ char name[256]; char pass[256]; int money; }BANK; void insert(BANK *p,int max); int main(){ int i; size_t st; BANK *person; person = (struct DATA *)malloc(sizeof(struct DATA)); //person = (struct DATA *)malloc(5); if(person == NULL){ printf("確保失敗\n"); exit(-1); } //memset(person,'\0',sizeof(struct DATA)); と、言う風に、記載ソースは途中ですがメモリをとりました。 mallocの後ろの部分ですが、sizeof(struct DATA)と5ではどうちがうのでしょう??2通りともコンパイルエラーはないです。 5は動的に最大5までとるって事はわかるのですが、struct DATAの方はいくつとるのです??いくつもで入力次第です? そして、動的したのにたいしてmemsetしたら実行エラー(コンパイルは通りました)おきました。動的にたいしてmemはダメなのでしょうか? アドバイスいただけたらありがたいです。宜しくお願いいたします。

  • メモリーの確保について

    皆さんはプログラムをするときに入力に応じて配列の大きさが変わるような場合には下記の方法のどれでプログラムしますか?こうすべきなのがベストだ!みないなお考えを聞かせてください。 (1)char str[100]のように大きなサイズを前もって用意しておく。これだとあとからデバック時に100って何か意味があるのかとか悩んでしまったりすると思います。 (2)malloc()とfree()関数を使って動的にメモリーを確保する (3)ポインタ配列を使う。char *strとか。 (4)その他 ご教授をよろしくお願いいたします。

  • 領域の確保の限界?

    typedef struct words{ char *w;//単語 char *m;//意味 }WORDS; という単語と意味を格納できる構造体を定義し それぞれが15文字程度の長さを格納させます。 現在100万語まではうまくいくのですが 1000万語にすると動かなくなってしまいます。 (動的にメモリを確保しています。) これって当たり前なのでしょうか? 当たり前ならその理屈を教えてもらえないでしょうか? また、解決策はあるのでしょうか? よろしくお願いします。

  • mallocで char *型の配列を確保したい

    char *方の配列を動的確保する必要が出たのですが、 char **array=(char **)malloc((char *) * 10); としてうまくいきません どうすれば確保できるのでしょうか 知っている方がいましたら教えてください ちなみに確保した配列はこの様に使えるようにしたいです strcpy(array[0],"testest");

  • 構造体で配列を使用しない方法。

    構造体で配列を使用せずに、メモリ領域を獲得する関数を使用する場合はどうすればよろしいですか??  例 char *str; struct seiseki{ char name[20]; int eig; int suu; int kok; char rank[3]; }; str = ()malloc();

専門家に質問してみよう