• ベストアンサー

どのようなレコード構成でもfillerという項目はスペースで埋める関数

C言語の話です。 A,B 二つのファイルにテキストのデータを書き込みます。 レコードを何件も書き込んでいくのですが、レコードとレコードは連続して書き込んでいきます。 各ファイルのレコードの構成を表す構造体が次のように定義されています。 A_RECのほうはAファイルで、B_RECのほうはBファイルです。 typedef struct { char rec_kbn[1]; char num[16]; char filler[3]; }COM, *COMp; typedef struct { COM com; char a1[8]; char a2[2]; char a3[9]; char filler[11]; }A_REC, *A_RECp; typedef struct { COM com; char b1[3]; char b2[15]; char filler[22]; }B_REC, *B_RECp; AとBのレコード構成は違いますが、 fillerというメンバは必ず空白(スペース)で埋めなくてはいけません。 fillerというメンバに対して何もしなかったら、そこには何が入っているのでしょうか。 ヌル文字('\0')で埋まっているのでしょうか。それとも、特定できないごみでしょうか。 空白で埋まっていないなら、空白で埋めることになるのですが、 fillerを空白で埋めるという操作をまとめて一つの関数にできないでしょうか。 どんな構造体(あるいは構造体のポインタ)を受け取っても、fillerというメンバは空白で埋めるという関数は作れますか。 やはり、fillerの位置やサイズは引数として受け取らなければ、そのような関数は作れませんか。 typedef struct { char rec_kbn[1]; char num[16]; char filler[3]="  "; /* ダブルクォーテーションの間はスペース3つ */ }COM, *COMp; このようにすることはできますか。できるならば、ちゃんと意図したとおり、空白で埋められているのでしょうか。 なお、*COMpや*A_RECpや*B_RECpですが、それは使わなくてもきまりごととして書くことになっています。 よろしくお願いいたします。

noname#836
noname#836

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

  • ベストアンサー
  • itohh
  • ベストアンサー率45% (210/459)
回答No.9

こんにちは。itohhといいます。 補足の内容からすると、 1.レコードの内容が設定されたものが、どこからか渡ってくる。 2.レコードの種類も渡ってくる。(A_REC?B_REC?) 3.渡ってきた段階では、Fillerには、何が入っているか保証されない。 4.2種類を超えるレコードの種類数がある。 一番いい方法は、レコードを設定する側でスペースクリアするのが常道だと思うのですが... 2種類ぐらいだと、マクロも良いかもしれませんが、何種類もあるとすると、汎用的にはなかなかいきませんよね。 (だから、ここに質問しているのだと思いますが...) こういう案は、如何ですか? 1.レコード毎にFillerの位置とサイズをテーブルにする。 2.関数には、レコードの設定されているエリアのアドレスと、レコード種類を渡す。 3.テーブルを参照してスペースを詰める。 ちょっと、力業っぽいところが、ありますが... こういう方法なら、net0505さんはわかってらっしゃるとは思うのですが、念のため。 typedef struct {   int mCD;   int mOffset;   int mLen; } strFiller; // レコード種類毎のテーブル static strFiller FilRec[] = {   { 1, 17, 3},    // A_REC(COM)   { 1, 39, 11},   // A_REC   { 2, 17, 3},    // B_REC(COM)   { 2, 38, 22},   // B_REC   {-1, -1, -1}   // EOF }; void FillerInt(char* pRec, int pRecCD ) {   int i=0;      For( i=0; FilRec[i].mCD != -1; i++ ) {     if( pRecCD == FilRec[i].mCD ) {       memset( pRec+FilRec[i].mOffset, ' ', FilRec[i].mLen );     }   } } void main() {   A_REC *a=NULL;   B_REC *b=NULL;   Set_aRec(a); // どっからか、設定されてくる。      FillerInt((char*)a, 1);   Set_aRec(b); // どっからか、設定されてくる。      FillerInt((char*)b, 2); }

noname#836
質問者

お礼

みなさまがたへ。 みなさんありがとうございます。 読み返してみると、なんだか、私の知りたいことが私の質問にうまくあらわされていないようなので、 このまま、質問や補足を続けていても、答える側の方も混乱してくるかと思いますので、 まことにかってながら、この辺で締め切らせていただきます。 どの方のお答えも私の質問に対しては、正しいのだと思います。 ですから、ご回答されたみなさんに点を差し上げたいのですが、 そういうことにはいかないので、 できるだけ、わたしの知りたいこと(または それに近い方)に点を差し上げることにしました。 次に質問するときは、ちゃんと適切な質問を心がけます。 すっきりしない結末ですみません。

その他の回答 (8)

  • leaz024
  • ベストアンサー率75% (398/526)
回答No.8

> 「文字配列の初期値つき定義において > char str[3]="ABC"; > のように、配列の大きさと文字列の長さが等しい場合、 > '\0'が付加されることなく初期化される。」 すみません、すっかり記憶から抜け落ちていました。 確かに上の記述で、間違いなくchar型3つ分のみの初期化が行われます。 だいたい初期化子が多いと、コンパイルエラーになっちゃいますもんね。  ところで No.6 の補足より > レコードを作成するもとになるデータを取得する。 と書かれていますが、この部分は出来ているのですか?(また、どこから取得してくるのでしょう?)  もし出来ているのなら、データの識別はどのように行っているのでしょうか?  データのサイズが異なるとなると、取得する部分自体が、それを踏まえてデータを読まないといけない気がするのですが。(A_RECとB_RECって、サイズ違いますよね?<間違い?)

noname#836
質問者

お礼

おっしゃるとおりですね。

  • natural
  • ベストアンサー率37% (419/1115)
回答No.7

naturalと申します。 質問を読ませていただいたところ、fillerというメンバーの役割は単純にサイズをそろえる為に各構造体の末尾に加えられているように見えたのですが、如何でしょう? もしそうであれば(つまり使用されないメンバーであれば)、各構造体変数を定義した直後に構造体変数全体をスペースで初期化しておけば良い様に思われます。 そうすれば使用されないメンバーfillerはスペースで埋まったままファイルに書き込まれますし、読み出したときも同じ型の構造体変数に取り込めばスペースで埋まるでしょうし・・・。 想定されている使い方とは違いますか? 尚、fillerは末尾に加えられている、と書きましたが、初期化後書き換えられる可能性が無いメンバーでしたらどこに入っていてもOKですよね。

  • itohh
  • ベストアンサー率45% (210/459)
回答No.6

こんにちは。itohhといいます。 いっそのこと、初期化としてAとBのレコードをスペースクリアするっていうのは、NGですか? 例. A_REC a; memset( &a, ' ', sizeof(A_REC) ); // スペースでクリアする これならば、どこにfillerがあっても、OKだと思うんですけど。 はずしていたら、ごめんなさい。

noname#836
質問者

補足

ご回答は、fillerまたはそれと同じようなメンバがどこにいくつあっても対応できる点がいいと思います。 ご回答はNo.2(#2)の方と近いですが、 私の質問の仕方ではまさしく、おっしゃるとおりの答えになると思います。 しかし、私が考えていることは、少し違います。 大きい目で見ると、 int piyopiyo(......) { ....... while(....) { レコードを作成するもとになるデータを取得する。 レコードのfillerメンバを空白で埋める。 レコードをファイルに書き込む。(エラーなら処理を中止し、-1をreturn) } 書き込んだレコード件数をreturnする。 } このような関数piyopiyoを作りたいのです。この関数piyopiyoは、A、Bどちらにでも使えるようにしたいのです。(実をいうと、本当は、A,B以外にもっと、ファイルがたくさんあるのです。) それで、質問で求めている関数は、 piyopiyoの中の「レコードのfillerメンバを空白で埋める。」 の部分の働きをするものです。 だから、 memset( &a, ' ', sizeof(A_REC) ); というように、aやA_RECというふうには、書きたくないのです。 質問の仕方が悪くて本当にすみません。m(__)m

  • leaz024
  • ベストアンサー率75% (398/526)
回答No.5

> fillerというメンバは必ず空白(スペース)で埋めなくてはいけません。 とありますが、これはA_RECやB_RECに含まれる、COM構造体のfillerも含まれますか?だとすると、toysmithさんのマクロは、さらに手を加えなければなりませんが・・・  まぁそれとは別に、もう少し安全な方法を。  構造体のサイズが違う点を利用して、関数で処理するようにしてみます。  まず、     void init_filler(void *pv, size_t size); というプロトタイプの、共用fillerメンバ初期化関数を作ります。  引数には、構造体変数のアドレスと、サイズを渡します。  これだけでは呼び出すのが面倒なので、呼び出しをマクロ化します。     #define INIT_FILLER(x) init_filler((x), sizeof(*(x)))  xには、構造体変数のアドレスを指定します。  こうすると、間違って構造体変数そのものを渡してしまっても、構文エラーになります。  肝心の関数の実装は、次のような感じです。     void init_filler(void *pv, size_t size)     {       A_RECp pa;       B_RECp pb;       switch (size) {       case sizeof(A_REC):         pa = (A_RECp)pv;         memset(pa->filler, ' ', sizeof(pa->filler));         break;       case sizeof(B_REC):         pb = (B_RECp)pv;         memset(pb->filler, ' ', sizeof(pb->filler));         break;       }     }  一応、     init_filler(NULL, sizeof(A_REC)); などという意地悪な呼び出し方をしない限り、それなりに安全に動作します。  また先に言ったように com.filler もスペースで埋めなければならない場合は、それぞれ1行ずつ memset の行を加えてください。  それから > typedef struct { >   char rec_kbn[1]; >   char num[16]; >   char filler[3]="  ";  ←ここ! > } COM, *COMp; という記述はできません。  これはあくまで型の「宣言」であって、変数の「定義」ではないからです。  メンバの初期化は、構造体変数の宣言時に、初期化構文に従って行ってください。  ただし、"  "(スペース3つ)では初期化できません。"  "には、見えない '\0' が付いていることを忘れちゃだめですよ。

noname#836
質問者

お礼

あと、なるべくなら、この関数の中に、 A_RECp,B_RECp のような特定の構造体に対応する記述は、本当はないほうがいいのですが... (C_RECとかD_RECとか、どんなレコードにでも対応するようにしたい。) それはできないんですよね.... (^^;

noname#836
質問者

補足

>> fillerというメンバは必ず空白(スペース)で埋めなくてはいけません。 >とありますが、これはA_RECやB_RECに含まれる、COM構造体のfillerも含まれますか? 含まれます。 私がなるほどなあ、と思ったのは、 void init_filler(void *pv, size_t size) と仮引数pvがvoid * で宣言されていて、 pa = (A_RECp)pv; pb = (B_RECp)pv; とキャストしているところです。 ありがとうございました。 > それから >> typedef struct { >>   char rec_kbn[1]; >>   char num[16]; >>   char filler[3]="  ";  ←ここ! >> } COM, *COMp; >という記述はできません。 > これはあくまで型の「宣言」であって、変数の「定義」ではないからです。  わかりました。 /************************************************ ところで、文字配列の初期化に関することですけど。 本質問の場合は違うのかもしれませんが、 『新版 秘伝C言語問答ポインタ編』(柴田望洋 著)(ソフトバンク) という本(p64)に気になる記述が書いてあります。 「文字配列の初期値つき定義において char str[3]="ABC"; のように、配列の大きさと文字列の長さが等しい場合、 '\0'が付加されることなく初期化される。」 *************************************************/

  • toysmith
  • ベストアンサー率37% (570/1525)
回答No.4

> 型に依存しない処理をかけそうだな Cは本来「型に依存する言語」ですから型に依存しない処理はあまり書かないほうが… この手のマクロは逃げ道です。 逃げ道を用意しなければならない状況とは 「開発メンバーがC言語を良く理解できておらず、OJTしているひまも与えられない」 っていう異常な場合が多いでしょう。 それ以外の状況では「ちゃんと型を意識したプログラミング」を指導するのが王道かと思います。 で、#1の私の書いたマクロ、めちゃくちゃ間違ってました。 #define Fill(xx) \ ((sizeof(xx) == sizeof(void *) ?\ (memset((xx)->filler, sizeof((xx)->filler), ' ')) : (memset((xx).filler, sizeof((xx).filler), ' ')) これでは動きませんね。 #define Fill(xx) \ ((sizeof(xx) == sizeof(void *) ?\ (memset((void *)&(xx)->filler, ' ',sizeof((xx)->filler))) : (memset((void *)&(xx).filler, ' 'sizeof((xx).filler))) 条件を満たせば動くと思いますがかなり危ないマクロです。 2回も訂正してすみません。 見なおし不足でした。

noname#836
質問者

補足

もしも誤解があるといけないので。 質問中の >どんな構造体(あるいは構造体のポインタ)を受け取っても、 というのは、 「構造体でも構造体のポインタでも、どっちにも対応できなくてはいけない」 という意味ではなく、 「どちらかに対応できていればいい」 という意味です。 質問が下手なのかもしれません。すみません。^^);

  • toysmith
  • ベストアンサー率37% (570/1525)
回答No.3

memsetの引数を間違えてました。 第2引数と第3引数が逆です。 ごめんなさい。

noname#836
質問者

お礼

ありがとうございます。

  • X4RR
  • ベストアンサー率62% (5/8)
回答No.2

C言語の仕様では変数はstaticで宣言されてる場合を除き、特定の値で初期化されることはありません(参考URLの1.30を参照) で、この場合の初期化の方法としてはmemset関数あたりが有効ですね。 COM c; memset(&c, ' ', sizeof(COM)); /*構造体を空白で初期化*/ とか。

参考URL:
http://www.catnet.ne.jp/kouno/c_faq/c_faq.html
noname#836
質問者

補足

fillerの初期化がどのようにされているかの話は、 typedef struct {   ...... }A_REC, *A_RECp; っていうのが問題というより、 A_REC a_rec; という宣言がどこでされているか、staticがついているか、 によるという話ですね。 (参照URL1.30では、明示的に構造体の話は書いてないけれど。) ありがとうございました。

  • toysmith
  • ベストアンサー率37% (570/1525)
回答No.1

マクロを連動使ってみては? sizeof(struct ...)!=sizeof(void *)であることを条件とすれば…。 #define Fill(xx) \ ((sizeof(xx) == sizeof(void *) ?\ (memset((xx)->filler, sizeof((xx)->filler), ' ')) : (memset((xx).filler, sizeof((xx).filler), ' ')) C99ならtypeofを使ってもっとスマートに出来ると思いますが現行ANSI-Cだとマクロを使わないと難しいと思います。 C++だとコンストラクタでfillerを空白埋めできますね。class化しないといけないから多少ややこしいかも。 それよりも… memset()を呼ぶだけだから素直に memset(&p->filler, sizeof(p->filler), ' ') ; とか memset(&p.filler, sizeof(p.filler), ' ') ; って書いちゃいけないんですか? あと、構造体変数の初期値は確保方法と記憶クラス、初期化指定に依存します。 初期化指定無し: 静的に実体を確保確保した場合='\0'。 自動変数として実体を確保した場合=不定 初期化指定あり: 初期化指定に従う 動的確保した場合(標準関数を想定): malloc()の場合=不定 calloc()の場合='\0'

noname#836
質問者

補足

早速ありがとうございます。 さすがなるほどなあと思ったのは、マクロを使えば、型に依存しない処理をかけそうだな、ということです。 Aに関する処理と、Bに関する処理をまとめて一つの関数にしたいのです。 よく考えさせていただきます。ありがとうございました。

関連するQ&A

  • 構造体のファイル出力

    以下のような構造体を、テキストファイルに保存するにはどうしたらよいでしょうか? 構造体には、4つのデータがある場合、以下のようにします。 [構造体] typedef struct comp_bango { char bango[3]; } COMP; 出力テキスト 100 200 300 400

  • ファイル操作について

    ファイルから情報を読みこみ構造体に設定しようとしています。 方法としては、fopen,fgetsにより行取得しようと考えているのですが、ファイルが以下のようになっており、 キーワードに対応する構造体も決まっています。 力作業で設定しても良いのですが、何かもっと美しい方法があれば助言お願いします。 ファイル内容 (A) AAA="12345" BBB="33333" (B) BBB="1122" CCDD="5674" (C) TTTT="#####" AAAA="kkkkk" 設定する構造体 typedef struct{ char AAAA[20]; char BBBB[20]; }A; typedef struct{ char BBB[20]; char CCDD[20]; }B; typedef struct{ char TTTT[20]; char AAAA[20]; }C; です。

  • リトルエンディアン→ビッグエンディアン

    (1)リトルエンディアン typedef struct recvData{  int a;  unsigned char b[16]; unsigned char c[8]; unsigned int d[4]; } recvData_t; recvData_t rData; (2)ビッグエンディアン typedef struct sendData{  int a;  unsigned int b[4]; unsigned int c[2]; unsigned int d[4]; } sendData_t; sendData_t sData; 上記のようなリトルエンディアンの構造体の各メンバのデータを、ビッグエンディアンの構造体の各メンバのデータにそれぞれ格納するには どうしたらよいでしょうか?

  • 構造体・ビットフィールドのvolatileに関して。

    以下のような構造体があったとする。 -(1) -----------------------------  typedef volatile struct ABC{    char x;    char y;  }stABC; -(2) -----------------------------   typedef struct ABC{    volatile char x;    volatile char y;  }stABC; --------------------------------- ■質問  -------------   stABC abc;   abc.x // ←volatileは有効?   abc.y // ←volatileは有効?  ------------- ・このとき、メンバx,y は(1)/(2)の構造体型宣言どちらでも  同じ意味合いになっているのでしょうか。  ※特に(1)の場合、volatileは有効になっているのでしょうか。 ・ビットフィールド時も同じと考えて問題ないでしょうか。   (1)typedef volatile struct ABC{      char x: 1 ;      char x: 7 ;    }stABC;   (2)typedef struct ABC{      volatile char x: 1 ;      volatile char x: 7 ;    }stABC;

  • free()について

    free()についてです。 よろしくお願いします。 ある構造体Aがあったとして その構造体Aの中に構造体Bのポインタが あったとします。 typedef struct{ char b; short c; } B_t; typedef struct { int a; B_t *bbb; } A_t; この構造体をプログラム中でポインタで扱い mallocで領域確保している場合に(A,B共に) Aをfree()した場合は、Bも開放されるのでしょうか? 以下、質問のサンプルです。 A_t *aaa; aaa = (A_t *)malloc(sizeof(A_t)); aaa->bbb = (B_t *)malloc(sizeof(B_t)); : : free(aaa); /* ←これで、aaaのメンバであるbbbは */ /* 開放されるのでしょうか? */ よろしくお願い致します。

  • if文の分岐をelseにする事ができません。

    以下のような定義の構造体があります。 この構造体のメンバであるchar型配列Cが、 【実行】にあるようにif文で使われている時にelseの処理2を実行する事ができませんでした。 char型の変数だと、'\0'が入っていればelseとなるのですが、 配列となっているCにstrcpyで'\0'や0,NULLを設定してみてもelseにはなりませんでした。 この様なif文でelseの処理2を実行するにはどうすれば良いのでしょうか? 初歩的な質問かも知れませんが、回答頂けると幸いです。 【定義】 typedef struct { struct { char C[100]; }B }A 【実行】 static A* hoge; hoge = (A*)malloc(sizeof(A)); if(A->B.C) { 処理1 }else{ 処理2 }

  • 異なる構造体のデータのコピー

    (1)で受け取った構造体のメンバのデータを (2)の構造体のメンバにコピーしたいと考えています。 (1) typedef struct _recvData{ int data01; int data02; char data03; char data 04; char data05; char data 06; char data07; char data08; char data09; char data10; char array01[16]; char array02[16]; } recvData; (2) typedef struct _sendData{ int header; int data01; int data02; char data03; char data 04; char data05; char data 06; char data07; char data08; char data09; char data10; int array01[4]; int array02[4]; } sendData; そこで2点ほどおしえていただきたく、お願い致します。 (1)構造体が微妙に異なるため、各メンバ変数に値をそれぞれ代入していくしか方法がないのでしょうか? (2)array01, array02はデータの型が異なる場合、データのコピーはどのようにしたらよいでしょうか?

  • 構造体のメンバーの静的なサイズ取得

    構造体のメンバーをヘッダーファイル中で得たい場合、 以下のような方法以外になにか方法はないでしょうか? 単純に XXXX x;と宣言してそれを sizeof(x.chwk)と 使うというような方法以外で なにかあれば教えてください。 ---------------------------------------- typedef struct { char chwk[100]; } XXXX; typedef struct { char chwk[ sizeof(( (XXXX*)0)->chwk ) ]; } YYYY; main(int arc, char *arg[] ){ YYYY y; printf( " YYYY chwk size = %d\n", sizeof(y.chwk)); }

  • 入れ子の構造体について

    例えば、入れ子の構造体を1つ使いたい場合、 struct bbb{ int b; }; typedef struct aaa{ struct bbb a; }AAA; AAA dt; と書くと、「dt.a.b = 10」とやれば、値等を設定できると思いますが、 入れ子の構造体を2つ使いたい場合も、同じように書けるのでしょうか? struct ccc{ int c; }; struct bbb{ struct ccc b; }; typedef struct aaa{ struct bbb a; }AAA; AAA dt; dt.a.b.c = 10; と書けるのでしょうか?こんがらがってしまって、どう書いていいのか・・。 2つでも出来るのであれば、コードの書き方を教えて頂けませんか?

  • C++言語で、構造体のコピーは可能(しても良い)のでしょうか?

    C++言語で、構造体のコピーは可能(しても良い)のでしょうか? 問題がある場合は、なぜだめなのか知りたいです。 構造体は可変長ではありません。 typedef struct kumi { char namae[10]; int ten; }Kumi; Kumi a, b; strcpy(a.namae, "AAA"); a.ten = 50; b = a;

専門家に質問してみよう