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

299 lines
5.9 KiB
Markdown

# 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` ファイルの先頭には、簡単な説明を書く。
```c
/* user.h - ユーザー管理に関する宣言を提供するモジュール */
#ifndef USER_H
#define USER_H
/* ... */
#endif /* USER_H */
```
### 2.2 関数コメント(Doxygen 形式推奨)
公開 API になる関数には、Doxygen 形式のコメントを付けると便利。
```c
/**
* @brief ユーザーを作成する。
*
* @param name ユーザー名(NULL終端文字列)。
* @param age 年齢。
* @return 成功時は0、失敗時は負のエラーコード。
*/
int user_create(const char *name, int age);
```
### 2.3 行コメント
複雑なロジックやトリッキーなコードには、意図をコメントで残す。
```c
/* バッファが足りない場合は拡張する */
if (needed_size > buf->capacity) {
/* ... */
}
```
---
## 3. 命名規則
1. **関数名/変数名**: `snake_case` を使用する。
```c
int max_value = 0;
int get_max_value(int a, int b);
```
2. **構造体名**:
* `snake_case` または `CamelCase` だが、プロジェクト内で統一する。
* typedef を併用する場合は `typedef struct user user_t;` のような接尾辞 `_t` を使うこともある。
```c
typedef struct user {
int id;
char name[64];
} user_t;
```
3. **定数マクロ**: `UPPER_CASE_SNAKE` を使う。
```c
#define MAX_USER_NAME_LEN 64
```
4. **列挙型**:
```c
typedef enum {
STATUS_OK = 0,
STATUS_ERROR = -1
} status_t;
```
5. **グローバル変数**は `g_` プレフィックスを付けるなど、わかりやすくする。
```c
static int g_initialized = 0;
```
---
## 4. インクルードガイドライン
1. ヘッダファイルのインクルード順:
1. 対応する自ヘッダ(`.c` と同名の `.h`)
2. 標準ライブラリ
3. 他のプロジェクトヘッダ
```c
#include "user.h"
#include <stdio.h>
#include <stdlib.h>
#include "logger.h"
```
2. `.c` ファイルで **必要なものだけ** `#include` する。
3. ヘッダは **インクルードガード** または `#pragma once` を必ず付ける。
```c
#ifndef USER_H
#define USER_H
/* 宣言 */
#endif /* USER_H */
```
---
## 5. エラーハンドリング
1. C では例外がないため、**戻り値によるエラーコード**を徹底する。
```c
int rc = do_something();
if (rc != 0) {
fprintf(stderr, "do_something failed: %d\n", rc);
return rc;
}
```
2. `0` を成功、負の値をエラーコードとする慣習が多い(プロジェクトで統一)。
3. リソース確保後に複数のエラーがあり得る場合は、`goto cleanup` パターンを使ってリークを防ぐ。
```c
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 基本構造
```text
src/
user.h
user.c
main.c
```
#### `user.h`(宣言)
```c
#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`(実装)
```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`
```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` の例
```yaml
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
ColumnLimit: 100
AllowShortIfStatementsOnASingleLine: false
BreakBeforeBraces: Allman
```
### 7.2 VSCode 設定例(`.vscode/settings.json`)
```json
{
"[c]": {
"editor.defaultFormatter": "ms-vscode.cpptools",
"editor.formatOnSave": true
},
"C_Cpp.clang_format_style": "file",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true
}
```