C言語の話です。
A,B 二つのファイルにテキストのデータを書き込みます。
レコードを何件も書き込んでいくのですが、レコードとレコードは連続して書き込んでいきます。
各ファイルのレコードの構成を表す構造体が次のように定義されています。
A_RECのほうはAファイルで、B_RECのほうはBファイルです。
typedef struct{
charrec_kbn[1];
charnum[16];
charfiller[3];
}COM, *COMp;
typedef struct{
COMcom;
chara1[8];
chara2[2];
chara3[9];
charfiller[11];
}A_REC,*A_RECp;
typedef struct{
COMcom;
charb1[3];
charb2[15];
charfiller[22];
}B_REC,*B_RECp;
AとBのレコード構成は違いますが、
fillerというメンバは必ず空白(スペース)で埋めなくてはいけません。
fillerというメンバに対して何もしなかったら、そこには何が入っているのでしょうか。
ヌル文字('\0')で埋まっているのでしょうか。それとも、特定できないごみでしょうか。
空白で埋まっていないなら、空白で埋めることになるのですが、
fillerを空白で埋めるという操作をまとめて一つの関数にできないでしょうか。
どんな構造体(あるいは構造体のポインタ)を受け取っても、fillerというメンバは空白で埋めるという関数は作れますか。
やはり、fillerの位置やサイズは引数として受け取らなければ、そのような関数は作れませんか。
typedef struct{
charrec_kbn[1];
charnum[16];
charfiller[3]=" "; /* ダブルクォーテーションの間はスペース3つ */
}COM, *COMp;
このようにすることはできますか。できるならば、ちゃんと意図したとおり、空白で埋められているのでしょうか。
なお、*COMpや*A_RECpや*B_RECpですが、それは使わなくてもきまりごととして書くことになっています。
よろしくお願いいたします。
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);
}
みなさまがたへ。
みなさんありがとうございます。
読み返してみると、なんだか、私の知りたいことが私の質問にうまくあらわされていないようなので、
このまま、質問や補足を続けていても、答える側の方も混乱してくるかと思いますので、
まことにかってながら、この辺で締め切らせていただきます。
どの方のお答えも私の質問に対しては、正しいのだと思います。
ですから、ご回答されたみなさんに点を差し上げたいのですが、
そういうことにはいかないので、
できるだけ、わたしの知りたいこと(または それに近い方)に点を差し上げることにしました。
次に質問するときは、ちゃんと適切な質問を心がけます。
すっきりしない結末ですみません。
No.8
- 回答日時:
> 「文字配列の初期値つき定義において
> char str[3]="ABC";
> のように、配列の大きさと文字列の長さが等しい場合、
> '\0'が付加されることなく初期化される。」
すみません、すっかり記憶から抜け落ちていました。
確かに上の記述で、間違いなくchar型3つ分のみの初期化が行われます。
だいたい初期化子が多いと、コンパイルエラーになっちゃいますもんね。
ところで No.6 の補足より
> レコードを作成するもとになるデータを取得する。
と書かれていますが、この部分は出来ているのですか?(また、どこから取得してくるのでしょう?)
もし出来ているのなら、データの識別はどのように行っているのでしょうか?
データのサイズが異なるとなると、取得する部分自体が、それを踏まえてデータを読まないといけない気がするのですが。(A_RECとB_RECって、サイズ違いますよね?<間違い?)
No.7
- 回答日時:
naturalと申します。
質問を読ませていただいたところ、fillerというメンバーの役割は単純にサイズをそろえる為に各構造体の末尾に加えられているように見えたのですが、如何でしょう?
もしそうであれば(つまり使用されないメンバーであれば)、各構造体変数を定義した直後に構造体変数全体をスペースで初期化しておけば良い様に思われます。
そうすれば使用されないメンバーfillerはスペースで埋まったままファイルに書き込まれますし、読み出したときも同じ型の構造体変数に取り込めばスペースで埋まるでしょうし・・・。
想定されている使い方とは違いますか?
尚、fillerは末尾に加えられている、と書きましたが、初期化後書き換えられる可能性が無いメンバーでしたらどこに入っていてもOKですよね。
No.6
- 回答日時:
こんにちは。
itohhといいます。いっそのこと、初期化としてAとBのレコードをスペースクリアするっていうのは、NGですか?
例.
A_REC a;
memset( &a, ' ', sizeof(A_REC) ); // スペースでクリアする
これならば、どこにfillerがあっても、OKだと思うんですけど。
はずしていたら、ごめんなさい。
この回答への補足
ご回答は、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
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' が付いていることを忘れちゃだめですよ。
この回答への補足
>> 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'が付加されることなく初期化される。」
*************************************************/
あと、なるべくなら、この関数の中に、
A_RECp,B_RECp
のような特定の構造体に対応する記述は、本当はないほうがいいのですが...
(C_RECとかD_RECとか、どんなレコードにでも対応するようにしたい。)
それはできないんですよね.... (^^;
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回も訂正してすみません。
見なおし不足でした。
この回答への補足
もしも誤解があるといけないので。
質問中の
>どんな構造体(あるいは構造体のポインタ)を受け取っても、
というのは、
「構造体でも構造体のポインタでも、どっちにも対応できなくてはいけない」
という意味ではなく、
「どちらかに対応できていればいい」
という意味です。
質問が下手なのかもしれません。すみません。^^);
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
この回答への補足
fillerの初期化がどのようにされているかの話は、
typedef struct {
......
}A_REC, *A_RECp;
っていうのが問題というより、
A_REC a_rec;
という宣言がどこでされているか、staticがついているか、
によるという話ですね。
(参照URL1.30では、明示的に構造体の話は書いてないけれど。)
ありがとうございました。
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'
この回答への補足
早速ありがとうございます。
さすがなるほどなあと思ったのは、マクロを使えば、型に依存しない処理をかけそうだな、ということです。
Aに関する処理と、Bに関する処理をまとめて一つの関数にしたいのです。
よく考えさせていただきます。ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- C言語・C++・C# プログラムが書けません。 4 2023/01/22 22:57
- C言語・C++・C# C言語初心者 構造体 課題について 1 2023/03/10 19:30
- Excel(エクセル) こんにちは。Excelのことで教えてください。vLOOK関数の埋め込まれた列があり、その列の中で引用 3 2022/07/30 16:36
- PDF C#でfloatを整数部、小数部とも桁数固定で文字表示したい 2 2022/07/28 09:37
- 大学・短大 C言語線形リストの問題です 3 2022/12/22 00:45
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- Visual Basic(VBA) 3つのプロシージャをまとめたら実行時エラー発生で対応不能 6 2022/05/17 01:47
- Visual Basic(VBA) 特定の文字を簡単な操作で半角スペースに変換するか削除したい 2 2022/11/01 10:35
- その他(データベース) Accessのクエリで1フィールドの抽出条件設定をNullでなく全角半角含む空白のみの文字列でない文 1 2023/04/24 15:20
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
DataGridViewの内容をDBに反映...
-
ファイル書込みで一行もしくは...
-
JSPのNULLレコード表示について...
-
ADO VBA 実行時エラー3021
-
レコードセット(ADO.Recordset)...
-
DataGridViewにてセル以外をク...
-
差し込み印刷のレコード数について
-
ワードの差込印刷で教えて下さ...
-
データセットのレコード更新が...
-
アクセスでレポートの1印刷内...
-
サブレンジ分割されたNDB(富士...
-
AccessVBAのMoveメソッドにつき...
-
DataGridViewの、選択されてい...
-
[VB6]プログレスバーコントロー...
-
[VBA] ADOの Clone と AddNew
-
カレントレコードが無い事を判...
-
どのようなレコード構成でもfil...
-
【ExcelVBA】Powerクエリーでい...
-
Accessデータシートビューの行...
-
ACCESSで大量の更新を行うと「...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ADO VBA 実行時エラー3021
-
ファイル書込みで一行もしくは...
-
JSPのNULLレコード表示について...
-
レコードが存在しなかった場合
-
DataGridViewの、選択されてい...
-
カレントレコードが無い事を判...
-
DataGridViewの内容をDBに反映...
-
Access を×ボタンで閉じ...
-
アクセスでレポートの1印刷内...
-
ヘッダレコードとトレーラレコ...
-
Access でレコードセレクタが押...
-
レコードセット(ADO.Recordset)...
-
ACCESSで大量の更新を行うと「...
-
差し込み印刷のレコード数について
-
DataGridViewにてセル以外をク...
-
サブレンジ分割されたNDB(富士...
-
Line Inputで文字化け(助けて...
-
固有レコード識別子の選択とは
-
[VBA] ADOの Clone と AddNew
-
ワードの差込印刷で教えて下さ...
おすすめ情報