Blog Datasheets Home About me Clients My work Services Contact

G2Labs Grzegorz Grzęda

Writing Clean and Maintainable Code in C

July 26, 2024

Writing Clean and Maintainable Code in C

As software developers, one of our goals should always be to write clean and maintainable code. Clean code is easy to read, understand, and modify, while maintainable code ensures that our programs can be easily updated and expanded upon in the future. In this blog post, we will explore some strategies and best practices for writing clean and maintainable code in the C programming language.

1. Use Meaningful and Descriptive Naming

One of the fundamental principles of clean code is using meaningful and descriptive names for variables, functions, and data structures. By providing clear and concise names, we can enhance the readability and maintainability of our code. Let’s take a look at an example:

1
2
3
int x; // Not clear what `x` represents

int number_of_students; // Much more descriptive

In the above example, using the name number_of_students instead of x makes it immediately clear what the variable represents.

2. Break Down Functions into Smaller Units

Functions should be designed to perform a single task – this is often referred to as the Single Responsibility Principle. Breaking down complex functions into smaller units not only makes the code more readable and easier to understand, but also allows for easier testing and maintenance.

Consider the following example where a function is responsible for both calculating the average and printing the result:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void calculateAndPrintAverage(int* array, int size) {
    int sum = 0;

    for (int i = 0; i < size; i++) {
        sum += array[i];
    }

    double average = (double)sum / size;

    printf("The average is: %lf\n", average);
}

It is preferable to separate the calculation and printing tasks into two separate functions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
double calculateAverage(int* array, int size) {
    int sum = 0;

    for (int i = 0; i < size; i++) {
        sum += array[i];
    }

    return (double)sum / size;
}

void printAverage(double average) {
    printf("The average is: %lf\n", average);
}

By breaking down the original function into smaller units, the code becomes more modular and easier to comprehend.

3. Limit Function and Variable Scope

Minimizing the scope of functions and variables helps in reducing complexity and prevents unintended consequences. Only declare variables and define functions where they are strictly needed. This practice improves code readability and prevents naming conflicts.

Consider the following example:

1
2
3
4
5
6
7
8
9
void processArray(int* array, int size) {
    int i;

    for (i = 0; i < size; i++) {
        // Process array elements
    }

    // May accidentally use `i` somewhere else
}

In this case, the variable i is declared outside the loop, which makes its scope broader than necessary. It is better to declare it within the loop’s scope:

1
2
3
4
5
6
7
void processArray(int* array, int size) {
    for (int i = 0; i < size; i++) {
        // Process array elements
    }

    // No risk of accidentally using `i` elsewhere
}

4. Properly Use Comments

Comments play an essential role in code documentation. They provide clarity and context, making it easier for other developers (including your future self) to understand your code. However, avoid stating the obvious and focus on adding comments where they convey additional insights or clarify complex logic.

1
2
// Increment the counter by 1
counter++; // Unnecessary comment

Comments that clarify the motivations or intentions behind a piece of code are much more valuable:

1
2
// We increment the counter here to keep track of the number of processed elements
counter++;

5. DRY (Don’t Repeat Yourself)

Avoid code duplication as it increases the maintenance burden and introduces the risk of inconsistency. Whenever you find yourself writing the same logic in multiple places, consider encapsulating that logic in a reusable function.

1
2
3
4
5
6
7
8
9
void processElement(int element) {
    // Common logic to be reused
}

void processArray(int* array, int size) {
    for (int i = 0; i < size; i++) {
        processElement(array[i]);
    }
}

Extracting common code into separate functions improves code readability, maintainability, and reduces the chances of introducing bugs.

Conclusion

By adopting these practices and principles, you can greatly improve the cleanliness and maintainability of your C code. Writing clean and maintainable code is not only beneficial during the initial development stage but also allows for smoother collaboration, easier debugging, and effortless future enhancements. Remember, code is read more often than it is written, so optimizing for readability and maintainability should always be a priority.


➡️ Getting Started with ESP32 Development


⬅️ Introduction to ESP32 Microcontroller


Go back to Posts.