Coding Convention
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
9. When to Use a Struct
Structs in C are used to group related data together into a single unit. Use them to improve clarity, reduce duplication, and logically organize your code.
9.1 Use a Struct When
- You have related data items that conceptually belong together.
// Good: Position is a single concept with multiple parts
typedef struct Position_t {
int x;
int y;
} Position_t;
- A function takes or returns multiple values that are always used together.
// Instead of passing x and y separately:
void move_to(int x, int y);
// Use:
void move_to(Position_t pos);
- You want to encapsulate the state of a module, object, or entity.
typedef struct Player_t {
int id;
char name[32];
int health;
int score;
} Player_t;
- You want to make your code more self-documenting.
Structs make data structures explicit and easier to reason about.
- You are modeling a real-world object or concept.
For example: `Window_t`, `Vector2D_t`, `Enemy_t`, `Timer_t`, etc.
9.2 Avoid Creating a Struct When
- The values are only used temporarily and locally in one function.
In this case, keep them as local variables unless clarity improves by grouping.
- The data items are not conceptually related.
Grouping unrelated values can reduce clarity.
- It will add unnecessary indirection or complexity.
Avoid premature abstraction — keep it simple unless there's a clear benefit.
9.3 Naming Guidance
- Use `PascalCase` + `_t` for struct type names.
- Use `lowercase_with_underscores` for member fields.
- Prefer descriptive member names over short or cryptic ones.
9.4 Example: Before and After Struct
Before (repetitive, unclear):
void render_entity(int x, int y, int width, int height, const char *texture);
After (clear, modular):
typedef struct Rect_t {
int x;
int y;
int width;
int height;
} Rect_t;
void render_entity(Rect_t bounds, const char *texture);