dポイントプレゼントキャンペーン実施中!

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

大変申し訳御座いませんが、
ご指摘・ご指導願いませんでしょうか?

どうか宜しくお願い致します。

A 回答 (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>[可変長];
};
    • good
    • 0

>また、多数にmallocを使用するとメモリ確保失敗時に、


>それまで確保したエリアの開放を行わなくてはいけなくなる懸念から、
>できるだけ使用しないようにしたいのです。

ここだけ反応。
失敗しようがしまいが、allocしたポインタは必ずfreeする必要が有るので、一緒。
スタックやリスト等使って、freeするアドレス管理した方が、きっと単純になります。
解放(開放じゃないよ)問題は、皆さん書いてるのであえて省略。
    • good
    • 0

可変長となるのは,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;
}
    • good
    • 0

動的なメモリ確保自体が理解できていないようですね。



>開放は下記の記述で問題ないと思っております。
>free(gTest);

gTest はスタック上の配列の先頭位置を指しているので free した途端にクラッシュしそう。

gTest[i].testSmall が動的に確保したメモリ位置を指すようにしたいなら、配列の要素毎に testSmallStructT 分のメモリを malloc して結果を gTest[i].testSmall に格納するしかないのでは?

そして、利用がおわったら gTest[0].testSmall を順次 free する。

メモリ管理を testBigStructT にカプセル化したいなら C を諦めて C++ でコンストラクタ/デストラクタを書くしか。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!