質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

5回答

3476閲覧

階層構造をもつ構造体で、効率よく静的な領域を確保したい

imudak

総合スコア40

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2016/06/27 05:45

データ列を解析して以下の複数パターンデータをもつメッセージを受信します。

  • uint8
  • uint16
  • blob(バイト列) : 最大255
  • 子の配列:最大15(孫はなし)

このとき、受け側のメッセージ構造体を、mallocを使わずに静的にあらかじめ
確保しておきたいです。
単純に作ると以下のようにn x nの領域を確保することになってしまいます。
子がいなければBlobの最大サイズで済むのですが…

このような場合、どのように構造体を組むのが賢いのでしょうか。

自分では、静的にメモリを確保した独自メモリプールを用意してオレオレmallocを作る、
くらいしか思いつきません。
でも結局最大サイズ必要なのだから、構造が複雑化するだけのような気もしますし…

#define MAX_BLOB_SIZE 255 // BLOB最大サイズ #define MAX_NUM 15 // 子の最大サイズ // メッセージの型 enum MsgType_enum { MsgType_Uint8, MsgType_Uint16, MsgType_Blob, MsgType_Childs, MsgType_Max, } ; // メッセージの型 typedef uint8_t MsgType; typedef struct Message_st { uint8_t id; uint16_t msgLen; Data recvData[MsgTypeMax]; // 型の数だけ用意 } Message; // データ部 typedef struct Data_st Data; typedef struct Data_st{ uint8_t id; MsgType type; uint16_t dataLen; union { uint8_t u8; uint16_t u16; uint8_t blob[MAX_BLOB_SIZE]; Data childs[MAX_NUM]; } data; } Data; // 実際にはシングルトンで1インスタンスのみ確保。 Message recvMsg;

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

こんにちは。

それ以前にData_stの定義部がコンパイル通らないように思います。
まず、Data型を不完全な状態で実体(childs)を定義しようとしてます。これは無理では?
考え方的にも、Data型はchilds[MAX_NUM];を含むため、循環定義となるので無理な気がします。

次に、上記問題を回避した場合ですが、「静的」に領域を定義するということは、コンパイラがその領域の定義を把握できると言う意味ですので、内部のサイズが異なる構造体を定義するためには、異なる構造体として定義することが必須です。この事実は、静的型付け言語ではいかんともし難いです。

C++であれば、Data型をテンプレートとしMAX_BLOB_SIZEとMAX_NUMをsize_t型のテンプレート・パラメータとした上で、それぞれの値毎にテンプレートを実体化するようなコーディングを行えば、Data型を「静的」に定義し、取り扱うことが可能になります。
しかし、この方法でも、「内部のサイズが異なる構造体を定義するためには、異なる構造体として定義することが必須」を回避できるわけではありません。MAX_BLOB_SIZEとMAX_NUMを「静的に」与える必要がありますので、恐らく事実上役に立たないだろうと思います。

投稿2016/06/27 07:56

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

imudak

2016/06/27 08:38

回答ありがとうございます。 擬似コードに簡素化する際に問題があったようです。失礼しました。
imudak

2016/06/28 00:38

ご指摘をきっかけに、書き直したコードを別回答(上?)に書きました。確かに元のコードでは無限にメモリが必要になりますね… 孫がいないことが条件なので、上記回答でメモリ必要最小限にできたかもしれません。
guest

0

static で malloc を使わないのであれば、
始め
char *last, pool[大きい数] ;
と用意して
void *my_malloc(int size) {
void *ret ;
ret = last ;
last += size ;
return *ret ;
}
みたいにする以外はないのでは?
char pool[大きい数] ;と宣言するとobjectのサイズが大きくなり、それが問題な場合は、アセンブラで非初期化領域を確保する方法が使えます。

staticでなくてもいいなら、OSの低レベルメモリ確保機能を使います。brk。その上にmy_mallocをかぶせることになります。mallocを使いたく無い理由と密接に関係あると思いますが、mallocは比較的遅く、多くの人が思っているよりも中身は複雑です。汎用性を考えるとmallocする量が大きい時と小さい時、malloc/freeが頻繁に行われる時など考えることは多いです。
そのあたりの話は、古い本ですが、「UNIX システムコール・プログラミング」が詳しいです。

単にリーク、debugのことを心配しているのならば、my_mallocよりもmemory debuggerを使うことを考えたほうがいいです。

投稿2016/06/27 06:55

gm300

総合スコア580

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

imudak

2016/06/27 08:35

回答ありがとうございます。 オレオレmallocはご提示のコードのようなものを考えていました。 動的確保避けるのは速度面の制約から来ている部分が大きいのですが、mallocとメモリデバッガ案も検討してみます。
gm300

2016/06/27 23:26

単に速度の改善であれば、objectを1つづつmallocするのではなく、100個単位とかで処理することでmallocのover headを減じることができます。速度面重視のmallocの実装もあります。
imudak

2016/06/28 00:46

std::stringとかでまとめてメモリ確保するのと同じような仕組みですね。ありがとうございます。速度重視のmalloc探してみます。
guest

0

「静的」という前提であるなら、提示のコードでいいんじゃないでしょうか。
疑似mallocを作ってもわかりづらいだけだと思います。

投稿2016/06/27 06:13

ttyp03

総合スコア16996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

imudak

2016/06/27 06:18

回答ありがとうございます。やはり直球でシンプルに書くしかないようですね。
guest

0

オレオレ malloc は静的に含まれますか?
静的にという条件が付いている以上、必要となるかもしれない量の確保は必須だと思いますが。

投稿2016/06/27 05:58

Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

imudak

2016/06/27 06:18 編集

回答ありがとうございます。オレオレmallocの中は、静的に確保するので結局静的に含まれます。 やはり、しっかり確保するしかないようですね…
Zuishin

2016/06/28 02:37

malloc もプログラミングの開始時にまとめて確保したメモリの中から割り当ててるだけですよ。これをヒープと言います。
imudak

2016/06/28 07:35

ありがとうございます。 自分の理解だと、オレオレmallocの中でどさっと最初に確保すれば「スタック」になるかと思うのでいいことがあるかなと。
Zuishin

2016/06/28 07:49

free しなくても関数の終了とともに勝手に解放されるのは malloc に対する利点ですね。ローカル変数と比べたら、速度的には不利になりますしメモリ消費量は減りませんので意味ない気もしますが。
guest

0

疑似コードを書き直しているうちに思いつきました。
孫がいない前提なので、以下でよいかもしれません。
main()内に書いたように、少しメンバーへのアクセスが冗長にはなりますが…

#include <stdio.h> #define MAX_BLOB_SIZE 255 // BLOB最大サイズ #define MAX_NUM 15 // 子の最大サイズ // メッセージの型 enum MsgType_enum { MsgType_Uint8, MsgType_Uint16, MsgType_Blob, MsgType_Childs, MsgType_Max, }; // データ部(Base) typedef uint8_t MsgType; typedef struct Base_st { uint8_t id; MsgType type; uint16_t dataLen; union { uint8_t u8; uint16_t u16; uint8_t blob[MAX_BLOB_SIZE]; } data; } Base; // データ部(子) typedef struct Child_st { Base base; } Child; // データ部(親) typedef struct Data_st { Base base; Child childs[MAX_NUM]; } Data; // メッセージ typedef struct Message_st { uint8_t id; uint16_t msgLen; Data data[MsgType_Max]; // 型の数だけ用意 } Message; // 実際にはシングルトンで1インスタンスのみ確保。 Message recvMsg; int main(int argc, char** argv) { // Uint8 recvMsg.data[MsgType_Uint16].base.id = 1; recvMsg.data[MsgType_Uint16].base.type = MsgType_Uint16; recvMsg.data[MsgType_Uint16].base.data.u16 = 0xC0C3; // Childs recvMsg.data[MsgType_Childs].base.id = 1; recvMsg.data[MsgType_Childs].base.type = MsgType_Childs; recvMsg.data[MsgType_Childs].childs[2].base.id = 10; recvMsg.data[MsgType_Childs].childs[2].base.type = MsgType_Uint8; recvMsg.data[MsgType_Childs].childs[2].base.data.u8 = 0xF7; return 0; }

投稿2016/06/28 00:34

imudak

総合スコア40

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/06/28 02:00 編集

こんにちは。 childがbaseとstructを構成しているので多重化できていないようです。 Data型は ヘッダ・サイズ + MAX_BLOB_SIZE * MAX_NUMの大きさがあり、それをMsgType_Max個確保してます。元々書かれていたn x nのMsgType_Max倍のサイズになっていると思います。 問題ないでしょうか? ところで、実は「効率よく静的な領域を確保したい」が矛盾していると思います。 「効率よく」は動的に決まる値に応じて最小限の領域を確保したいの意味ではないでしょうか? つまり、効率優先ならばメモリの確保は動的に行わざる得ないと思います。 静的確保が優先ならば効率を断念するのが現実的と思います。 階層構造を外して、単にblobサイズに応じてメモリ確保を最適化する仕組みを考えてみる良いかも知れません。それでさえ「効率よく」静的確保できないと思います。
imudak

2016/06/28 07:28

回答ありがとうございます。 おっしゃる通りだんだん自分のもやもやした要求自体がおかしいような気がしてきました。 やはり一般化したコードを書こうとする限りMAX_BLOB_SIZEの掛け算が最大値を押し上げてしまうのは避けられないですね。 仕様に特化、最適化させて、BLOBはポインタにして、必要なBLOBを全パターン用意した方がよさそうです。 この辺で一旦質問閉じます。みなさんありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問