| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
このノードでは, GNU ソフトウェアを書くときの C 言語の上手な 使い方を説明します.
5.1 ソースコードの整形 5.2 コメントの書き方 5.3 構文についての規約 5.4 変数,関数,ファイルの名前付け 5.5 異なる OS 間の移植性 5.6 サポートする CPU のタイプについて 5.7 システム関数の呼び出し 5.8 国際化 5.9 Mmap mmapの安全な使い方
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
C言語では, カラム0に関数本体の始まりの開き括弧を置くことが大事です. カラム0に関数の始まりでない括弧や, 開き丸括弧, 開き大括弧を書くのは やめましょう. カラム0に開き括弧があると関数の始まりとみなすツールがいくつかあります. そのようにコードが整形されてないとこういったツールはうまく機能しません.
関数定義において, 関数名をカラム0から始めることも大事です. 他の人が関数定義を検索するのに 役立つし, ツールが関数定義を認識するのにも助けになります. つまり, 適切なフォーマットは次のようになります.
static char *
concat (s1, s2) /* Name starts in column zero here */
char *s1, *s2;
{ /* Open brace in column zero here */
...
}
|
あるいは標準 C では次のように定義します.
static char *
concat (char *s1, char *s2)
{
...
}
|
標準 C で書く場合, 引数がうまく一行に収まらない場合には 次のように二行に分けます.
int
lots_of_args (int an_integer, long a_long, short a_short,
double a_double, float a_float)
...
|
この章の残りでは, C のフォーマッティングスタイルの他の面についての
我々のお勧めを紹介します. これは、indent プログラムのバージョン 1.2
以降のデフォルトのスタイルでもあります。これは、以下のオプションに
対応します。
-nbad -bap -nbc -bbo -bl -bli2 -bls -ncdb -nce -cp1 -cs -di2 -ndj -nfc1 -nfca -hnl -i2 -ip5 -lp -pcs -psl -nsc -nsob |
我々は以下のものを必須とは考えていません. 二つの別々のプログラムが異なるフォーマッティングスタイルを使っても ユーザには何の問題もないからです.
ただし, どんなスタイルを使うにしても, それを一貫して使ってください. 一つのプログラムでスタイルを混ぜて使うと読みにくくなるからです. 既存のプログラムに読者が行った変更を寄与するなら, そのプログラムの スタイルに従ってください.
関数本体のコードについては, 以下のようなスタイルを推奨します.
if (x < foo (y, z))
haha = bar[4] + 5;
else
{
while (z)
{
haha += foo (z, z);
z--;
}
return ++x + bar ();
}
|
開き丸括弧の前とコンマの後ろにスペースを置くとプログラムが読みやすくなります. 特にコンマの後には必ずスペースを入れましょう.
式を複数行に分けて書く時は演算子の後ろではなく, 前で分けます. 次のようにします.
if (foo_this_is_long && bar > win (x, y, z)
&& remaining_condition)
|
優先順位が異なる2つの演算子を同じ深さに字下げするのはやめましょう. たとえば, 次のように書いてはいけません.
mode = (inmode[j] == VOIDmode
|| GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])
? outmode[j] : inmode[j]);
|
代わりに丸括弧を補って, 字下げでネストを示すようにします.
mode = ((inmode[j] == VOIDmode
|| (GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])))
? outmode[j] : inmode[j]);
|
丸括弧を余分につけるとEmacs なら適切に字下げしてくれます. 例えば次の字下げは手で行なうのならこれでも良いでしょう.
v = rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000
+ rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000;
|
ですが, Emacs はこれを変更してしまいます. 丸括弧を追加すれば, 同じように見栄えが良くなり, Emacs もいじることをしません.
v = (rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000
+ rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000);
|
do-while 文 は以下のように書いてください.
do
{
a = foo (a);
}
while (a > 0);
|
プログラムの(関数の中ではない)論理的な位置でページ替えするときは, フォームフィード文字(control-L)を使ってください. 印刷されたページに収まる必要はないので,一ページの長さがどれだけになっても かまいません. フォームフィード文字は, 行中にそれだけで現れなければなりません.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
すべてのプログラムは, 何をするプログラムかを簡潔に説明するコメントで 始まってなくてはいけません. 例えば, `fmt - filter for simple filling of text' のように書きます.
GNU プログラムではコメントは英語で書いてください. というのは, 英語はほぼ全ての国の全てのプログラマが読むことの出来る唯一の言語だからです. 英語をうまく書けない人は, 出来る範囲で英語で書くようにして, 他の人に 書き直すのを手伝ってもらってください. 英語でコメントを書けない人は, 書いたコメントを英語に翻訳してくれる人を見つけてください.
関数ごとにコメントを書き,何をする関数か,引数は何か,引数の
値の意味と用途は何かを述べましょう.Cの型が慣例に沿った使われ方をしているなら,
Cの引数宣言の意味を繰り返して書く必要はありません.
標準的でない使い方(たとえば
char * 型の引数が,実際には文字列の一番目ではなく,二番目の文字の
アドレスであるような場合)があったり,
値によっては期待どおりに動かない可能性のある場合
(たとえば,改行を含む文字列は動きが保証されない,等)は,その旨を明記しましょう.
また, 戻り値があるならばその意味を説明します.
コメントのセンテンスの終りにはスペース2文字を置いて, Emacs のセンテンス用コマンドが使えるようにしてください. また, 完結したセンテンスを書くようにして, 最初の1語は先頭の文字を大文字に してください. センテンスの始めに小文字の識別子が来た場合は大文字にしないでください. 綴りを変えると違う識別子になってしまいます. 小文字で始めたくなかったらセンテンスを違ったやりかたで書いてください. ( 例えば,"The identifier lower-case is ...").
引数の値を説明するのに引数名を使えば, 関数のコメントはより明確になります. 変数名そのものは小文字であるべきですが, 変数自身でなく変数の値について言う 場合は大文字で書きましょう. つまり,"an inode" ではなく "the inode number NODE_NUM"と書きましょう.
関数の直前のコメント中で関数名を記述する必要は普通ありません.読む人が 自分で見ることができるからです.しかし,コメントが長過ぎて関数本体が一画面に 入り切らない場合などは別です.
各静的変数については次のようにコメントすべきです.
/* Nonzero means truncate lines in the display; zero means continue them. */ int truncate_lines; |
入れ子になっていない,数行しかない短い条件節のときは別にして, `#endif'にはすべてコメントを入れるべきです. そのコメントには,最後の条件節の条件を,肯定/否定を含めて書きます. `#else' の場合は,条件とそれに続くコードの肯定/否定を説明する コメントを書きます. 例えば以下のようになります.
#ifdef foo ... #else /* not foo */ ... #endif /* not foo */ #ifdef foo ... #endif /* foo */ |
これに対して, `#ifndef' の場合は次のように書きます.
#ifndef foo ... #else /* foo */ ... #endif /* foo */ #ifndef foo ... #endif /* not foo */ |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
全てのオブジェクトの型を明示的に宣言してください.例えば,
関数の引数は全部明示的に宣言してください.
int 型を返す関数も,int を省略しないで
明示的に宣言して下さい.
プログラマの中には GCC の `-Wall' オプションを使って, 警告が出たところを片っ端から直していくのが好きな人もいます. そうしたい人はどうぞおやりください. 他に `-Wall' が 好きではないプログラマもいます. 有効であり正統なコードに対しても 警告を出し, そのための変更はしたくないからです. こちらも, そうしたい方はどうぞご自由に. コンパイラは読者の召使 なのですから.
外部関数とソースファイルの後部に出現する関数の宣言はファイルの最初の方の
一箇所(最初の関数定義の前あたり)にまとめるか,ヘッダファイルに入れてください.
関数内に extern 宣言を入れないでください.
1つの関数内で同じ局所変数(tempのような)を何度も違う値で
使うことはよくありますが,こうしないで,目的別に違う局所変数を宣言して
その意味を反映した名前をつけたほうが良いでしょう.
プログラムが理解しやすくなるだけでなく, 良いコンパイラによる最適化の助けに
なるでしょう.
また, 各局所変数はそれを使っている最小のスコープ内に移動させるべきでしょう.
こうするとプログラムがきれいになります.
グローバルな識別子と同じ名前の局所変数や引数を使ってはいけません.
複数の変数を複数行にまたがって一度に宣言しないでください. 行ごとに新たな宣言を開始します.たとえば,
int foo,
bar;
|
とせずに,
int foo, bar; |
と書くか
int foo; int bar; |
としてください.(これらがグローバル変数ならば,その前にコメントも必要です.)
if 文の中にさらに if-else 文がある場合は
if-else を括弧で囲みます.
if (foo)
if (bar)
win ();
else
lose ();
|
とするのではなく,次のようにしてください.
if (foo)
{
if (bar)
win ();
else
lose ();
}
|
else 文の中に if 文があるなら,次のように else if
を一行に書きます.
if (foo) ... else if (bar) ... |
このとき, then 節をすぐ前の then 節と同じ深さに字下げして
ください.または,ネストした if を括弧で囲みます.
if (foo)
...
else
{
if (bar)
...
}
|
1つの宣言で, 構造体タグとその変数あるいはtypedefの両方を宣言しないでください. タグの宣言は別にし,それを使って変数あるいはtypedef の宣言をします.
if 条件中で代入をしないでください.例えば次のように書いてはいけません.
if ((foo = (char *) malloc (sizeof *foo)) == 0)
fatal ("virtual memory exhausted");
|
代わりに次のように書きましょう.
foo = (char *) malloc (sizeof *foo);
if (foo == 0)
fatal ("virtual memory exhausted");
|
lint の警告を抑制するために,プログラムを醜くしないようにしてください.
void へのキャストは入れないでください.
キャストなしのゼロは, 可変数引数の関数呼び出しに使われるとき以外は,
ヌルポインタ定数として全く正しいものです.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
大域変数や関数の名前は, 一種のコメントの働きをすると考えてください. ですから, 短すぎる名前は付けないでください. 代わりに, その 変数や関数の意味について有益な情報を得ることが出来るような名前を 選びましょう. GNU のプログラムでは, 名前は英語で意味をもつような 名前にしてください. これは, コメントを書くときと同じです.
局所変数は短くてもかまいません. なぜなら, 局所変数は一つの文脈でしか 使われず, その文脈のコメントで使用目的を説明するからです.
シンボル名で, 語の省略形を使い過ぎないようにしましょう. 省略形を数個作って, その意味を説明し, 頻繁に使うものなら構いませんが, 判りにくい省略形をたくさん使うのは止めましょう.
名前の中で語を分けるのにはアンダースコアを使ってください.
Emacs の語単位のコマンドが使えるようになります.
小文字だけを使って,大文字はマクロと enum 定数と
一定の規約にしたがった名前のプリフィックスのためにとっておきます.
例えば, iCantReadThis のような名前は使わずに
ignore_space_change_flag
のような名前を使ってください.
コマンド行オプションが指定されたかどうかを示す変数の名前は,オプション文字 を使うのではなく, オプションの意味を表す名前にすべきです. コメントはオプションの正確な意味とオプション文字の両方を明言すべきです. たとえば,次のようにします.
/* Ignore changes in horizontal whitespace (-b). */ int ignore_space_change_flag; |
整数の定数を定義したかったら `#define' より enum を使いましょう.
GDB が enum 定数を理解するからです.
ファイルを MS-DOS のファイルシステムに落した時に、MS-DOS が
ファイル名を短くすることによる、ファイル名の衝突が起きないかどうか、
あらかじめ確認したいこともあるでしょう。そのためには、
doschk というプログラムを使うことができます。
GNU のプログラムの中には、ファイル名が 14 文字以下に収まるように
制限して、古い System V のシステムに持っていった時に起こり得る
ファイル名の衝突を回避するものがあります。既存の GNU プログラムで
この機能を持っているものはそのまま保持するようにしてください。
しかし、新規の GNU プログラムではこの機能は不要です。
doschk は、14文字を越えるファイル名についても報告します。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Unix の世界では,「移植性」とは異なるバージョンの Unix に移植することを 意味します. GNU のプログラムにとっては,この種の移植性は望ましいことでは ありますが,究極のものではありません.
殆どの Unix で移植性を達成するのに一番手っ取り早い方法は Autoconf を使うことです.ホストとなるプラットフォームについて Autoconf が提供できる以上の情報が必要となる事はまずありません. そういう情報が必要になるようなプログラムは,殆どが既に書かれて しまっているからです.
半ば内部的なデータベース(例えばディレクトリ)のフォーマットを使うことは,
より高レベルのもの(ディレクトリの場合はreaddir)が使える時は,
避けてください.
Unix 以外のシステム,例えば MSDOS, Windows, Macintosh, VMS, MVS 等に ついては,サポートするのは大変な場合がほとんどです. その場合, そのような互換性のないシステムをサポートするよりは, GNU と GNU/Linux で役に立つ機能を追加するのに時間を使ったほうが良いでしょう.
読者の C のファイルをコンパイルするときに「機能テストマクロ」
_GNU_SOURCE を定義するのは良い考えです.
GNU あるいは GNU/Linux でコンパイルするときは, これにより
GNU ライブラリの拡張関数の宣言が有効になります. そして, 読者の
プログラムで同じ関数名を何か別の方法で定義すると普通はコンパイラが
エラーメッセージを出します.
(実際にはこれらの関数を使う必要はありません. 他のシステムにたいして
プログラムの移植性をさらに高めたいのであれば. )
ですが, これらの GNU の拡張を使おうと使わまいと, その名前を何か 他の意味で使うのは避けるべきでしょう. それをやってしまうと, コードを他の GNU プログラムに移すのが難しくなってしまいます.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
GNU システムは, CPUの型による差異 ---例えば,バイト順の違いや
アラインメントの要求など -- により,異なるものができるでしょう.
これらの差異を取り扱うのは極めて重要なことです.しかし, int 型が
32ビットより小さい可能性にまで対処する必要はありません.我々は
GNU では 16ビットマシンをサポートしないからです.
同様に,long が size_t のような事前定義型より
小さくなる可能性も考える必要はありません.
例えば以下のように書いてかまいません.
printf ("size = %lu\n", (unsigned long) sizeof array);
printf ("diff = %ld\n", (long) (pointer2 - pointer1));
|
標準C 1989 は,これが動作することを要求していますし,反例は ひとつしか知られていません.Microsoft Windows IA-64 上の 64ビットプログラムです.我々は,GNU プログラムをその環境に移植 しようと思う人にどうするか任せるつもりです.
off_t のようなファイルの大きさを表す事前定義型は例外です.
これらは多くのプラットフォームで long より大きくなるので,
上のようなコードは動作しません.off_t 型の値を
表示する,移植性のある方法の一つは,自分で数字をいちいち印字することです.
int型オブジェクトのアドレスが,そのオブジェクトの最下位バイトの
アドレスでもあると仮定するのは正しくありません.ビッグエンディアンの
マシンでは間違いです.つまり,以下のようなコーディングは避けなければ
なりません.
int c; ... while ((c = getchar()) != EOF) write(file_descriptor, &c, 1); |
関数を呼び出すとき,色々な型へのポインタ間の違いや,ポインタと整数型 の違いを気にする必要はありません.ほとんどのマシンでは,どちらにしても 何の違いもないからです.違いが存在する数少ないマシンについては, そういうマシンでは標準 C のプロトタイプをサポートしていますから, プロトタイプ宣言(おそらく標準 C の場合にのみ有効になるようにして)を 使ってコードを正しく動作させることができます.
ある場合には,引数として整数とポインタを区別することなく同じ関数に
渡しても大丈夫ですし,どんなシステムでもプロトタイプ宣言を使う
必要がありません.例えば,多くの GNU ソフトウェアでは,
以下のようなエラーメッセージ出力関数を使っており,渡された引数を
さらに printf 系の関数に渡しています.
error (s, a1, a2, a3)
char *s;
char *a1, *a2, *a3;
{
fprintf (stderr, "error: ");
fprintf (stderr, s, a1, a2, a3);
}
|
実際問題として,この例はあらゆるマシンで正しく動作します. というのは, ポインタは引数の種類の中で最も幅の広いものだからです. これは「正しい」とされる方法よりもずっと簡単です. こういう場合には,プロトタイプ宣言を使わないようにしましょう.
標準 C しかサポートしないと決めたなら, 代わりに `stdarg.h' を
使って error を定義し, 引数を vfprintf で渡すことができます.
ポインタを整数にキャストするのは可能な限り避けてください.
そういうキャストは移植性を非常に悪くしますし, ほとんどのプログラムで簡単に
避けられるはずです.
ポインタを整数にキャストするのが本質的な場合
---例えば,Lisp インタプリタは,型情報をアドレスと同じく一語に
格納します---にそうするのは良いのですが,その場合には,
異なるサイズの語を扱うための仕組みを自分で明示的に用意する必要があります.
また, malloc が返すアドレスの通常の範囲が, 0 から遠く離れた
ところから始まるシステム用に準備が必要になります.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
C 言語は実装により,大いに異なっています. 標準 C によりその差異は 少なくなっていますが, 非互換性が完全に無くなったわけではありません.一方, 多くのGNU のパッケージは依然として標準以前のコンパイラを サポートしています. そんなに難しいことではないからです. この章では,不必要に移植性を失うことを避けるための 標準 C ライブラリ関数の使い方をお勧めします.
sprintf の戻り値は使わないでください.書き込まれた文字数を
返すシステムもありますが,全部のシステムがそうとは限らないので.
vfprintf は常に利用可能というわけではないことに注意してください.
main の戻り値型は int 型として宣言しなければなりません.
main 関数は, exit の呼び出しか, 整数の終了コードの
返却を以て終了する必要があります.
不定値を返さないようにしなければなりません.
あるシステムでは,システム関数についてのどんな宣言も間違いの場合があります. 矛盾を最小にするために,システム関数の宣言はシステムのヘッダファイルに まかせてください.ヘッダファイルの中に宣言がない関数については, 宣言無しのままにしておいてください.
関数を宣言無しで使うのは気持ち悪いかもしれませんが,実際にはそういう問題が 起きるシステムではシステムのライブラリ関数の大部分はそれで動作するものです. つまり, 欠点は理論の上だけのことです.一方,宣言を行うとしばしば 実際に衝突を引き起こします.
malloc や realloc を宣言するのはやめてください.
大部分の GNU プログラムでは,これらの関数は一度だけ, xmalloc と
xrealloc という名前の関数で使うようになっています.
これらの関数は,それぞれ, malloc と realloc を呼び出して,
結果のチェックを行っています.
xmalloc と xrealloc は,自分のプログラムの中で
定義することになるので,衝突の恐れなく他のファイルで宣言を行う
ことができます.
ほとんどのシステムでは, int はポインタと同じ大きさです.
つまり, malloc と realloc は宣言無しでも正しく
動作します. int とポインタの大きさが同じでない
例外的なシステム(大部分は 64 ビットマシン)のためには,
#ifdef 等でかこって malloc と realloc を宣言するか,
宣言をそういうシステムに固有のコンフィギュレーションファイルに
書くことにすれば良いでしょう.
これは思ったほど問題になりません. 標準 C の新しい文字列関数は まだ多くのシステムでサポートされていないので, 使うのは避けるべきです. 問題なく使用できる文字列関数は以下の通りです.
strcpy strncpy strcat strncat strlen strcmp strncmp strchr strrchr |
コピーおよび連結系の関数は,その戻り値を使わない限り,宣言無しで
正しく動作します.宣言無しで戻り値を使うと,ポインタと int
の幅が違うシステムで,あるいはもしかすると何か他の原因で,問題が
起きるでしょう.戻り値を使わないようにするのが簡単なので,そう
しましょう.
比較系の関数と strlen はほとんどのシステムで,恐らく
GNU ソフトウェアが動作する全てのシステムで,宣言無しで正しく動作します.
2,3のシステムで条件付きで宣言する必要があるかもしれません.
探索系の関数は, char * を返すように宣言する必要があります.
幸運にも,これらの関数が返すデータ型に違うものはありません.
しかし,関数名は異なるものがあります. index と rindex という
名前を使っているシステムと, strchr と strrchr という
名前を使っているシステムがあります.両方の組をサポートしている
システムもありますが,どちらかの組が全てのシステムで動作するということは
ありません.
名前の組のどちらか片方を選んで,選んだらプログラム全体でそれで通すべきです.
(最近は, 新しいプログラムを書くときは strchr と strrchr を
選んだほうが良いでしょう. 標準で規定されている名前なので.)
選んだ組の名前を両方とも char * を返す関数として宣言します.
選んだほうの組をサポートしていないシステムでは,もう一方の組を使って
マクロとして定義してください.例えば, strchr と strrchr を
使うことに決めた場合には,ファイルの先頭(あるいは一つのヘッダファイル)に
以下のように書いてください.
#ifndef HAVE_STRCHR #define strchr index #endif #ifndef HAVE_STRRCHR #define strrchr rindex #endif char *strchr (); char *strrchr (); |
ここで, HAVE_STRCHR と HAVE_STRRCHR は,
対応する関数が存在するシステムでマクロとして定義されているものとします.
これらのマクロが正しく定義されるようにする方法の一つは
Autoconf を使うことです.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
GNU には, GNU gettext と呼ばれるライブラリ関数があり,プログラム中の メッセージを色々な言語に翻訳するのを手助けしてくれます. このライブラリ関数をあらゆるプログラムで使うようにすべきです. プログラム中のメッセージは英語で書き, gettext を使って他の言語へ 翻訳できるようにしましょう.
GNU gettext の使い方は以下のように,翻訳が必要な各文字列を
マクロ gettext で囲みます.
printf (gettext ("Processing file `%s'..."));
|
こうしておくと, GNU gettext が文字列 "Processing file
`%s'..." を翻訳したものと置き換えてくれます.
プログラムで一旦 gettext を使い始めたら,翻訳が必要な文字列を追加
した場合, gettext の呼出しを入れるのを忘れないでください.
あるパッケージで GNU gettext を使うには,テキストドメイン名 というものをそのパッケージに対して指定する必要があります. テキストドメイン名は,そのパッケージの翻訳を他のパッケージの翻訳と 区別するのに使われます.普通は,テキストドメイン名はパッケージ名と 同じにすべきです.例えば, GNU ファイルユーティリティなら `fileutils' とします.
gettext をうまく動作させるためには,単語や文の構造について何らかの仮定を 設けたコードを書くのは避ける必要があります. あるセンテンスの一部をデータに依存して変更したいときには, そのセンテンス全体を含む文字列定数を選択肢の数だけ用意しましょう. センテンスの中の一部分だけを条件で切り分けるのは止めましょう.
以下にやってはいけない例を示します.
printf ("%d file%s processed", nfiles,
nfiles != 1 ? "s" : "");
|
この例の問題は,複数形は `s' を付けることで示されると仮定していることです. この文字列にたいして以下のように gettext を適用したとします.
printf (gettext ("%d file%s processed"), nfiles,
nfiles != 1 ? "s" : "");
|
メッセージはこれとは違った文を使う事もできるはずなのに,複数形を 示すのに 's' を使うことが強制されます. 元のプログラムを以下のように書くのが良いでしょう.
printf ((nfiles != 1 ? "%d files processed"
: "%d file processed"),
nfiles);
|
こうしておけば,二つの文字列にそれぞれ別に gettext を適用することが できます.
printf ((nfiles != 1 ? gettext ("%d files processed")
: gettext ("%d file processed")),
nfiles);
|
これだと, "file" という言葉の複数形を示す方法がどんなものでも良く, また, どんな言語でも扱うことができます.
以下のコード例では, 同様の問題がセンテンス構造レベルでも起きます.
printf ("# Implicit rule search has%s been done.\n",
f->tried_implicit ? "" : " not");
|
このコードに gettext への呼びだしを加えた場合, 全ての言語に
対して正しい結果を与えることはできません. 何故なら, 言語によっては
否定を表すためにはセンテンスの複数の箇所に語を追加する必要があるからです.
これに較べて, 元のコードを以下のようにすれば gettext の呼出しの
追加は単純明解になります.
printf (f->tried_implicit
? "# Implicit rule search has been done.\n",
: "# Implicit rule search has not been done.\n");
|
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
mmap について, 全てのファイルに対して
正しく動作するか, あるいは全然動作しないかのどちらかである,
という考えを持っていたら改めてください. mmap は,
あるファイルについては動作するが, 別のファイルでは動作しないことも
あるのです.
mmap の正しい使い方というのは, 使いたいと思うある特定のファイルに
対して mmap を試してみて, 駄目だったら, read や write
を使った別の方法で代わりに行う, というものです.
何故このような予防策が必要かと言うと, GNU カーネル(HURD) では,
ユーザが拡張可能なファイルシステムを提供しており, そういう拡張
されたファイルシステムには色々な種類の「通常ファイル」が存在し得るからです.
多くの場合は, mmap をサポートしていますが, 中にはサポート
していないものもあります. これらのあらゆる種類のファイルを扱えるように
プログラムを作るのが重要なことです.
| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |