STYLE(9) | FreeBSD Kernel Developer's Manual | STYLE(9) |
名称
style — カーネルソースファイルのスタイルガイド解説
このファイルは、 FreeBSD ソースツリーのカーネルソースに好ましいスタイルを明記しています。これは、ユーザランドのコードスタイルの手引きでもあります。例において、スタイル規則の多くを暗黙的に使用しています。 style がこれらの事例について言及していないと決め付ける前に、注意して例を確認してください。 style は、そのような事柄については、記述していません。
/* * FreeBSD のためのスタイルガイド. CSRG の KNF (Kernel Normal Form) * に基づいている. * * @(#)style 1.14 (Berkeley) 4/28/95 * $FreeBSD: release/10.0.0/share/man/man9/style.9 217087 2011-01-07 08:34:12Z trasz $ */ /* * 非常に重要な 1 行コメントは, このように見えます. */ /* ほとんどの単一行コメントは, このように見えます. */ /* * 複数行のコメントは, このように見えます. それらを実際のセンテンスに * します. それらが実際のパラグラフのように見えるように, それらを満た * します. */
著作権のヘッダは、複数行にわたるコメントであるべきで、コメントの最初の行は、次のように星 (アスタリスク) の後ろにダッシュ (-) を付けます:
/*- * Copyright (c) 1984-2025 John Q. Public * All rights reserved. * * 長くて, 退屈なライセンスは, ここですが, 簡潔さのために切り詰めました. */
自動スクリプトは、最初のカラムが“ /*-
”で始まるすべてのコメントを対照としてツリーからライセンス情報を集めます。利用者がライセンスまたは著作権表示でもない最初のカラムで始まるコメントを最初のカラムから始まるコメントでライセンスや著作権表示でないものを indent(1) に再整形させたくない場合は、それらのコメントのダッシュを星に変えてください。最初以外のカラムで始まるコメントは、決してライセンス声明とはみなしません。訳注: 行頭にないものは、ライセンス声明とみなしません。
著作権ヘッダの後には、空行を 1 行入れ、C/C++ 言語でないソースファイルには、 $FreeBSD$
を付けます。バージョン管理システムの ID タグは、ファイル中に 1 個のみあるべきです (このファイルでは違いますが)。 C/C++ ソースファイル以外は、この例に従いますが、 C/C++ ソースファイルは、以降の例に従います。外部から入手したファイルのすべての VCS (バージョン管理システム) リビジョン識別子は、存在すれば維持します。これには、ファイルの来歴を示す複数の ID も含みます。一般的に、外来の ID またはその下部構造は、編集しません。さもなければ (“ #if defined(LIBC_SCCS)
”のように) 囲まれていない場合には、全ての互換性のない小片を隠すため、およびその ID をオブジェクトファイルから追い出しておくために、両方を“ #if 0 ... #endif
”の中に囲みます。ファイルの名前が変更された場合には、外来の VCS ID の前に“ From:
”のみを追加します。
#if 0 #ifndef lint static char sccsid[] = "@(#)style 1.14 (Berkeley) 4/28/95"; #endif /* not lint */ #endif #include <sys/cdefs.h> __FBSDID("$FreeBSD: release/10.0.0/share/man/man9/style.9 217087 2011-01-07 08:34:12Z trasz $");
ヘッダファイルの前に、空行を 1 行付けます。
カーネルのインクルードファイル (すなわち、 sys/*.h) が初めに来ます。通常、 < sys/types.h> または < sys/param.h> のどちらかが必要ですが、両方は、必要ないでしょう。 < sys/types.h> は、 < sys/cdefs.h> をインクルードしており、依存関係は、問題ありません。
#include <sys/types.h> /* 山括弧中の非ローカルなインクルード. */
ネットワークプログラムである場合は、次にネットワークインクルードファイルを置きます。
#include <net/if.h> #include <net/if_dl.h> #include <net/route.h> #include <netinet/in.h> #include <protocols/rwhod.h>
カーネル用のファイルには、 /usr/include 中のファイルを使用しないでください。
それから空行を置き、 /usr/include のファイルを続けます。 /usr/include のファイルは、アルファベット順にソートされているべきです。
#include <stdio.h>
グローバルなパス名は、 < paths.h> で定義されています。プログラムにローカルなパス名は、ローカルディレクトリの“ pathnames.h”に入れます。
#include <paths.h>
それから、空行があって、ユーザインクルードファイルが来ます。
#include "pathnames.h" /* 二重引用符中のローカルなインクルード. */
アプリケーションインタフェースを実装している場合を除き、実装の名前空間で #define したり名前を定義したりしてはいけません。
“安全でない”マクロ (副作用を持っているもの) の名前と、明らかな定数のマクロの名前は、すべて大文字です。式のように展開されるマクロは、単一のトークンにするか外側に括弧をつけます。 #define とマクロ名の間にタブ文字を 1 個入れます。マクロがある関数のインライン展開である場合は、関数名は、全て小文字で、マクロは、すべて大文字の同じ名前を持ちます。バックスラッシュは、右揃えします。こうすると読みやすくなります。マクロが複合文をカプセル化する場合には、それを do ループで囲みます。これにより、 if 文で安全に使用できます。最後の文の終端のセミコロンは、マクロではなくマクロの実施時に付けられるべきです。これにより、清書器やエディタで文法解析しやすくなります。
#define MACRO(x, y) do { \ variable = (x) + (y); \ (y) += 2; \ } while (0)
コードが #ifdef または #if を使用して条件付きでコンパイルされるときには、どこで条件付きでコンパイルされるコードが終了するのかを読む人が容易に識別することが可能にするために、それに続く適合する #endif または #else にコメントを追加しても構いません。このコメントは、(主観的に) 長い部分、20 行以上の部分、またはネストされた #ifdef の連続が読む人を混乱させるかもしれないとき、にのみ使用されるべきです。たとえコンパイルされない領域が小さくなるかもしれないでも、 lint(1) の目的のために条件付きでコンパイルされない個所のために、例外が作られても構いません。そのコメントは、 #endif または #else から 1 つの空白によって分離されるべきです。短い条件付きでコンパイルされる部分のために、終わりのコメントを使用するべきではありません。
#endif のためのコメントは、対応する #if または #ifdef で使用されている表現に合わせるべきです。 #else および #elif のためのコメントは、先行する #if および/または #elif 文に使用されている表現の反対に合わせるべきです。コメントの中では、補助表現“ defined(FOO)
”は、“ FOO
”と省略されます。コメントの目的のためには、“ #ifndef FOO
”は、“ #if !defined(FOO)
”とみなされます。
#ifdef KTRACE #include <sys/ktrace.h> #endif #ifdef COMPAT_43 /* 大きな範囲はここ, または他の条件付きのコード. */ #else /* !COMPAT_43 */ /* またはここ. */ #endif /* COMPAT_43 */ #ifndef COMPAT_43 /* もう一つの大きな範囲はここ, または他の条件付きのコード. */ #else /* COMPAT_43 */ /* またはここ. */ #endif /* !COMPAT_43 */
このプロジェクトは、 u_intXX_t 形式の古い BSD スタイルの整数識別子よりもむしろ、 uintXX_t 形式の ISO/IEC 9899:1999 (“ISO C99”) の符号なし整数識別子を使用するように、徐々に移行しています。新しいコードは、後者を使用するべきで、さらにその領域の他の主要な作業が完了し、古い BSD スタイルを好むための優先する理由がない場合には、古いコードは、新しい形式に変換されるべきです。空白文字のコミットと同様に、 uintXX_t のみのコミットをするよう、考慮すべきです。
列挙値は、全て大文字を使用します。
enum enumtype { ONE, TWO } et;
識別子の internal_underscores の使用は、camelCase または TitleCase より優先されます。
宣言の中では、型に関係付けられたトークンを除いて、アスタリスクと隣接したトークンの間には、空白文字を置きません。 (これらの識別子は、基本の型の名前、型の修飾語句、および今宣言されようとしているもの以外の typedef 名です。) これらの識別子は、アスタリスクから 1 つの空白で分離します。
構造体の中で変数を宣言する時には、使用順、サイズ順 (大きいものから小さなものへ)、アルファベット順にソートして宣言します。最初の区分は、通常適用しませんが、例外があります。各宣言は、それぞれ独立した行にて行います。構造体の名前の位置を、あなたの判断で読み易いように、タブ 1 個または 2 個を使用して揃えてください。少なくとも 90% のメンバの名前を揃えるのに十分な場合には、 1 つだけのタブを使用するべきです。非常に長い型の後の名前は、単一の空白で区切られるべきです。
重要な構造体は、それが使用されるファイルの先頭で宣言されるか、複数のソースファイルで使用される場合は、別のヘッダファイルで宣言されるべきです。構造体がヘッダファイルで宣言されている場合には、それら構造体の使用は、宣言とは、分けられるべきで、かつ extern であるべきです。
struct foo { struct foo *next; /* アクティブな foo のリスト. */ struct mumble amumble; /* mumble のコメント. */ int bar; /* コメントを整列しようとする. */ struct verylongtypename *baz; /* 2 つのタブに, 合わない. */ }; struct foo *foohead; /* グローバルな foo リストの先頭. */
可能な時には、必ず、あなた自身でリストを操作するのではなく、 queue(3) マクロを使用してください。従って、前の例をより良く書くと次のようになります。
#include <sys/queue.h> struct foo { LIST_ENTRY(foo) link; /* foo リストのキューマクロを 使用する. */ struct mumble amumble; /* mumble のコメント. */ int bar; /* コメントを揃えます. */ struct verylongtypename *baz; /* 2 つのタブに, 合わない. */ }; LIST_HEAD(, foo) foohead; /* グローバルな foo リストの先頭. */
構造体の型に typedef を使用する事は、避けてください。 typedef は、その下位にある型を適切に隠さないので、問題となり得ます。例えば、typedef が構造体そのものであるのか、構造体へのポインタであるのか、あなたが知る必要があります。更に、typedef は、正確に 1 度だけ宣言しなければなりませんが、不完全な構造体型は、必要な回数だけ宣言可能です。 typedef は、スタンドアロンなヘッダファイル中で使用することが困難です。 typedef を定義するヘッダは、この typedef を使用するヘッダの前にインクルードするか、この typedef を使用するヘッダによってインクルードする必要があります (これは、名前空間の汚染となります)。さもなければ、typedef を得るための裏口が必要となってしまいます。
規約が typedef を要求する場合には、その名前を構造体タグに一致させます。標準 C または、 POSIX によって明示されたものを除いては、“ _t
”で終る typedef を避けてください。
/* 構造体名と typedef を一致させる. */ typedef struct bar { int level; } BAR; typedef int foo; /* これは, foo です. */ typedef const long baz; /* これは, baz です. */
全ての関数は、どこかでプロトタイプされます。
私的な関数 (すなわち、他のどこでも使用されない関数など) の関数プロトタイプは、最初のソースモジュールの先頭に置かれます。単一のソースモジュールにローカルな関数は、 static で宣言されるべきです。
カーネルの別の部分から使用される関数は、関連のあるインクルードファイルの中でプロトタイプされます。関数プロトタイプは、異なる順序の使用を強制する理由がない場合には、なるべくアルファベット順の論理的な順序で整列されるべきです。
複数のモジュールでローカルに使用される関数は、“ extern.h”等の分離したヘッダファイルの中に置かれます。
__P マクロは、使用しません。
ファイルの 50% かそれ以上を巻き込んだ修正の場合は、一般にコードは、“新しいコード”とみなすことができます。これは、既存のコードの慣例を破り、現在の style ガイドラインを使用するのに十分です。
カーネルは、パラメータの型に関連付けられた名前を持ちます。例えば、カーネル内で次のように使用します:
void function(int fd);
ユーザランドのアプリケーションに対して見えるヘッダファイルの中では、可視のプロトタイプは、型を伴った“保護された” (アンダスコアで開始する) 名前を使用するか、型だけで名前を使用しないかのどちらかが必要です。保護された名前の使用がより望ましいです。例えば、次のように使用します:
void function(int);
または
void function(int _fd);
プロトタイプは、関数名の行揃えを行なうために、次のようにタブの後に追加の空白文字を置いても構いません:
static char *function(int _arg, const char *_arg2, struct foo *_arg3, struct bar *_arg4); static void usage(void); /* * すべての主なルーチンには, それらが何を行うか簡潔に記述するコメントが * あるべきです. "main"ルーチンの前のコメントは, プログラムが何を行うか * 記述するべきです. */ int main(int argc, char *argv[]) { char *ep; long num; int ch;
一貫性のために、オプションの解析には、 getopt(3) が使用されるべきです。 getopt(3) 呼び出しと switch 文では、オプションをソートすべきですが、 switch 文のカスケードの一部の場合は、例外です。 switch 文のカスケード要素は、 FALLTHROUGH
コメントを持つべきです。数値の引数は、精度をチェックされるべきです。明白でない理由によって到達できないコードは、/* NOTREACHED
*/ でマークされます。
while ((ch = getopt(argc, argv, "abNn:")) != -1) switch (ch) { /* switch をインデント. */ case 'a': /* case をインデントしない. */ aflag = 1; /* case の本体を 1 つのタブで インデント. */ /* FALLTHROUGH */ case 'b': bflag = 1; break; case 'N': Nflag = 1; break; case 'n': num = strtol(optarg, &ep, 10); if (num <= 0 || *ep != '\0') { warnx("illegal number, -n argument -- %s", optarg); usage(); } break; case '?': default: usage(); } argc -= optind; argv += optind;
予約語 ( if, while, for, return, switch) の後に空白を入れます。何も伴わないか、ただ 1 つの文を伴う制御文は、ブレース (‘ {
’と‘ }
’) を使用しません。 1 つの文が、複数行である文の場合には、これは、許されます。無限ループは、 while ではなく for で行ないます。
for (p = buf; *p != '\0'; ++p) ; /* 何もしない */ for (;;) stmt; for (;;) { z = a + really + long + statement + that + needs + two + lines + gets + indented + four + spaces + on + the + second + and + subsequent + lines; } for (;;) { if (cond) stmt; } if (val != NULL) val = realloc(val, newsize);
for ループの各部は、空のまま残しても構いません。異常に複雑なルーチンでない限りは、ブロックの中に宣言を置いてはなりません。
for (; cnt < 15; cnt++) { stmt1; stmt2; }
インデントは、8 文字のタブです。第 2 レベルのインデントは、4 文字の空白です。長い分を折り返す必要がある場合、オペレータを行末に置きます。
while (cnt < 20 && this_variable_name_is_too_long && ep != NULL) z = a + really + long + statement + that + needs + two + lines + gets + indented + four + spaces + on + the + second + and + subsequent + lines;
空白文字を行末に追加してはいけません。また、インデントを形成するためには、タブとその後に空白のみを使用します。タブが生み出す以上の空白や、タブの前の空白は、使用しません。
ブレースの終了と開始は、 else と同じ行に置かれます。必要でないブレースは、省いても構いません。
if (test) stmt; else if (bar) { stmt; stmt; } else stmt;
関数名の後は、空白を空けません。コンマの後には、空白があります。‘ (
’または‘ [
’の後ろまたは‘ ]
’または‘ )
’の前には、空白を空けません。
error = function(a1, a2); if (error != 0) exit(error);
単項演算子は、空白を要求しませんが、二項演算子は、要求します。優先順位が要求する場合または文が括弧なしでは、混乱する場合以外は、括弧は、使用しません。他人は、あなたよりも混乱しやすいかもしれないということを覚えておいてください。あなたは、以下を理解できますか?
a = b->c[0] + ~d == (e || f) || g && h ? i : j >> 1; k = !(l & FLAGS);
成功すれば 0 で、または失敗すれば、1 で exit するべきです。
exit(0); /* * "Exit 0 on success." (成功すれば, 0 で終了) * のような分かりきったコメントを避ける */ }
関数の型は、関数自身に先行する行にあるべきです。関数の本体の開始のブレースは、単独で 1 行であるべきです。
static char * function(int a1, int a2, float fl, int a4) {
関数の中で変数を宣言する時には、サイズ順に、次にアルファベット順にソートして宣言します。 1 行に複数の宣言は、可能です。行が溢れる場合は、型の予約語を再度使用します。
宣言時に変数を初期化することによってコードを不明瞭にしない様に注意してください。この機能は、良く考えて使用してください。初期化に関数呼び出しを使用しないでください。
struct foo one, *two; double three; int *four, five; char *six, seven, eight, nine, ten, eleven, twelve; four = myfunction();
他の関数の内部で関数を宣言しないでください。 ANSI C によると、このような宣言は、宣言のネスティングによらず、ファイルスコープになります。ローカルスコープに見えるものの中にファイルの宣言を隠すことは好ましくなく、良いコンパイラは、苦情を言います。
キャストと sizeof 演算子の後には、空白を続けません。この規則は、 indent(1) が理解しないことに注意してください。 sizeof は、常に括弧をつけて書かれます。冗長な括弧の規則は、 sizeof( var) の事例には、適用されません。
NULL は、好まれるヌルポインタ定数です。コンパイラが型を知っている文脈、例えば代入では、 ( type *)0 または ( type *) NULL の代わりに、 NULL を使用します。他の文脈では、特に全ての関数の引数では、 ( type *) NULL を使用します。 (関数のプロトタイプがスコープ外かもしれない場合に、キャストは、いろいろな引数にとって必須で、その他の引数にとっても必要です。) ポインタは、 NULL と比較します。例えば、次のように使います:
(p = f()) == NULL
次のようには、使いません:
!(p = f())
真理値 (ブール値) ではない場合、テストには、 ! を使用しないでください。例えば、次のように使います:
if (*p == '\0')
次のようには、使いません:
if (!*p)
void * を返すルーチンでは、戻り値をどのポインタ型にもキャストしてはなりません。
return 文の値は、括弧で囲まれているべきです。
err(3) または warn(3) を使用し、勝手に作らないでください。
if ((four = malloc(sizeof(struct foo))) == NULL) err(1, (char *)NULL); if ((six = (int *)overflow()) == NULL) errx(1, "number overflowed"); return (eight); }
古いスタイルの関数宣言は、このようになっています:
static char * function(a1, a2, fl, a4) int a1, a2; /* int の宣言, それらをデフォルトにしない. */ float fl; /* double と float のプロトタイプ違いに注意. */ int a4; /* 宣言された順番でリスト. */ {
あなたが明確に K&R との互換性を必要とする場合以外は、 ANSI の関数宣言を使用してください。長いパラメータリストの折り返しには、 4 個の空白による通常のインデントを付けます。
可変個数の引数は、このようにします:
#include <stdarg.h> void vaf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); STUFF; va_end(ap); /* void の関数に return は不要. */ } static void usage() { /* 関数にローカル変数がないなら, 空行を挿入. */
fputs(3), puts(3), putchar(3) 等ではなく、 printf(3) を使用してください。これは、速くて大抵は、きれいで、言うまでもなくつまらないバグを避けます。
使用法 (usage) の文は、マニュアルページの SYNOPSIS (書式) の様であるべきです。使用法の文は、次の構造であるべきです:
- オペランドの無いオプションが、最初にアルファベット順に、 1 組の大括弧 (‘
[
’と‘]
’) でくくられます。 - オプションとそのオペランドがこれもアルファベット順に続き、それぞれのオプションとその引数を 1 組の大括弧でくくります。
- 必須の引数 (もしあれば) が続き、コマンドラインで指定されるべき順で一覧されます。
- 最後に、すべての任意の引数が指定されるべき順で、すべて大括弧の中に一覧されます。
縦棒 (‘ |
’) は、“二者択一”のオプションまたは引数を分割し、同時に使用するオプションと引数は、単一の大括弧でくくります。
"usage: f [-aDde] [-b b_arg] [-m m_arg] req1 req2 [opt1 [opt2]]\n" "usage: f [-a | -b] [-c [-dEe] [-n number]]\n"
(void)fprintf(stderr, "usage: f [-ab]\n"); exit(1); }
マニュアルページのオプション記述は、純粋なアルファベット順であるべきであることに注意してください。つまり、オプションが引数を取るか否かに関わらないということです。アルファベット順は、前述の大文字小文字の順序を考慮に入れるべきです。
新しい中心的なカーネルのコードは、適度に style ガイドに従うべきです。サードパーティが保守するモジュールやデバイスドライバのためのガイドラインは、より緩やかですが、最低限内部的には、彼らの一貫したスタイルであるべきです。
ソースリポジトリの文体の変更 (空白文字の変更を含む) は、困難で、正当な理由なしには、避けるべきです。リポジトリの中のおおよそ FreeBSD KNF style に適合しているコードは、この適合から離れてはなりません。
可能な時には、いつでも、コードは、コードチェッカ (例えば、 lint(1) または gcc -Wall) を通過し、発生する警告は、最小限となるべきです。
歴史
このマニュアルページは、 FreeBSD プロジェクトの現在の慣習と要望を反映するための時折の更新を伴って、 4.4BSD-Lite2 リリースの src/admin/style/style ファイルに大部分基づいています。 src/admin/style/style は、 Version 6 AT&T UNIX の Ken Thompson と Dennis Ritchie のプログラミングスタイルを CSRG によって成文化したものです。January 7, 2010 | FreeBSD |