閉じる

C言語 コメントの書き方とNG例|プロが使うルールとサンプルコード

C言語のコードは、一度書いて終わりではありません。

後から読み返す自分やチームメンバーが理解しやすいように整理されたコメントを書くことが、品質と開発スピードの両方に効いてきます。

本記事では、C言語のコメントの基本ルールから、プロが実務で使う書き方、やってはいけないNG例、さらにそのまま真似できるサンプルコードまで、体系的に詳しく解説します。

C言語のコメントの基本ルール

コメントの種類

C言語では、2種類のコメント記法が標準で用意されています。

ブロックコメント(/* … */)

/で始まり/で終わる形式です。

複数行にも1行にも使えます。

C言語
/* これはブロックコメントです。
 * 複数行にわたって説明を書くときに便利です。
 */
int main(void) {
    return 0;
}

ブロックコメントは、ファイル先頭のヘッダーコメントや、関数の仕様説明など、まとまった文章を書くときに向いています。

行コメント(// …)

//から行末までがコメントになります。

C言語
int count = 0;  // ループ回数を数える

行コメントは、短い補足やポイントだけを書きたいときに向いています。

C99以降の標準仕様で使えますが、古いコンパイラでは利用できない場合があるため、組み込みやレガシー環境では事前に確認する必要があります。

コメントで使ってはいけない記号の注意点

ブロックコメントの//入れ子にできない点に注意が必要です。

C言語
/* 外側のコメント
/* ここに別のコメントを書こうとすると */
// コンパイルエラーになる可能性が高い
*/

このようなパターンを避けるために、「既にある/* ... */の中に、さらに/*を書かない」ルールをチームで共有しておくと安全です。

どの範囲にコメントを書くべきか

コメントは多ければ良いわけではありません。

「どこに、どのレベルで」書くかを決めておくと、コードが読みやすくなります。

ファイル・モジュール単位のコメント

ファイルの先頭には、そのファイルが何を担当しているかを明記します。

C言語
/* timer.c - システムタイマ制御モジュール
 *
 * 概要:
 *   - ハードウェアタイマを初期化し、周期割り込みを提供する
 *   - ミリ秒単位の遅延関数を提供する
 *
 * 注意事項:
 *   - 割り込みハンドラから呼ばれる関数は再入可能であること
 *   - マルチスレッド環境ではロックの取得順序に注意すること
 */

このレベルでは、「細かい処理内容」ではなく、責務・制約・他モジュールとの関係を中心に書きます。

関数レベルのコメント

関数の役割や、引数・戻り値・前提条件などをまとめて説明します。

詳細は後述の「関数コメントの書き方」で具体例を示します。

ブロック(条件分岐・ループ)レベルのコメント

if文やfor文などが「なぜその条件なのか」「なぜこのループが必要なのか」が分かりにくい場合、その直前にコメントを書きます。

C言語
/* タイムアウト判定:
 * - 現在時刻が開始時刻 + 500ms を超えた場合は異常とする
 */
if (now_ms > start_ms + 500) {
    return ERR_TIMEOUT;
}

行レベルのコメント

行レベルのコメントは、本当に必要な場合に限るのがポイントです。

コードから読み取れる内容をなぞるコメントは避け、「理由」や「背景」に限定します。

コメントで避けるべき曖昧な表現

曖昧なコメントは、無いよりも有害になることがあります。

避けるべき曖昧な表現の代表例として、次のようなものがあります。

  • 「たぶん」「おそらく」「一応」「たいてい」「とりあえず」など
  • 「よく分からないが動く」「とにかくこれでうまくいった」
  • 「そのうち直す」「あとで最適化する(いつか)」

これらは、責任の所在をぼかしてしまい、仕様や品質の判断を誤らせる危険があります。

代わりに、根拠・期間・出典・制約を明確に書きます。

C言語
/* 仕様上の制約:
 *   - プロトコル仕様書 v1.3 の 4.2節では、
 *     パケット長は最大 32 バイトと定義されている。
 *   - そのため、バッファサイズは固定で 32 にしている。
 */
#define PACKET_BUF_SIZE  (32)

コメントの書き方

意図や設計方針を書くコメントの例

「なぜこの設計にしたのか」は、コードだけでは伝わりにくい部分です。

将来、別の人がリファクタリングする際に、意図が分かっているかどうかで判断が大きく変わります。

C言語
/* イベントキューの実装について
 *
 * 設計方針:
 *   - メモリフットプリントを小さくするため、固定長配列を使用する
 *   - 割り込みハンドラからもアクセスされるため、ロックは使わない
 *   - その代わり、単一プロデューサ・単一コンシューマの前提を置く
 *
 * 代替案:
 *   - リンクリスト + 動的確保は検討したが、
 *     組み込み環境での断片化リスクを避けるため採用しなかった
 */

このように、採用しなかった案や制約も書いておくと、将来同じ検討を繰り返さずに済みます。

関数コメント(役割・引数・戻り値)の書き方

関数コメントは、ミニマムなドキュメントと考えると分かりやすいです。

少なくとも、次の情報を揃えておきます。

  • 関数の役割(何をするか)
  • 引数(意味・単位・前提条件)
  • 戻り値(成功/失敗の判定方法)
  • 副作用(グローバル変数の更新、外部I/Oなど)
  • 注意事項(スレッドセーフかどうか、呼び出し順序など)
C言語
/* タイムアウト付き読み取り関数
 *
 * 概要:
 *   指定されたファイルディスクリプタから、最大lenバイトまで読み取る。
 *   timeout_msで指定された時間内にデータが到着しない場合、タイムアウトとして扱う。
 *
 * 引数:
 *   fd         - 読み取り対象のファイルディスクリプタ(有効な値であること)
 *   buf        - 読み取りデータを書き込むバッファ(長さlen以上)
 *   len        - 読み取る最大バイト数(0より大きいこと)
 *   timeout_ms - タイムアウト時間(ミリ秒)。0の場合は即時タイムアウト。
 *
 * 戻り値:
 *   > 0  - 実際に読み取ったバイト数
 *   = 0  - EOFに到達
 *   < 0  - エラー(詳細はerrnoに設定される)
 *
 * 注意事項:
 *   - この関数はスレッドセーフではない。
 *   - ノンブロッキングモードのfdに対しては使用しないこと。
 */
ssize_t read_with_timeout(int fd, void *buf, size_t len, int timeout_ms);

引数や戻り値の意味が自明でない場合ほど、丁寧にコメントを書くことが重要です。

変数・定数に対するコメントの付け方

変数や定数は、名前だけでは伝わりきらない情報をコメントで補います。

定数に対するコメント

C言語
/* ログファイル1行あたりの最大長(バイト)
 * 仕様上、1行の長さは最大1024バイトと決められているため、
 * バッファサイズは終端文字分を含めて1025にしている。
 */
#define LOG_LINE_MAX_LEN  (1025)

このように、「なぜその値なのか」「どの仕様に基づくのか」を書きます。

変数に対するコメント

C言語
/* 現在の接続数。
 * 接続が増減するたびに、必ずこの値を更新すること。
 * 負の値にはならない前提。
 */
static int current_connection_count = 0;

また、単位が重要な場合は必ずコメントで明記するか、名前に含めます。

C言語
int timeout_ms;    /* タイムアウト時間(ミリ秒) */
double speed_mps;  /* 速度(m/s) */

アルゴリズムや処理手順を説明するコメント

アルゴリズムの実装は、「どう書いてあるか」よりも「何をしているか」「なぜその方法なのか」が重要です。

C言語
/* バブルソートによる昇順ソート
 *
 * 概要:
 *   - 配列の隣り合う要素を比較し、順序が逆であれば交換する。
 *   - これを配列の末尾まで繰り返し、末尾から順に最大値が確定していく。
 *
 * 選定理由:
 *   - 要素数が最大でも 20 個と少ないため、実装が単純なバブルソートを採用。
 *   - 安定ソートであることも条件の1つ。
 *
 * 計算量:
 *   - 時間計算量はO(n^2)
 *   - 空間計算量はO(1)
 */
void bubble_sort(int *arr, size_t n)
{
    size_t i, j;
    for (i = 0; i < n; ++i) {
        for (j = 0; j + 1 < n - i; ++j) {
            if (arr[j] > arr[j + 1]) {
                /* 隣り合う要素の順序が逆の場合に交換する */
                int tmp   = arr[j];
                arr[j]    = arr[j + 1];
                arr[j+1]  = tmp;
            }
        }
    }
}

コメントでは、処理の全手順を逐一説明する必要はありません

概要・理由・特性を中心に書き、詳細はコードの読みやすさでカバーします。

TODOコメント・FIXMEコメントの使い分け

実務では、未完了の作業や既知の問題をTODOFIXMEコメントで管理することがあります。

TODOコメント

「将来やりたい改善や未完の実装」を示します。

C言語
/* TODO(2025-03-31まで):
 *   - 設定値をハードコードではなく設定ファイルから読み込むようにする
 *   - 環境変数での上書きもサポートする
 */
int max_clients = 100;  /* とりあえず固定値 */

期限や担当者をTODO(担当者, 期限)のように書くルールを決めておくと、放置されにくくなります

FIXMEコメント

「既知のバグや問題があるが、まだ直せていない箇所」に付けます。

C言語
/* FIXME(佐藤, 2025-01-15):
 *   - マルチスレッド環境でデータ競合が発生する可能性がある。
 *   - 暫定対処としてグローバルロックを使用しているが、
 *     性能劣化が大きいためロック粒度の見直しが必要。
 */
pthread_mutex_t g_lock;

FIXMEは放置してはいけないコメントなので、CIでFIXMEを検出してアラートを出す運用も有効です。

コメントのNG例と悪いパターン

コードの内容をそのまま説明するだけのコメント

「見れば分かること」をそのまま書くコメントは、ノイズになりがちです。

C言語
/* NG例 */
int i;  // iという変数
i = 0;  // 0を代入する
i++;    // 1増やす

このレベルの説明はコードの可読性を下げるだけなので、次のように変数名で意図を表現します。

C言語
/* OK例 */
int retry_count = 0;  /* リトライ回数。初期値は0回 */
retry_count++;

コメントで補う前に、まずは名前を改善することを意識します。

更新されずに嘘になるコメントのNG例

コードとコメントの内容が食い違っていると、バグの原因になります。

C言語
/* 最大3回リトライする */
#define MAX_RETRY  (4)

for (int i = 0; i < MAX_RETRY; ++i) {
    /* ... */
}

このような矛盾を避けるため、コメントに具体的な数値を書かない方が安全な場合があります。

C言語
/* リトライ回数:
 * - 一時的な通信エラーに対して再試行する。
 * - 実際の回数はMAX_RETRYで定義する。
 */
#define MAX_RETRY  (4)

「変更がコード側とコメント側の両方に必要になる書き方」は極力避けるのがポイントです。

冗長で読みづらい長文コメント

説明を丁寧にしようとして、コメントが長文だらけになるのも問題です。

C言語
/* NG例: 1つの段落に長文が詰め込まれている
 * この関数は、データベースからユーザ情報を取得してキャッシュに格納し、
 * もしユーザが存在しない場合にはエラーコードを返すようにしていて、
 * さらにログを出力して監査ログにも記録するようになっており...
 */

長くなりそうな場合は、要点を箇条書きにして行を分けることで読みやすくできます。

C言語
/* この関数の役割:
 *   - DBからユーザ情報を取得する
 *   - 結果をメモリキャッシュに格納する
 *   - ユーザが存在しない場合はエラーコードを返す
 *   - 監査ログにアクセス履歴を記録する
 */

1行の長さは80文字前後を目安に折り返すと、IDEやレビュー画面でも見やすくなります。

バグの原因になる誤解を招くコメント

コメントが実際の挙動と違うイメージを与えると、テストやリファクタリングでミスを誘発します。

C言語
/* この関数はスレッドセーフである */
static int counter = 0;

int increment_counter(void)
{
    return ++counter;  /* 実際にはロックしていない */
}

このようなコメントは「嘘の仕様書」と同じです。

実際の挙動に自信がない場合は、次のように書き方を変えます。

C言語
/* 現状、この関数はスレッドセーフではない。
 * 将来的にマルチスレッド対応が必要になった場合は、
 * counterへのアクセスにミューテックスを導入すること。
 */

断言口調で書くときは、テストやコードを見て事実であることを確認してからにします。

メンテナンスしにくいコメントスタイルの例

コメントにもスタイルがあります。

一貫性のないスタイルはメンテナンス性を下げます。

NGパターンの例として、次のようなものがあります。

  • 1つのファイルの中で///* ... */の使い方がバラバラ
  • 関数コメントのフォーマットが毎回違う
  • 日本語と英語が混在し、どちらも中途半端
  • インデントや整形が崩れていて読みにくい

これらを避けるために、チームでコメントスタイルをルール化し、「関数コメントは必ずこの形式」「TODOには期限を書く」などの基準を共有しておくことが重要です。

コメントのサンプルコード集

ここからは、実際にそのまま使えるコメント付きのサンプルコードを紹介します。

良いコメント付き関数のサンプルコード

C言語
#include <stdio.h>
#include <string.h>

/* 文字列の末尾にサフィックスを追加するユーティリティ関数
 *
 * 概要:
 *   - destの末尾にsuffixを連結する。
 *   - 連結結果がdest_sizeバイトを超える場合は、
 *     超えないところまでで切り捨てる(終端の'\0'は必ず付与)。
 *
 * 引数:
 *   dest      - 連結先バッファ。初期文字列が格納されていること。
 *   dest_size - destバッファの全体サイズ(バイト数)。
 *   suffix    - 末尾に連結したい文字列(UTF-8を想定)。
 *
 * 戻り値:
 *   0   - 成功(切り捨ての有無にかかわらず)
 *   -1  - 引数が不正(NULLポインタ、サイズ0など)
 *
 * 注意事項:
 *   - 切り捨てが発生したかどうかは戻り値では分からない。
 *     将来的に判別が必要になった場合は、別APIの追加を検討すること。
 */
int append_suffix(char *dest, size_t dest_size, const char *suffix)
{
    size_t len_dest;
    size_t len_suffix;

    if (dest == NULL || suffix == NULL || dest_size == 0) {
        return -1;
    }

    len_dest   = strlen(dest);
    len_suffix = strlen(suffix);

    if (len_dest >= dest_size - 1) {
        /* すでにこれ以上追加できないので、そのまま終了 */
        dest[dest_size - 1] = '\0';
        return 0;
    }

    /* 追加できる最大バイト数を計算する(終端の'\0'を除く) */
    size_t max_append = dest_size - 1 - len_dest;

    /* 実際にコピーするバイト数(切り捨てが発生する可能性あり) */
    size_t copy_len = (len_suffix < max_append) ? len_suffix : max_append;

    memcpy(dest + len_dest, suffix, copy_len);
    dest[len_dest + copy_len] = '\0';

    return 0;
}

int main(void)
{
    char buf[16] = "Hello";

    append_suffix(buf, sizeof(buf), ", world!");

    printf("%s\n", buf);
    return 0;
}
実行結果
Hello, world!

このサンプルでは、関数コメントで「仕様」「制約」「将来の拡張ポイント」を明確にしています。

エラーハンドリングのコメント例

エラーハンドリングは、「なぜこのように扱うのか」をコメントしておくと、意図が伝わりやすくなります。

C言語
#include <stdio.h>
#include <errno.h>
#include <string.h>

/* 設定ファイルを開き、FILEポインタを返す
 *
 * エラー時の扱い:
 *   - ファイルが存在しない場合(ENOENT):
 *       デフォルト設定で起動し、警告ログのみ出力する。
 *   - 権限エラー(EACCESなど):
 *       起動を中止し、ユーザにメッセージを表示する。
 */
FILE *open_config_file(const char *path)
{
    FILE *fp = fopen(path, "r");
    if (fp == NULL) {
        int err = errno;

        if (err == ENOENT) {
            /* 設定ファイルが存在しない場合:
             *   - デフォルト設定で起動することを想定。
             *   - ログレベルWARNで警告を出しておく。
             */
            fprintf(stderr,
                    "Warning: config file '%s' not found. "
                    "Using default settings.\n",
                    path);
            return NULL;
        }

        /* それ以外のエラーは致命的とみなし、起動を中止する。 */
        fprintf(stderr,
                "Error: failed to open config file '%s': %s\n",
                path, strerror(err));
        return NULL;
    }

    return fp;
}

このように、エラーごとの方針をコメントに残しておくと、別の開発者が挙動を変えようとするときに判断しやすくなります。

組み込み向けCコードのコメント例

組み込みCでは、ハードウェア依存の情報をコメントでしっかり補う必要があります。

C言語
#include <stdint.h>

/* タイマーモジュールのベースアドレス
 * Datasheet: XYZ Microcontroller Timer v2 (Table 3-1)
 */
#define TIMER_BASE_ADDR   (0x40000000UL)

/* タイマー制御レジスタ(TCR)のオフセット
 *   bit0: enable (1=有効, 0=無効)
 *   bit1: mode   (0=one-shot, 1=periodic)
 *   bit2: irq_en (1=割り込み有効, 0=無効)
 */
#define TIMER_TCR_OFFSET  (0x00U)

/* タイマー周期レジスタ(TPR)のオフセット
 *   - 単位は1クロックサイクル
 *   - 実際のタイマー周期は TPR / Fclk で決まる
 */
#define TIMER_TPR_OFFSET  (0x04U)

#define TIMER_TCR   (*(volatile uint32_t *)(TIMER_BASE_ADDR + TIMER_TCR_OFFSET))
#define TIMER_TPR   (*(volatile uint32_t *)(TIMER_BASE_ADDR + TIMER_TPR_OFFSET))

/* 1ms間隔でタイマー割り込みを発生させる初期化関数
 *
 * 引数:
 *   sys_clk_hz - システムクロック周波数(Hz)
 */
void timer_init_1ms(uint32_t sys_clk_hz)
{
    /* 1ms分のカウント値を計算
     *   例: sys_clk_hz = 48MHz の場合、48,000カウントで1ms
     */
    uint32_t counts_per_ms = sys_clk_hz / 1000U;

    TIMER_TPR = counts_per_ms;

    /* タイマー有効 + 周期モード + 割り込み有効 */
    TIMER_TCR = (1U << 0) | (1U << 1) | (1U << 2);
}

ここでは、レジスタの意味・ビット配置・データシートへの参照をコメントで明確にしています。

チーム開発で使えるコメント規約サンプル

最後に、チームで共有できるコメント規約のサンプルを示します。

【Cコメント規約サンプル(抜粋)】

1. 言語
   - コメントは原則として日本語で記述する。
   - 外部公開APIやプロトコル仕様に関するコメントのみ英語とする。

2. コメント記法
   - 関数コメント、モジュール概要コメントにはブロックコメント(/* ... */)を使用する。
   - 行単位の短い補足には行コメント(// ...)を使用する。

3. 関数コメントのテンプレート
   - 形式:
     /*
      * 概要:
      *   - ...
      *
      * 引数:
      *   arg1 - ...
      *
      * 戻り値:
      *   0   - ...
      *   <0  - ...
      *
      * 注意事項:
      *   - ...
      */
   - 全ての外部公開関数(API)には関数コメントを必須とする。

4. TODO / FIXME コメント
   - 書式:
     - TODO(担当者, 期限): 説明...
     - FIXME(担当者, 期限): 説明...
   - 期限のフォーマットはYYYY-MM-DDとする。
   - FIXMEはリリース前に0件であることをCIでチェックする。

5. 禁止事項
   - コードの内容をそのまま説明するコメント(「iをインクリメントする」など)。
   - 曖昧な表現(「たぶん」「おそらく」「一応」など)。
   - 日本語と英語が混在した中途半端なコメント。

このような規約を最初に決めておくことで、レビューの軸がはっきりし、コメント品質のばらつきも減らせます

まとめ

C言語のコメントは、単なる「メモ」ではなく、将来の自分や他人への正式なコミュニケーション手段です。

どの範囲に何を書くかを意識し、コードからは読み取れない「意図」「背景」「制約」「設計方針」を中心にコメントすることで、保守性と信頼性が大きく向上します。

NG例で見たような「嘘のコメント」「冗長すぎるコメント」を避けつつ、チームで統一したルールを運用すれば、コメントは強力な開発支援ツールになります。

本文とサンプルコードを参考に、日々のC言語開発に適切なコメントスタイルを取り入れてみてください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!