C言語関係掲示板

過去ログ

No.79. 綺麗で見やすいプログラム


どうもいつもお世話になってます。shuです。

最近ふと思ったんですが、自分の書いたプログラムは客観的に見て、
綺麗で見やすいのだろうか?、おかしな処理をしていないのだろうか?
ということです。
下にソースを載せましたので、おかしな点、改良点、質問などを
いただければうれしいのですが。

プログラム自体は、コマンドラインで入力された文字列より
フラグの判定をするためだけのものです。

arg.c
arg.h


こんにちは、ともじです。
#ユニークな題名に一瞬焦りました。shuさんはきっと綺麗・・・だと思います。

>最近ふと思ったんですが、自分の書いたプログラムは客観的に見て、
>綺麗で見やすいのだろうか?、おかしな処理をしていないのだろうか?
>ということです。

これは、常々自分でも思います。私は、プログラムは誰にでもわかるように
書くのがいいと思っているので、プロの方から見ればかなり稚拙な表現が
多いんじゃあないかと。
それを、公開しているんですから、お恥ずかしい限りです。

shuさんのプログラム、パッと見ですが、ヘッダファイルに関数のコードが入って
いるのが気になりました。やはり、別ファイルにするのがいいのではないでしょうか。
あと、私だったら、
                if (*argv[i] == '-') { expand.mode |= GetExpandMode(&argv[i][1], &expand); }
                else                             { puts("**** no flags ****"); break; }

は、行を分けますね。

shuさんのプログラムとは関係ないのですが、main()関数が最後に来るのは
嫌いです。
あと、if (*str) も嫌いです。
インクリメント/ディクリメント演算子を比較や代入と一緒に使うのも嫌いです。
なんて言っているといつまでたっても、カッコイイプログラムは書けませんが。


ども。


>shuさんのプログラム、パッと見ですが、ヘッダファイルに関数のコードが入って
>いるのが気になりました。やはり、別ファイルにするのがいいのではないでしょうか。

ヘッダファイルには、関数定義は書かないのが慣習です。
# こういう不文律があるのがCのよくないとこですね

あと、これは作法というよりテクニックになりますが、ヘッダファイルが複数回
読み込まれる場合に対応しておいたほうがいいです。

#ifndef ARG_H
#define ARG_H

/* ヘッダ本体 */

#endif

というような感じで。includeディレクトリの*.hをどれでもいいので
参照してみてください。


もひとつちょっとしたこと。

#define MODE_NORMAL 0 /* (0x00) */
#define MODE_A 1 /* (0x01) */
#define MODE_B 2 /* (0x02) */
#define MODE_C 4 /* (0x04) */

#define MODE_AB 3 /* (0x03) */
#define MODE_AC 5 /* (0x05) */
#define MODE_BC 6 /* (0x06) */
#define MODE_ABC 7 /* (0x07) */

のとこで、

#define MODE_AB ((MODE_A)|(MODE_B))
#define MODE_AC ((MODE_A)|(MODE_C))
#define MODE_BC ((MODE_B)|(MODE_C))
#define MODE_ABC ((MODE_A)|(MODE_B)|(MODE_C))

とすれば、見ただけで意味がわかります。まあ、MODE_ABとか書いてあるので
想像はつきますが。内側のカッコが冗長といえばそうですが、同じような定義の
しかたを何重にもしていた場合のことを考えて、ということです。
なお、これくらいはコンパイラが気を利かせてくれる(はず)なので実行効率は
(おそらく)同じになります。


switch文についてちょっと。
SwitchFlag()の中のswitch文ですが、defaultがありません。それと、一般に
defaultの処理の最後にもbreakをつけておくとちょっとは安心して眠れます。
switchはバグがでやすい文なので、気をつけすぎでも損は無いかと。


変数名についてですが、_ではじまる名前は何か意味があったような気がします
(気のせいかも)。現在標準でないけど将来標準になる可能性がある、だか、
準標準的な、だか、たしかそんなような感じです。ちょっと記憶が怪しくて
すみません。。


きれいかどうかとは少しはなれますが。
ポインタの宣言についてですが

char* p;

と書くか

char *p;

と書くかでちょっとした議論があります(きいたことがあるかもしれませんね)。
前者の信者は、「char型のポインタ」であることが明確にわかる、という
主張をし、後者の信者は、前者だと、char* p,q;とか書いたとき誤解する
可能性がある、という主張をしています。これにさらに、言語による主流も
からんできます。C++は前者が主流のようですが、Cではどちらかといえば
後者がやや多いようです(個人的な感覚で)。多くのCコンパイラはC++コンパイラ
でもあるため、学習時にC++のサンプルコードに(C++とはしらないうちに)触れる
機会もけっこうあるためか、Cプログラマが前者の方式に「かぶれる」ことも
あるようです。

ちなみに、char* p,q;みたいなことをやりたいときは
(*注意* プログラマの意図はchar* p;char* q;とする)

typedef char* char_ptr;

としておいて

char_ptr p,q;

とかするのもひとつの手かも。
# なお、#define char* char_ptr では意図した通りにはなりません


>shuさんのプログラムとは関係ないのですが、main()関数が最後に来るのは
>嫌いです。

これは、全体をトップダウンで把握するかボトムアップで把握するかという
趣味の問題かと(あるいは最初と最後のどっちから読み書きするかという問題)。
main()が最後にあるソースは、おそらく他の関数も、サブルーチンを全部
記述してから、というようになっていると思います(たぶん)。

なお、個人的な経験則としては、設計はトップダウンで、実装はボトムアップで
行うとよいようです(全部ひとりで担当するなら)。これを地でいくコーディング
方法は、プロトタイプ宣言を、初めの段階で全部書くというものですね(個人的
には気分や規模しだいでときどきやります)。


>インクリメント/ディクリメント演算子を比較や代入と一緒に使うのも嫌いです。

嫌いですか。。すみません、たまにやります。。。
たとえば、スタック操作など、

/* push */
stack[sp++] = x;

/* pop */
y = stack[--sp];

のように、意味的に一連の動作として考えられる場合には、いっしょに
書いちゃったりします(個人的な意見ですが)。なお、これも実行効率は
おそらくかわりません。


あとはコメント関係ですね。読みやすいコードについているコメントは
なんとなく共通しているので、その辺を意識してみるといいかも、という
ことで(詳細はめんどうなのでパス)。


必要があって読みにくい(例えばメモリの制限があるとか高速な動作が
要求されているとか)のか、ただ単にきたないのか見極める必要が
ありますね。そのためには、とにかく他人の書いたコードをたくさん
読むといいと思います。必要があって読みにくい場合は、コメントで
補っておくと、自分だけでなくそれを読む他人にも将来いいことがあるかも
しれません。
# 1か月後の自分は十分に他人です。。


とりあえず、いまおもいつくのはこれくらいです。

では。


いろいろと参考になりました。
どうもありがとうございました。


私が気づいたとこをいくつか。

・#define MODE_A 1 /* (0x01) */
これではコメントに意味がない気がします。
#define MODE_A 0x01 /* フラグの意味の説明 */
としとくといいかも。

・int flags;
個人的には符号付の型で論理演算するのがなんかいやです。
あとint自体もあまり好きじゃないです。
intってあまりに汎用的でなにが入ってるやらあれなんで。
typedef unsigned short Flags;
Flags flags;
とか適当にtypedefしとくといい気がします。

・for (i = 0; strlen(&strArgv[i]) > 0; i++) {
なんだかわかりにくい気がします。
あと1ループごとにstrlenを呼ぶのは無駄があるかも。
len = strlen(&strArgv[i]);
for (i = 0; i < len; i++) {
でいいような気がしますが、ちょっと自信なし。

>kikkさん
> 変数名についてですが、_ではじまる名前は何か意味があったような気がします
たしか"_"+大文字、"__"+小文字は予約語だった記憶があります。勘違いかも。


今回の質問でかなりの勉強になりました。
自分一人では気付かない点が結構あるものです。
皆さんのアドバイスを参考にしていろいろ修正してみました。

どうもありがとうございました!!

修正版

arg.c
arg.h
func.c
func.h


ども。

もうちょっとだけ。


func.hのなかでarg.hを#includeしないと、func.cの冒頭の#includeの順が
逆だった場合におこられるような気がします。なお、これを修正したとして
func.cにarg.hを#includeするかどうかはちょっと考えちゃいますね。
個人的には「する」ですが。。


ヘッダファイルの使い方(何を書いて何を書かないか)について。
# ちなみに、こういうことが取り上げてある本はきっと良書です

.cと.hは組になっていて、.hはインタフェース、.cはその実装とすると
いいかんじのモジュール(コンパイル単位/機能ユニット)になります。
.cと.hは絶対に1:1でなければならないというわけではありませんが。
でも、インタフェースとなる.hは1個にしといたほうがいいでしょう、きっと。
これは、単に使うだけならば、.cをみなくても、.hだけで用が足りる
ようにする、ということです。また、実装には必要だけれど、
そのモジュールの中からのみでしか使わないような関数や定数などは、
.hによって公開すべきではないとおもいます。簡潔、かつ、いいかげんな
いいかたをすれば「余計なものは公開しない」ということです。

なにを見せてなにを見せないかをよく考えれば、自然に.cと.hの書き分け
ができます。きっと。

処理の関数への分割のしかたはけっこういい線いってるとおもいます。


あと、これはきれいかどうかとはあんまり関係ないような気がするし、
趣味の問題かもしれませんが。

「初期化」および「フラグのチェック、展開モードを求める」
は1つの関数、例えば

GetExpandMode(int argc, char** argv, ExpandMode* expnad_mode)

とかにしてしまってもいいかな、とおもいました。もっとも、想定している
具体的な使い方がわからないので、本当のとこはなんともいえませんが。

SwitchFlag()の中身はswitchじゃなくifだとか、modeとflagsはどっちか
いらないんじゃないかとか、設計面で気になるところはありますが、ご本人は
そんなことは承知の上でやってるとおもいますので、そのへんは「ま、いっか」
ということで。。


>>kikkさん
>> 変数名についてですが、_ではじまる名前は何か意味があったような気がします
>たしか"_"+大文字、"__"+小文字は予約語だった記憶があります。勘違いかも。

やっぱりそんな感じですかー。勉強しなおさないと。。


きれいかどうかとはあんまり関係ないことばかり書いてしまったかも。。

では。


今晩は。

>.cと.hは組になっていて、.hはインタフェース、.cはその実装とすると
>いいかんじのモジュール(コンパイル単位/機能ユニット)になります。
>.cと.hは絶対に1:1でなければならないというわけではありませんが。
>でも、インタフェースとなる.hは1個にしといたほうがいいでしょう、きっと。
>これは、単に使うだけならば、.cをみなくても、.hだけで用が足りる
>ようにする、ということです。また、実装には必要だけれど、
>そのモジュールの中からのみでしか使わないような関数や定数などは、
>.hによって公開すべきではないとおもいます。簡潔、かつ、いいかげんな
>いいかたをすれば「余計なものは公開しない」ということです。

実務でCのプログラムを組んでいた頃の.cと.hの作り方は以下のようでした。

・関数は明らかに関数Bが関数Aに包括されるもの以外は別ファイルとする。
・ヘッダファイルは関数別ではなく機能別に作成する。
・各モジュールは標準ヘッダ、ユーザヘッダの順に#includeする。

というわけで、shuさんの.hの中に標準ヘッダが#includeされているのが
気になりました。私が実務でCプログラムを組んでから随分時が経つので
今のスタイルの標準はわからないのですが。

>>たしか"_"+大文字、"__"+小文字は予約語だった記憶があります。勘違いかも。

私も何かで読んだことがあるのですが、どこでだったか思い出せませんでした。
LSICとTurbo-C++のヘッダファイルを覗いてみましたが、"_"+小文字、"__"+大文字
で使用しているものもありました。
いずれにしても、"_"で始まる変数や関数名は作らない方がいいんでしょうね。


ども。


>・各モジュールは標準ヘッダ、ユーザヘッダの順に#includeする。
>
>というわけで、shuさんの.hの中に標準ヘッダが#includeされているのが
>気になりました。私が実務でCプログラムを組んでから随分時が経つので
>今のスタイルの標準はわからないのですが。

今のスタイルの標準はわかりませんが。。

これ、むずかしいですよねー。すべての.hと.cで、それぞれが標準ヘッダ、
ユーザヘッダの順っていう条件を満たしていても、ユーザヘッダを#include
する順番しだいでは条件が満たされなくなりますので。

再定義を避けるために、標準ヘッダ、ユーザヘッダの順にっていうこと
なんでしょうけど、たしかにこれを徹底すると再定義は避けられます。
でも、自分で作ったモジュールのポータビリティが落ちちゃうのが
つらいとこです。自分の.hのなかにANSIの定数や型を書きたくなることは
よくありますし。それと、自分がいま使ってる処理系の.hをみてだいじょうぶ
でもほかでは保証できない(標準ヘッダファイルの「内容」は実装に相当する
ので処理系依存)のも困ったものですね。

個人的に現実的な方法と思っているのは、

1. すべての.hと.cで、それぞれの中では必ず標準、ユーザの順で#include
2. ANSIの定数は避ける(=ANSIの定数は(なるべく)覚える)
3. それ以外の非標準なものは先頭に_がついていることを期待する

というようなものです(もっといいのおしえてー)。1.は、やれることは
やっておく、ということで。2.は、まあ、気にしなくてもたぶんコンパイル時に
おこられるので必死になって全部覚えることもないかも。3.は今まで見た
いくつかの標準ヘッダの実装から期待されるものです(どうも処理系依存な
シンボルは_ではじまるのが多い)。で、_ではじまるシンボルは使わない、
ということで。

Windowsプログラミングなんかだと、ANSIのにくわえてWindows用のシンボルが
出てくるので、気にしだすと眠れなくなりますね。。あ、気にしなきゃならない
シンボルが増えるのはべつにWinだけじゃなくて、特定環境用のプログラムなら
みんなそうですね。。。


ちなみに、標準のヘッダは一部の例外(assert.hだったかな)を除いて
どういう順で何回#includeされてもいいようになっています。


では。

p.s.
わかってるひとはわかってるとおもいますが。。
シンボルが再定義されたときのコンパイラ(プリプロセッサ)の動作、および
それを回避するための方法は、.hを複数回#includeするのを回避する方法と
同じです。くわしくは#ifndefとか#ifdefあたりを何かで調べてみてください。


こんばんは、詳しい返信ありがとうございます。

>>・各モジュールは標準ヘッダ、ユーザヘッダの順に#includeする。

>これ、むずかしいですよねー。すべての.hと.cで、それぞれが標準ヘッダ、
>ユーザヘッダの順っていう条件を満たしていても、ユーザヘッダを#include
>する順番しだいでは条件が満たされなくなりますので。

そうそう、悩んだ記憶があります。確か、このユーザ.hを#includeする
場合には、この標準.hも#includeするように、とかしたような。
でも、こうすると、ほんの数ステップの関数に#includeがいっぱいで
かっこ悪いんですよね。

>個人的に現実的な方法と思っているのは、
>
>1. すべての.hと.cで、それぞれの中では必ず標準、ユーザの順で#include
>2. ANSIの定数は避ける(=ANSIの定数は(なるべく)覚える)
>3. それ以外の非標準なものは先頭に_がついていることを期待する

参考になります。有難うございました。


さらに修正
http://web9.freecom.ne.jp/~shuz/main.lzh


ども。

そろそろほんとにこまかいことしかいえなくなってきましたが、もうちょっと。


各ヘッダファイル中でのヘッダの#includeは、そこで#includeする必然性が
ないように思います。.cのほうで#includeすればいいヘッダをヘッダによって
取り込んでいるようですが(もしかして何か意図が?)。


非公開関数はstaticをつけておくと、なお安心。。とかいいながら自分では
めんどくさくてつけないことが多いです。。いけませんね。。。
# なんでデフォルトがstaticじゃなくてexternなんでしょ?設計ミスかな??


以下はプログラムの仕様に関して推測を含んだ上での意見です。はずしている
可能性あり。マユにツバをつけて読んでください。。


MODE_NORMALは意味的には本当はMODE_DEFAULTでは?
そうだとして、GetExpandMode()のリターン直前に

if(*expand_mode==0)
 *expand_mode=MODE_DEFAULT;

という処理が必要になるはずです。NORMAL(DEFAULT)で、何らかのフラグがONと
いう状況も考えられることに注意。


あと、以前「ま、いっか」といったのですがやっぱりどーしてもきになるので
(スミマセン)。
SwitchMode()の実装は、enumではなくビットフラグを使っていることを考えると
意味的に以下のようになるはずです。

/* switchのとこです */
/* 2番目以降がelse ifではない点に注意 */
if(*expand_mode&MODE_A) {
/* MODE_Aの処理 */
}
if(*expand_mode&MODE_B) {
/* MODE_Bの処理 */
}
if(*expand_mode&MODE_C) {
/* MODE_Cの処理 */
}
/* これで全部。ほかの、例えばMODE_ABはAとBに引っかかる */

本質的にswitchを使わなければならない処理ならば、名前つき定数にはenumを
使うほうがおそらく適切だとおもいます。ビットフラグはいくつかの独立した
条件を重複して格納しなければならない場合に使うとよいかと。


あと、SwitchMode()ですが、「フラグにより処理を変える」というのは、実装
であって、関数の処理内容ではない(とおもわれる)ため、関数名が適切ではない
かと。たとえばdoExpand()とかがいいのでは?で、「フラグにより処理を変える」
というコメントは、switch(上記の修正をしたのであればif)の前につける、と。

さらに、よく考えると、そもそも「フラグにより処理を変える」というのは
ちょっと変なような気がします。「フラグ」で表現できるのは1/0であり、
条件分岐にフラグをつかうと、そのフラグが意味する処理をやる/やらない
になるので(重ねて書きますが、プログラムが行いたいことを推測した上で
書いていますので、見当はずれの可能性があります)。


あと、SwitchMode()(に相当する関数)がExpMode.cにあるのは適切なのかなー
と思いました。ちょっと、なんともいえません。


GetExpandMode()について。個人的なスタイルもからみますが。
以前
GetExpandMode(int argc, char** argv, ExpandMode* expand_mode)
とかかくといいと書きましたが、ちょっと補足。最後の引数はExpandModeが
(将来)より複雑になり、単一の整数型のビットフィールドでは用が足りなく
なった場合の(構造体で取り扱う)ことを考えてExpandMode*としました。
(元がそうなっていたというのもありますが)。で、そのような、拡張を考え
なくてもよいと判断できる場合は削ってしまって、返り値でExpandModeを
受けてもいいかな、とおもいます。その場合、他の関数にもExpandModeを
ポインタで渡さなくてもすみます。その場合は、
ExpandMode GetExpandMode(int argc, char** argv)
になりますね。
# 元のプログラムでポインタにしたのは、構造体をそのまま渡すコストを
# 考えてのことですよね??


GetExpandMode()について、もひとつ(根拠がほとんどないので無視してもOK)。
処理が冗長な「気がします」。気のせいかもしれません。どうしてそんな
「気がする」かというと2つのループの処理が似ているからです。想定している
入力とそれに対する処理がよく分からないので、これ以上はなんともいえません。


with推測はここまで。

以下は、指摘ではなく、こんなのもあります、ということで。


せっかく標準エラー出力というのが用意されているのに忘れていませんか?
(覚えているけどめんどうだから使わないという確信犯な人はここは飛ばしてOK)
まあ、たしかに面倒といえば面倒ですし、テスト時に一時的に表示させるのに
までつかうのはどうかな、っていうのもありますが。まあ、必要性を感じたら
ということで。ちなみに自分で使うときは以下のマクロを定義しておきます。
# マクロの名前についてなにか言われるかな。。

/* いまのとこ、これで虫がでたことはないですが、無保証 */
#define printerr(ERR_MSG) fputs(ERR_MSG,stderr)


exit()の引数やmain()の返り値は、正常が0で異常が非零のようですが、
別に意味をもたせるつもりはないが、具体的な数値を書きたくないとか、
何を書いたらいいかわからない(考えたくない)場合は、stdlib.hに
EXIT_SUCCESSとEXIT_FAILUREという定数がありますので、使うといいかも。
でも、簡単なプログラムだとstdlib.hを#includeする必要がない場合もあり
EXIT_〜のためにstdlib.hを#includeするのもなんだかなーという気分に
ならないでもないですが。。


では。


>各ヘッダファイル中でのヘッダの#includeは、そこで#includeする必然性が
>ないように思います。.cのほうで#includeすればいいヘッダをヘッダによって
>取り込んでいるようですが(もしかして何か意図が?)。

.c がすっきりすると思ったからです。
.cのほうで#includeしたほうがいいでしょうか?


>非公開関数はstaticをつけておくと、なお安心。
># なんでデフォルトがstaticじゃなくてexternなんでしょ?設計ミスかな??

なぜ非公開関数にstaticをつけるのですか?、理由が良くわかりません。


>switchMode()の実装は、enumではなくビットフラグを使っていることを・・・

この辺のところはいろいろ参考になりました。


>GetExpandMode()について。個人的なスタイルもからみますが。
>・・・・・
>ExpandMode GetExpandMode(int argc, char** argv)
>になりますね。
># 元のプログラムでポインタにしたのは、構造体をそのまま渡すコストを
># 考えてのことですよね??

ハイ。
ExpandMode* GetExpandMode(int argc, char** argv, ExpandMode* expand_mode)
とかにしようと思ったんですが、ゴチャゴチャするのが嫌だったもんで。
argcとargvもは入ってる構造体でもつくろうかな。


>GetExpandMode()について、もひとつ(根拠がほとんどないので無視してもOK)。
>処理が冗長な「気がします」。気のせいかもしれません。どうしてそんな
>「気がする」かというと2つのループの処理が似ているからです。想定している
>入力とそれに対する処理がよく分からないので、これ以上はなんともいえません。

例えば
main -a str1 str2 -
こんな入力の場合のエラーだったんですが、
考えたら必要無いですね。(おそらく)
考え過ぎておかしな処理を入れてしまいました。


>せっかく標準エラー出力というのが用意されているのに忘れていませんか?

正直って良く知りませんでした。


いろいろと勉強になりました。
ありがとうございます。


ども。


>>各ヘッダファイル中でのヘッダの#includeは、そこで#includeする必然性が
>>ないように思います。.cのほうで#includeすればいいヘッダをヘッダによって
>>取り込んでいるようですが(もしかして何か意図が?)。
>
>.c がすっきりすると思ったからです。
>.cのほうで#includeしたほうがいいでしょうか?

一般にヘッダはさまざまなファイルに#includeされる可能性があります。
で、今回の場合はヘッダ内で#includeしているヘッダは実装のみに必要な
ようです。ので、さまざまなファイルに#includeされるとすると、実装に
関わる部分をあちこちに書いてしまうことになります。それだとなんとなく
気持ち悪いというか、不安というか、そんな気がします。

以前にも書いたかもしれませんが、ヘッダの中でヘッダを#includeする
必要性が本当にあるのは、多分、型や定数を使いたいときでしょう。たとえば
FILE*を引数としてもつ公開する関数を作った場合、そのインタフェース
となるヘッダファイルでは、ヘッダファイル中でstdlib.hを#include
しなければならないでしょう。もちろんユーザ定義型等でも同じようなことが
起こる可能性はあります、でしょう。


>>非公開関数はstaticをつけておくと、なお安心。
>># なんでデフォルトがstaticじゃなくてexternなんでしょ?設計ミスかな??
>
>なぜ非公開関数にstaticをつけるのですか?、理由が良くわかりません。

staticには2つの意味(使い方)があります。

ひとつは、メモリの静的割り当ての指定です(こういう説明のしかたをすれば
なんでstaticというキーワードなのかちょっとは納得できる?)。autoは
そのブロックの実行時にメモリ割り当てが行われますが、staticの割り当ては
コードの実行開始時点におこなわれます。

もうひとつはスコープをファイル内におさえるという指定です。こっちは
トップレベルの変数や関数に適用した場合の作用です。なんでデフォルトが..
というのは、もちろんデフォルトがstaticのほうが安全だからです。

以下の2つのコードをそれぞれファイルを分けて書いて、test2.cのstaticを
コメントアウトした場合としない場合でコンパイルしてみてください。
cc test1.c test2.cとかやって

/* test1.c */
extern int f(); /* externは、なんとなく、というか、一応、ね */
int main() { return f(); }

/* test2.c */
/*static*/ int f() { return 0; }

それにしてもなんで1つのキーワードに2つの機能があるんでしょ。
設計ミスかな。。

ほかにも設計ミスっぽいのがありますね、Cって。たとえば、voidとvoid*は
意味が関係ないというか違いすぎるというか。。とか。


>ExpandMode* GetExpandMode(int argc, char** argv, ExpandMode* expand_mode)
>とかにしようと思ったんですが、ゴチャゴチャするのが嫌だったもんで。
>argcとargvもは入ってる構造体でもつくろうかな。

argcとargvも入ってる構造体を作った場合、それをセットするときに、
その構造体のメンバに手を出すことになり、きたなくなりがちなので、
いまのまま、あるいは、ExpandMode*の引数を削ったものでよいかと。


では。


またまた修正
http://web9.freecom.ne.jp/~shuz/main.lzh

kikkさんのアドバイスのおかげでかなりスッキリしました。
ありがとうございます。


ども。

フラグをビットで保持するのはやめたんですね。保持できる数が8とか16とか
32という制限がありますが、まあ、増えそうなときは、あらかじめ意味で
変数を使い分けするというのもありですね。おそらくご承知のことと思いますが
ビットで保持することの利点は、容量と、複数の条件(フラグ)を同時に判定
できるという点で、欠点は操作がめんどくさくなりがちということです。
これはトレードオフなので場面にあわせてということになりますね。

修正版4についてのこれ以上のコメントは完全に趣味の問題になりそうなので
とくにありません。
# そろそろスレッドの大きさが気になってきましたし。。

別スレッドでFAQを紹介しておいたのでそちらも参考にしてください。


では。

戻る


「初心者のためのポイント学習C言語」 Last modified:2001.11.15
Copyright(c) 2000-2002 TOMOJI All Rights Reserved