コーディングスタイルについて コーディングスタイルとはソースコードのレイアウトのことです 空 白 やカッコをどこにいくつ 入 れるか どこで 改 行 するか コメントはどこにつけるか といったこともろもろを 全 部 まとめてコーディングスタイルと 言 います Cはフリーフォーマットな 言 語 です Web で The International Obfuscated C Code Contest( 難 解 C コード 国 際 コンテスト)の 入 賞 コードを 見 つけて 参 照 してみてください Cのコーディングスタイルにはこれほどの 自 由 度 があります しかしこのようにわかりにくいコードを 書 いてバグが 出 たときに 果 たしてデバッグは 可 能 でしょうか( 入 賞 者 たちはきっとデバッグに 非 常 な 苦 労 をしたでしょう) また ちょ っとした 機 能 を 追 加 するとき ためらいなく 手 をつける 気 になれますか? 仕 事 では わかりやすく 保 守 が 容 易 で 一 貫 しているコーディングスタイルが 求 めら れます 実 際 にはプロジェクトごとにコーディングスタイルを 決 めることも 多 いようです 以 下 にあるのはコーディングスタイルガイドの 一 例 です 研 修 ではここに 書 いてあるコ ーディングスタイルに 従 うものとします 最 初 は 面 倒 かもしれませんが これもマナーの 1つと 考 えてください 1/13
1. インデントとタブ インデント( 行 ごとの 字 下 げ 段 差 )は4 桁 とします if (a > 1) { x = a + 1; for (i = 0; i < n; i++) { array[i] = func(x, i); if (array[i] < 0) { fprintf(stderr, ERROR: Invalid value %d\n, else { foo(array[i]); : インデントにはタブが 利 用 できますが タブ 幅 の 設 定 は 人 によって 異 なり 4 桁 に 設 定 している 人 と8 桁 の 人 とがいます タブ 幅 設 定 が 異 なると 自 分 のエディタでは 問 題 なく 表 示 されても 他 の 人 のエディタではガタガタに 表 示 されてしまいます これを 防 ぐため タブキーを 押 したときには 空 白 文 字 が 入 力 されるようにエディタを 設 定 しておくことをお 薦 めします (サクラエディタでの 設 定 例 ) 設 定 タイプ 別 設 定 スクリーン タグ SPACE の 挿 入 にチェック 2. 関 数 宣 言 関 数 宣 言 は int foo( char *name, /* 名 前 */ int age) /* 年 齢 */ のように 書 きます あるいは 2/13
/* * 関 数 名 : foo * 機 能 : 名 前 と 年 齢 から その 人 の 寿 命 を 求 める * 引 数 : char *name : 名 前 * int age : 年 齢 * 戻 り 値 : (int) 寿 命 */ int foo(char *name, int age) と 関 数 へのコメントとあわせて 書 いても 良 いです 3. 変 数 宣 言 char *str; /* 説 明 */ int x; /* 説 明 */ のように 書 きます 型 が 同 じなら (1) char *from; /* 説 明 */ char *to; /* 説 明 */ のようにも 書 けるし カンマで 区 切 って (2) char *from, *to; /* 説 明 */ のようにも 書 けます しかし 追 加 や 削 除 のしやすさ およびコメントとの 対 応 を 重 視 して (1)を 採 用 します 変 数 宣 言 に 対 するコメントは できるだけつけるようにします コメントが 難 しい つま り 変 数 の 役 割 をひとことで 表 すことができないならば それは 設 計 がまずいといえます 変 数 の 役 割 を 明 確 にしましょう 一 つの 変 数 を 異 なる 目 的 で 使 うことはしないこと わずかな 記 憶 領 域 をけちるより わか りやすさを 優 先 します 上 の 例 では char と *from がとても 離 れているように 感 じます もうすこし 近 くてもよい 3/13
かもしれません しかし 構 造 体 変 数 を 使 うようになると このくらいがちょうどよく 思 え てきます char struct stat *buf; st; 要 は buf と st を 揃 えたいのです 次 のようだと ぱっと 見 てわかりにくいです char *buf; struct stat st; ポインタをあらわす * (アスタリスク)の 位 置 には 二 通 りの 書 き 方 があります char *ptr; //ポインタの 書 き 方 1 char* ptr; //ポインタの 書 き 方 2 研 修 では 前 者 の 書 き 方 を 採 用 します 4. 関 数 呼 び 出 しと 制 御 文 のカッコ 次 のように 関 数 名 とカッコは 離 さずに 書 きます c = fgetc(fp); if, for, while, switch といった 制 御 文 では カッコの 前 にスペースを 置 きます if (a > 0) switch (c) 慣 例 として return 文 にはカッコをつけません return 0; return a + b; 4/13
5. 制 御 文 の 中 カッコの 位 置 もっとも 論 争 が 起 きる 点 です いくつか 例 を 挙 げてみましょう if (a > 0) { printf(... ); 例 1 これを 採 用 します if (a > 0) { printf(... ); 例 2 if (a > 0) { printf(... ); 例 3 書 籍 などでは 例 1 が 多 いようです これは C 言 語 の バイブル と 呼 ばれたりする プ ログラミング 言 語 C (カーニハン リッチー 著 共 立 出 版 )という 本 で 採 用 しているスタ イルであること また 行 数 が 少 なくて 済 むということなどが 理 由 でしょう ただし 関 数 の 最 も 外 側 の 中 カッコは 通 常 1カラム 目 に 書 きます int func( void ) { int a; : : 例 2 は if の 判 定 文 と if の 中 身 が 連 続 した 行 にならないので 見 やすい カッコの 対 応 がわ かりやすいなどのメリットがあります 例 3 の 書 き 方 は GNU のコーディング 規 約 にありますが それ 以 外 ではあまり 見 かけません 5/13
6. 改 行 について 一 行 に 複 数 の 文 を 書 くことは 避 けます x = a + 1; y = b + 2; // 不 可 if 文 や if 文 の else 節 while 文 なども 改 行 します if (v < 0) printf(stderr,... ); // 不 可 if (a > 1) { x = a + 1; else { y = b + 2; while (i < n) { *p++ = str[i++]; コンマ 式 も for 文 の 中 以 外 では 使 用 しません z = c + 1, c++; // 不 可 for (i = 0, j= 0; i < n && j < m; i++, j++) { // 可 : 6/13
7. 制 御 文 での{ 中 カッコの 省 略 次 のような 場 合 です if (fp == NULL) fp = stdin; このような 場 合 fp == NULL のときの 処 理 を 追 加 しようとして 中 カッコをつけ 忘 れるこ とがあります if (fp == NULL) fprintf(stderr, 標 準 入 力 より 読 み 込 みます \n ); fp = stdin; すると 条 件 (fp == NULL)にかかわらず fp = stdin; は 実 行 されてしまいます このよ うなミスの 予 防 も 兼 ねて 制 御 文 が 実 行 するコードが1 行 だけのときも 中 カッコでくくるよ うにします if (fp == NULL) { fp = stdin; 8. switch 文 のインデント switch 文 も 様 々な 書 き 方 がありますが 研 修 中 は 以 下 のように 統 一 します switch (errcode) { case MANY_ARG: case LESS_ARG: puts( NG ); break; case NORMAL: break; default: puts( INVALID_ERROR ); break; 中 カッコの 位 置 は 6. 制 御 文 の 中 カッコの 位 置 に 準 拠 します 処 理 の 内 容 と 見 易 さを 考 慮 し : 以 降 には(コメント 以 外 は) 何 も 書 かず 各 処 理 と break; はインデントしま す 7/13
9. 演 算 子 周 りのスペース 次 のように 演 算 子 の 前 後 にはスペースを 置 きます area = width_x * width_y; ただし ++ や -- にはつけません また ポインタ 演 算 子 としての * は 次 のように 書 きます c = *p; アロー 演 算 子 にはスペースを 空 けません size = moon->magic; 10. 式 まわりのカッコ if 文 では 条 件 式 が 複 雑 になることがあります if (a < b c < d && e < f) と if ((a < b) ((c < d) && (e < f))) では 後 者 のほうが 悩 んだり 間 違 えたりする 余 地 が 少 なくなります 演 算 子 の 順 序 も 忘 れがちです val = higher << 16 + lower; これは 次 のコードと 同 じです val = higher << (16 + lower); シフト 演 算 子 は 優 先 順 位 が 低 いので 要 注 意 次 のように 書 きます val = (higher << 16) + lower; 8/13
結 論 として 優 先 順 位 で 悩 まないよう 極 力 カッコをつけるようにすること 誤 解 の 余 地 のないコードを 書 きましょう 11. コメントについて コメントには 独 立 した 行 に 書 くコメントと 行 末 コメントがあります 独 立 した 行 に 書 くコメントのインデントは コメントしようとするコードにあわせます /*... */ for (... ) { /*... */...; 行 末 コメントは 前 後 の 行 でカラムをそろえるようにします char *from; /* 説 明 */ char *to; /* 説 明 */ 特 に 関 数 の 引 数 や 変 数 宣 言 では コメントを 必 ずつけるようにします コメントの 重 要 な 目 的 は 意 図 を 記 述 することです つまり 何 をしようとしているか を 読 む 人 に 知 らせることです 読 み 手 にとって コードがどのような 動 作 をするかを 理 解 するよりも その 動 作 の 意 図 を 理 解 することの 方 が 難 しいものです 時 々 修 正 時 に 削 除 した 部 分 をコメントアウトして 残 しておく というものが 見 受 けられ ます /* if (a == 0) { 2007.12 削 除 */ /* hogehoge(); 2007.12 削 除 */ /* 2007.12 削 除 */ このようなコメントは ソースプログラムを 読 みにくくする 以 外 何 の 役 にも 立 ちません 変 更 の 履 歴 は ソースファイル 中 のコメントではなく 専 用 のツール(CVS SCCS など)を 使 用 して 管 理 してください 9/13
12. 名 前 一 般 変 数 や 関 数 には そのものをよく 表 す 名 前 をつけます そのような 名 前 が 浮 かばない 場 合 その 役 割 があいまいになってはいないでしょうか? 異 なる 複 数 の 目 的 のために 使 用 され てはいないでしょうか? 変 数 や 関 数 はある 単 一 の 目 的 のためだけに 使 用 されるべきで そ うでないなら 分 割 します Cでは 古 くから 関 数 名 や 変 数 名 に 小 文 字 を 用 いるのが 普 通 でした 最 近 ではそうでもなく なってきましたが まあ 小 文 字 が 基 本 でしょう アンダーバー(_)も 名 前 に 使 え 単 語 の 区 切 りのように 用 いることができます get_filename(&buf, sizeof(buf)); 大 文 字 をまぜる 命 名 法 では おそらく 次 のようになります getfilename(&buf, sizeof(buf)); 動 詞 + 対 象 という 命 名 がよくされます #define したマクロでは 大 文 字 で 名 前 をつけます #define MAX_NAME 10 一 般 に 次 の 変 数 は 次 のような 目 的 で 使 われるものと 思 われています i, j, k int 型 で ループカウンタに 用 いられます c char あるいは int 型 で 文 字 を 入 れます s char * 型 で 文 字 列 n 何 らかの 数 やカウンタ(int 型?) p 何 らかのポインタ fp FILE * 型 で ファイルポインタ このようなものは 慣 習 と 異 なる 別 な 意 味 で 用 いないようにします もっと 具 体 的 な 名 前 が 考 えられる 時 は そちらを 使 いましょう 10/13
13. 行 数 (サイズ)について 大 きすぎる 関 数 は 見 通 しが 悪 く 理 解 することもテストすることも 難 しくなります し たがって バグも 多 く 発 生 します 関 数 が 大 きすぎるということは 不 必 要 に 複 雑 であり 機 能 分 割 が 不 十 分 であることを 意 味 します 一 つの 関 数 のサイズは 空 白 行 を 除 いて およそ200 行 以 内 にします それを 越 すよう でしたら 機 能 をよく 整 理 し 複 数 の 関 数 に 分 割 します また 一 つのファイルのサイズはできれば1000 行 以 内 に 大 きくても2000 行 以 内 にします 11/13
14. バグを 未 然 に 防 ぐ assert コーディングスタイルの 話 からはそれますが assert の 話 関 数 の 引 数 チェックに 用 いる ことを 強 く 推 奨 します assert は デバッグ 用 の 関 数 で assert( 式 );と 書 きます 式 が 成 立 していれば 何 もしない が 式 が 不 成 立 の 場 合 はエラーメッセージを 出 して 終 了 します たとえば 正 の 数 を 引 数 としてとる 関 数 があるとします 設 計 では 正 の 数 かのチェックは その 関 数 を 呼 ぶ 側 が 行 い 正 の 数 しか 渡 ってこない ことになっています assert はこの ような 場 合 に 用 います #include #include <assert.h> <stdlib.h> int foo( int count) /* 正 の 数 */ { assert(count >= 0); /* 引 数 のチェック */ : assert なしで もし 負 の 数 が 渡 されたらどのような 動 作 をするのでしょうか 正 の 数 が 来 る はず なのでわかりません でしょうか わかりません というのは 厄 介 なバグを 生 む 大 きな 原 因 です 一 見 正 常 そうに 動 作 してしまうかもしれません それは 大 変 に 危 険 で す assert を 追 加 すると プログラムは 極 端 に 明 解 な 動 作 をします 仮 定 していない 値 が 渡 さ れたらエラー 終 了 するのです ここで assert は 内 部 的 な 不 整 合 を 検 出 している 点 に 注 意 してください 間 違 えているのは ユーザーではなく この 関 数 を 作 った 人 つまり 開 発 者 です このあたりの 切 り 分 けはな かなか 難 しいのですが 大 事 なことです ユーザーの 入 力 エラーに 対 しては assert は 用 いず もっと 美 しくエラー 終 了 すべきです はじめは assert にしておき 後 でエラーメッセージ 表 示 処 理 に 置 き 換 えていく 方 法 もあり ますが ともかくプログラムから このようなときの 動 作 はわかりません という 状 態 を なくすことです 12/13
15. 最 後 に 以 上 研 修 で 採 用 するコーディングスタイルの 説 明 およびアドバイスでした コーディングスタイルは 誰 でも 自 分 の 慣 れたスタイルが 書 きやすく 読 みやすいです また それぞれのスタイルにはそれぞれの 良 さがあり どちらが 優 れていると 判 定 できない 項 目 が 多 数 あります では 何 故 プロジェクトで 統 一 するかというと 個 々がバラバラのスタイルを 採 用 すると 全 体 として 非 常 に 見 難 いプログラムが 出 来 上 がってしまうからです ソースを 納 める 前 に indent コマンドを 実 行 する 事 を 求 められるジョブもあるくらいです (indent コマンドは ソースコードを 設 定 したコーディングスタイルに 矯 正 する)プロジェクトでどのコーディ ングスタイルを 採 用 するかはまちまちです ということで 以 上 に 述 べたコーディングスタイルは 絶 対 ではありません 重 要 なのは スタイルを 統 一 することにより 読 みやすく 美 しいプログラムを 書 く ということです 以 上 13/13