cpp-common-code/readme/c-coding-conv.md

5.9 KiB

C コーディング規約

1. 一般的なガイドライン

  1. コード整形は 必ず clang-format などの自動整形ツールに従う。
  2. インデントは スペース4つ もしくは スペース2つ とし、プロジェクト内で統一する(clang-format で強制)。
  3. 1行の長さは 80〜100文字程度 を目安にする。
  4. グローバル変数は最小限にし、関数引数と戻り値でデータをやりとりする。
  5. ポインタ・メモリ管理は明確にし、malloc/free の対応関係を書きやすい構造にする。
  6. ヘッダファイルとソースファイルを分け、宣言は .h、実装は .c に置く。

ドキュメント構造

  • src/ (ソースファイル)
    • 実装(implementation)を置く場所(.cや.cpp が入る)
  • include/ (ヘッダファイル)
    • .h または .hpp が入る
    • ほかのモジュールに公開したいインタフェースを記述する
    • CMake、Makefile などでinclude/include pathに入れれば、どこからでも #include "user.h"で参照できる。`

2. コメント/ドキュメント

2.1 ファイル先頭コメント

.c / .h ファイルの先頭には、簡単な説明を書く。

/* user.h - ユーザー管理に関する宣言を提供するモジュール */

#ifndef USER_H
#define USER_H

/* ... */

#endif /* USER_H */

2.2 関数コメント(Doxygen 形式推奨)

公開 API になる関数には、Doxygen 形式のコメントを付けると便利。

/**
 * @brief ユーザーを作成する。
 *
 * @param name ユーザー名(NULL終端文字列)。
 * @param age  年齢。
 * @return 成功時は0、失敗時は負のエラーコード。
 */
int user_create(const char *name, int age);

2.3 行コメント

複雑なロジックやトリッキーなコードには、意図をコメントで残す。

/* バッファが足りない場合は拡張する */
if (needed_size > buf->capacity) {
    /* ... */
}

3. 命名規則

  1. 関数名/変数名: snake_case を使用する。

    int max_value = 0;
    
    int get_max_value(int a, int b);
    
  2. 構造体名:

    • snake_case または CamelCase だが、プロジェクト内で統一する。
    • typedef を併用する場合は typedef struct user user_t; のような接尾辞 _t を使うこともある。
    typedef struct user {
        int  id;
        char name[64];
    } user_t;
    
  3. 定数マクロ: UPPER_CASE_SNAKE を使う。

    #define MAX_USER_NAME_LEN 64
    
  4. 列挙型:

    typedef enum {
        STATUS_OK = 0,
        STATUS_ERROR = -1
    } status_t;
    
  5. グローバル変数g_ プレフィックスを付けるなど、わかりやすくする。

    static int g_initialized = 0;
    

4. インクルードガイドライン

  1. ヘッダファイルのインクルード順:

    1. 対応する自ヘッダ(.c と同名の .h)
    2. 標準ライブラリ
    3. 他のプロジェクトヘッダ
    #include "user.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include "logger.h"
    
  2. .c ファイルで 必要なものだけ #include する。

  3. ヘッダは インクルードガード または #pragma once を必ず付ける。

    #ifndef USER_H
    #define USER_H
    
    /* 宣言 */
    
    #endif /* USER_H */
    

5. エラーハンドリング

  1. C では例外がないため、戻り値によるエラーコードを徹底する。

    int rc = do_something();
    if (rc != 0) {
        fprintf(stderr, "do_something failed: %d\n", rc);
        return rc;
    }
    
  2. 0 を成功、負の値をエラーコードとする慣習が多い(プロジェクトで統一)。

  3. リソース確保後に複数のエラーがあり得る場合は、goto cleanup パターンを使ってリークを防ぐ。

    int fn(void)
    {
        char *buf = NULL;
        FILE *fp = NULL;
        int   rc = 0;
    
        buf = malloc(1024);
        if (!buf) {
            rc = -1;
            goto cleanup;
        }
    
        fp = fopen("file.txt", "r");
        if (!fp) {
            rc = -2;
            goto cleanup;
        }
    
        /* 正常処理 */
    
    cleanup:
        if (fp) {
            fclose(fp);
        }
        if (buf) {
            free(buf);
        }
        return rc;
    }
    

6. 複数ファイル間の分割(.h / .c)

6.1 基本構造

src/
  user.h
  user.c
  main.c

user.h(宣言)

#ifndef USER_H
#define USER_H

typedef struct user {
    int  id;
    char name[64];
} user_t;

int  user_init(user_t *u, int id, const char *name);
void user_print(const user_t *u);

#endif /* USER_H */

user.c(実装)

#include "user.h"

#include <stdio.h>
#include <string.h>

int user_init(user_t *u, int id, const char *name)
{
    if (!u || !name) {
        return -1;
    }
    u->id = id;
    strncpy(u->name, name, sizeof(u->name) - 1);
    u->name[sizeof(u->name) - 1] = '\0';
    return 0;
}

void user_print(const user_t *u)
{
    if (!u) {
        return;
    }
    printf("User{id=%d, name=%s}\n", u->id, u->name);
}

main.c

#include <stdio.h>

#include "user.h"

int main(void)
{
    user_t u;
    if (user_init(&u, 1, "Alice") != 0) {
        fprintf(stderr, "failed to init user\n");
        return 1;
    }
    user_print(&u);
    return 0;
}

7. clang-format / VSCode の設定

7.1 .clang-format の例

BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
ColumnLimit: 100
AllowShortIfStatementsOnASingleLine: false
BreakBeforeBraces: Allman

7.2 VSCode 設定例(.vscode/settings.json)

{
  "[c]": {
    "editor.defaultFormatter": "ms-vscode.cpptools",
    "editor.formatOnSave": true
  },
  "C_Cpp.clang_format_style": "file",
  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true
}