Coding Convention

From NCOT Wiki
Jump to navigationJump to search

C Coding Convention

This convention defines naming and style rules for writing clean, consistent, and self-documenting C code.

General Principles

  • Use lowercase_with_underscores for variables and functions.
  • Use PascalCase for struct and enum type names.
  • Use prefixes to namespace functions and globals by module/library.
  • Keep names brief but descriptive.

1. Enums

Type Name

  • Use PascalCase with an `_e` suffix.
typedef enum GameState_e {
    GAME_STATE_INIT,
    GAME_STATE_RUNNING,
    GAME_STATE_PAUSED,
    GAME_STATE_EXIT
} GameState_e;

Enum Values

  • Use ALL_CAPS with a common prefix for namespacing.

2. Structs / "Classes"

Type Name

  • Use PascalCase with a `_t` suffix.
typedef struct Player_t {
    int id;
    char name[32];
    float health;
} Player_t;

Member Names

  • Use lowercase_with_underscores.
  • Optionally prefix with struct abbreviation if needed (e.g. `pos_x`, `player_name`).

3. Global Variables

  • Use `g_` prefix.
  • Follow with lowercase_with_underscores.
int g_frame_counter;
bool g_is_debug_mode;
  • For internal linkage, use `static`:
static int g_last_error_code;

4. Functions

General Functions

  • Use lowercase_with_underscores.
  • Use verb_noun structure for clarity.
void init_game();
int calculate_damage(int weapon_id);

Library/Module Functions

  • Prefix with the module name (e.g. `audio_`, `player_`).
// In audio module
void audio_init();
void audio_play_sound(const char *name);

5. Constants / Macros

  • Use ALL_CAPS_WITH_UNDERSCORES.
#define MAX_PLAYERS 16
#define PI 3.14159f

6. File Naming

  • Use `module_name.c` and `module_name.h`.

Examples:

  • `player.c`, `game_state.h`, `audio_mixer.c`

7. Example Summary

// game_state.h
typedef enum GameState_e {
    GAME_STATE_INIT,
    GAME_STATE_RUNNING,
    GAME_STATE_PAUSED,
    GAME_STATE_EXIT
} GameState_e;

// player.h
typedef struct Player_t {
    int id;
    char name[32];
    float health;
    bool is_alive;
} Player_t;

// player.c
Player_t g_main_player;

void player_init(Player_t *player);
void player_take_damage(Player_t *player, float damage);

8. Return Value Guidelines

Well-defined return values make code more predictable and easier to debug. Follow these conventions:

8.1 Function Types

**Void functions**

Use when the function performs an action and no result needs to be reported.

void log_message(const char *msg);
**Value-returning functions**

Use to return computed values or status.

int calculate_score(int time_remaining, int enemies_defeated);

8.2 Return Codes for Status/Errors

**Success/Failure convention**

Return `0` for success, non-zero for failure. Use named `#define` or `enum` values for clarity.

// error_codes.h
#define ERR_OK          0
#define ERR_FILE_NOT_FOUND 1
#define ERR_INVALID_ARG     2
// file_loader.c
int file_load(const char *path) {
    if (!path) return ERR_INVALID_ARG;
    ...
    return ERR_OK;
}
**Boolean results**

Return `bool` if the result is true/false only. Use `<stdbool.h>`.

#include <stdbool.h>

bool player_is_alive(Player_t *player);

8.3 Output via Pointers

If a function needs to return multiple values, return one as the return value and others via pointers.

bool get_player_stats(int player_id, int *health_out, int *score_out);

Use `_out` or `_result` suffix for output parameters.

8.4 Document Return Meaning Clearly

Always document what return values mean in headers or comments.

/**
 * Loads a resource from disk.
 * 
 * @param path Path to the resource file.
 * @return ERR_OK on success, or an error code on failure.
 */
int resource_load(const char *path);

8.5 Optional Return Value Idioms

**Nullable pointers**

Return `NULL` on failure when returning pointers.

Texture_t *texture_load(const char *filename); // Returns NULL on failure
**Sentinel values**

Use a known invalid value (e.g. `-1`, `NULL`, `0`) if needed and document it clearly.

int find_entity_by_name(const char *name); // Returns -1 if not found