STYLE

Section: Misc. Reference Manual Pages (9)
索引 jman

BSD mandoc
 

索引

名称

style - カーネルソースファイルのスタイルガイド  

索引

解説

このファイルは Fx ソースツリーのカーネルソースに好ましいスタイルを明記しています。 これはユーザランドのコードスタイルの手引きでもあります。 例において、スタイル規則の多くを暗黙的に使用しています。 がこれらの事例について言及していないと決め付ける前に、 注意して例を確認してください。 はそのような事柄については記述していません。
/*
 * FreeBSD のためのスタイルガイドです。
 * CSRG の KNF (Kernel Normal Form, カーネル標準書式) に基づいています。
 *
 *      @(#)style       1.14 (Berkeley) 4/28/95
 * $FreeBSD: src/share/man/man9/style.9,v 1.110.2.1 2005/03/01 12:44:49 brueffer Exp $
 */

/*
 * とても重要な 1 行のコメントはこのようにします。
 */

/* 殆どの 1 行のコメントはこのようにします。 */

/*
 * 複数行にわたるコメントはこのようにします。実際の文章を書きます。実際の
 * 段落に見えるように埋めていきます。
 */

著作権のヘッダは複数行にわたるコメントであるべきで、 コメントの最初の行は次のように星 (アスタリスク) の後ろにダッシュ (-) を 付けます:

/*-
 * Copyright (c) 1984-2025 John Q. Public.  All Rights Reserved.
 *
 * 長くて、退屈なライセンスはここに記述しますが、簡潔にするために
 * 編集しています
 */

自動スクリプトは最初のカラムが ``/*- '' で始まるすべてのコメントを対照としてツリーからライセンス情報を集めます。 利用者がライセンスまたは著作権表示でもない最初のカラムで始まるコメントを 最初のカラムから始まるコメントでライセンスや著作権表示でないものを indent(1) に再整形させたくない場合は、 それらのコメントのダッシュを星に変えてください。 最初のコメント以外のカラムで始まるコメントは 決してライセンス声明とはみなしません。 訳注: 行頭にないものはライセンス表明とみなしません。

著作権ヘッダの後には空行を 1 行入れ、ソースファイルには rcsid を付けます。 バージョン管理システムの 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: src/share/man/man9/style.9,v 1.110.2.1 2005/03/01 12:44:49 brueffer Exp $");

ヘッダファイルの前に、空行を 1 行付けます。

カーネルのインクルードファイル (すなわち、 sys/*.h が初めに来ます。 通常、 In sys/types.h または In sys/param.h のどちらかが必要ですが、 両方は必要ないでしょう。 In sys/types.h は In 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>

グローバルなパス名は In paths.h で定義されています。 プログラムにローカルなパス名はローカルディレクトリの Qq Pa 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 */

このプロジェクトは、 Vt u_intXX_t 形式の古い BSD スタイルの整数識別子よりもむしろ、 Vt uintXX_t 形式の St -isoC-99 の符号なし整数識別子を使用するように、徐々に移行しています。 新しいコードは後者を使用するべきで、さらにその領域の他の主要な作業が完了し、 古い BSD スタイルを好むための優先する理由がない場合には、古いコードは 新しい形式に変換されるべきです。 空白文字のコミットと同様に、 Vt uintXX_t のみのコミットをするよう、考慮すべきです。

列挙値は全て大文字を使用します。

enum enumtype { ONE, TWO } et;

宣言の中では、型に関係付けられたトークンを除いて、 アスタリスクと隣接したトークンの間には空白文字を置きません。 (これらの識別子は基本の型の名前、型の修飾語句、および今宣言されようとしている もの以外の 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 で宣言されるべきです。

カーネルの別の部分から使用される関数は、 関連のあるインクルードファイルの中でプロトタイプされます。 関数プロトタイプは、異なる順序の使用を強制する理由がない場合には、 なるべくアルファベット順の論理的な順序で整列されるべきです。

複数のモジュールでローカルに使用される関数は、 Qq Pa extern.h 等の分離したヘッダファイルの中に置かれます。

__P マクロは使用しません。

ファイルの 50% かそれ以上を巻き込んだ修正の場合は、 一般にコードは ``新しいコード'' とみなすことができます。 これは既存のコードの慣例を破り、 現在の ガイドラインを使用するのに十分です。

カーネルはパラメータの型に関連付けられた名前を持ちます。 例えば、カーネル内で次のように使用します:

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;
                        /* 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();
                        /* NOTREACHED */
                }
        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 で、または sysexits(3) にあらかじめ定義してある値で exit するべきです。

        exit(EX_OK);    /*
                         * "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 は常に括弧をつけて書かれます。 冗長な括弧の規則は Fn sizeof var の事例には適用されません。

NULL は、好まれるヌルポインタ定数です。 コンパイラが型を知っている文脈、例えば代入では、 Vt ( type * ) Ns 0 または Vt ( type * ) Ns Dv NULL の代わりに、 NULL を使用します。 他の文脈では、特に全ての関数の引数では、 Vt ( type * ) Ns Dv NULL を使用します。 (関数のプロトタイプがスコープ外かもしれない場合に、 キャストはいろいろな引数にとって必須で、その他の引数にとっても必要です。) ポインタは NULL と比較します。 例えば、次のように使います:

(p = f()) == NULL

次のようには使いません:

!(p = f())

真理値 (ブール値) ではない場合、テストには ! を使用しないでください。 例えば、次のように使います:

if (*p == '\0')

次のようには使いません:

if (!*p)

Vt 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) の文はマニュアルページの Sx SYNOPSIS (書式) の様であるべきです。 使用法の文は、次の構造であるべきです:

  1. オペランドの無いオプションが、最初にアルファベット順に、 1 組の大括弧 `(' [ と `]' ) でくくられます。
  2. オプションとそのオペランドがこれもアルファベット順に続き、 それぞれのオプションとその引数を 1 組の大括弧でくくります。
  3. 必須の引数 (もしあれば) が続き、 コマンドラインで指定されるべき順で一覧されます。
  4. 最後に、 すべての任意の引数が指定されるべき順で、 すべて大括弧の中に一覧されます。

縦棒 (`|' ) は、 ``二者択一'' のオプションまたは引数を分割し、 同時に使用するオプションと引数は、単一の大括弧でくくります。

"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(EX_USAGE);
}

マニュアルページのオプション記述は、 純粋なアルファベット順であるべきであることに注意してください。 つまり、オプションが引数を取るか否かに関わらないということです。 アルファベット順は、前述の大文字小文字の順序を考慮に入れるべきです。

新しい中心的なカーネルのコードは、適度に ガイドに従うべきです。 サードパーティが保守するモジュールやデバイスドライバのためのガイドラインは より緩やかですが、最低限内部的には彼らの一貫したスタイルであるべきです。

ソースリポジトリの文体の変更 (空白文字の変更を含む) は困難で、 正当な理由なしには避けるべきです。 リポジトリの中のおおよそ Fx KNF に適合しているコードは、この適合から離れてはなりません。

可能な時にはいつでも、 コードはコードチェッカ (例えば、 lint(1) または gcc -Wall を 通過し、発生する警告は最小限となるべきです。  

索引

関連項目

indent(1), lint(1), err(3), sysexits(3), warn(3), style.Makefile5  

索引

歴史

このページは BSD 4.4 Lite2 リリースの src/admin/style/style ファイルに大きく基づいていて、 現在の実装と Fx プロジェクトの要望を反映して、頻繁に更新しています。


 

索引

Index

名称
解説
関連項目
歴史

jman



Time: 07:07:59 GMT, January 12, 2009