Fundamentals 43 min read

What Makes a Good Programmer? 32 Essential C/C++ Coding Practices for Clean, Maintainable Code

This guide outlines thirty‑two practical C/C++ coding habits—from proper file headers, indentation, and commenting to error handling, const usage, and avoiding macros pitfalls—aimed at improving code readability, maintainability, and reliability for developers of all levels.

Liangxu Linux
Liangxu Linux
Liangxu Linux
What Makes a Good Programmer? 32 Essential C/C++ Coding Practices for Clean, Maintainable Code

1. Copyright and Version

Every source file should start with a comment block that records the file name, description, author, creation date, version, and modification history. This header makes the purpose and history of the file clear.

/************************************************************************
* File: network.c
* Description: Network communication functions
* Author: Hao Chen, 2003-02-03
* Version: 1.0
* Modification Log:
************************************************************************/

Functions should have similar detailed comment blocks describing parameters, return values, and possible exceptions.

/*================================================================
* Function: XXX
* Parameters: ...
* Description: ...
* Return: TRUE on success, FALSE on failure
* Author: ChenHao 2003/04/02
================================================================*/

2. Indentation, Spaces, Line Breaks, Blank Lines, Alignment

Use a consistent indentation (one TAB or four spaces). Separate operators and operands with spaces, and break long statements onto multiple lines for readability. ha = (ha * 128 + *key++) % tabPtr->size; Align related statements and group logical blocks with blank lines.

if ((hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)) == NULL) {
    return FALSE;
}

3. Code Comments

Write clear comments that explain what the code does, why it exists, and any non‑obvious decisions. Avoid leaving large sections of code without any annotation.

4. Header File #ifndef Guards

Wrap every header file with #ifndef / #define / #endif to prevent multiple inclusion.

#ifndef _STDIO_H_
#define _STDIO_H_
/* header contents */
#endif

5. Error‑Handling Conventions

Define error codes as constants and keep a single error‑message table. Use a unified perror function to print messages, which makes debugging and localization easier.

#define ERR_OPEN_FILE 1
char* errmsg[] = {"No error", "Open file error", ...};
void perror(char* info) {
    if (info) printf("%s: %s
", info, errmsg[errno]);
    else printf("Error: %s
", errmsg[errno]);
}

6. Parameter Validation

Always check pointers and other inputs before use. Prefer early‑exit error checks rather than nested else blocks.

if (p == NULL) {
    errno = ERR_BAD_ARGS;
    return FALSE;
}
/* normal processing */

7. Modifying Others' Code

When fixing legacy code, comment out the original lines, add new code with a clear comment indicating who made the change and when. This preserves history without losing the original intent.

8. Refactoring Repeated Code

Extract duplicated logic into functions or macros. This reduces maintenance effort and prevents bugs caused by inconsistent updates.

9. Parentheses in Expressions

Surround complex sub‑expressions with parentheses to make operator precedence explicit and avoid misinterpretation.

if ((ch >= '0' && ch <= '9') && (ch >= 'a' && ch <= 'z')) { ... }

10. Const Correctness

Mark input‑only pointer parameters with const. This documents intent and lets the compiler catch accidental modifications.

11. Limit Function Parameter Count

Prefer no more than six parameters; use a struct to group related data when more are needed.

12. Explicit Return Types

Never omit the return type; write void for functions that return nothing and always specify the type for others.

13. Avoid goto Except for Cleanup

Use goto only for centralized error‑handling cleanup blocks; otherwise, structured control flow is clearer.

14. Macro Definition Practices

Define macros without a trailing semicolon and always parenthesize parameters and the entire replacement text to avoid unexpected side effects.

#define MAX(a,b) ((a) > (b) ? (a) : (b))

15. Use static Wisely

Declare file‑local variables and functions as static to limit their visibility and avoid name clashes across translation units.

16. Function Size

Keep functions under 600 lines; ideally under 100 lines. If a function grows beyond 300–500 lines, consider splitting it into smaller, single‑purpose functions.

17. typedef for Clarity

Use typedef to create meaningful names for primitive types, structs, and function pointers, improving readability and portability.

18. Define Constants with Macros

Replace magic numbers with named macros (e.g., #define MAX_USER_CNT 120) so that a single change updates all uses.

19. sizeof on Types, Not Variables

When allocating memory, use sizeof(type) rather than sizeof(variable) to avoid allocating the size of a pointer instead of the intended object.

pScore = (int*)malloc(sizeof(int) * SUBJECT_CNT);

20. Pay Attention to Compiler Warnings

Treat all warnings as errors; they often indicate subtle bugs such as unused variables, implicit function declarations, or unsafe casts.

21. Prefer for Over while

Use for loops when the initialization, condition, and increment are related; this keeps loop logic in one place and improves readability.

22. Debug vs. Release Builds

Wrap debugging output in a macro (e.g., TRACE) that expands to real code when -DDEBUG is defined and to nothing otherwise. This avoids manual removal of debug statements.

#ifdef DEBUG
void TRACE(const char* fmt, ...) { /* print */ }
#else
#define TRACE(fmt, ...)
#endif

23. Logical Operators Short‑Circuit

Remember that || stops evaluating after the first true operand and && stops after the first false operand; use this to prevent null‑pointer dereferences.

if (p != NULL && strlen(p) != 0) { ... }

24. Use for Instead of while for Traversals

When iterating over linked structures, place the pointer initialization, condition, and advancement in the for header.

for (p = head; p; p = p->next) { ... }

25. Avoid Unnecessary Global Variables

Prefer macros or local variables over globals; globals increase coupling and can cause hidden side effects.

26. Consistent Naming Conventions

Use descriptive, mixed‑case names (e.g., GetLocalHostName, UserAccount), avoid single‑letter names except for loop indices, and apply prefixes for module‑level symbols.

27. Use static for Frequently Used Constants

Declare constant strings or tables as static const inside a source file to keep them private and avoid repeated allocations.

28. Keep Code Size Manageable

Large functions often hide duplicated logic; refactor them into smaller helpers to keep each file under a reasonable size.

29. Use calloc for Zero‑Initialized Memory

When allocating arrays, calloc automatically zeroes the memory, eliminating the need for a separate memset.

30. Avoid Adding Semicolons to Macro Definitions

Define macros without a trailing semicolon; the caller adds the semicolon, preventing accidental syntax errors after macro expansion.

#define LINE "================================="
#define PRINT_LINE printf(LINE)

31. Use sizeof Correctly for Multi‑Dimensional Arrays

Allocate multi‑dimensional structures by multiplying the size of the element type by the number of elements, not by using sizeof(pointer).

char **p = (char**)calloc(20, sizeof(char*));
for (i = 0; i < 20; ++i) p[i] = (char*)calloc(100, sizeof(char));

32. Summary

Adopting these thirty‑two coding habits improves code readability, maintainability, and reliability. Good programmers treat code as a craft: they write clear comments, enforce consistent style, validate inputs, manage resources carefully, and keep the codebase modular. Following these guidelines helps both individual developers and teams produce robust, long‑lasting software.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Software Engineeringbest practicescoding standardsC programmingC++
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.