Built with Tutify.io

    AIStudyLabs

    Best Coding Practices in C

    Best Coding Practices in C

    Introduction

    You now know how to write working C programs. This lesson focuses on writing good C programs: readable, maintainable, and robust. We’ll cover:


    Naming Conventions in C

    Clear names make code self-explanatory and reduce the need for comments and debugging.

    General Guidelines

    Example: Consistent Naming

    #include <stdio.h>
    
    #define MAX_LINE_LENGTH 256
    
    int read_line(FILE *input, char *buffer, int max_length);
    
    int main(void) {
        char line_buffer[MAX_LINE_LENGTH];
        int line_length;
    
        line_length = read_line(stdin, line_buffer, MAX_LINE_LENGTH);
    
        if (line_length > 0) {
            printf("Read %d characters: %s\n", line_length, line_buffer);
        }
    
        return 0;
    }
    
    int read_line(FILE *input, char *buffer, int max_length) {
        if (fgets(buffer, max_length, input) == NULL) {
            return 0; // indicate no characters read
        }
    
        int length = 0;
        while (buffer[length] != '\0') {
            length++;
        }
    
        return length;
    }
    
    // Output (example)
    // Read 5 characters: hello
    

    Explanation:


    Modular Programming with Header Files

    You’ve already used header files; now we’ll focus on best practices when splitting code into multiple files.

    Organizing Code into Modules

    Example: Simple Logger Module

    logger.h

    #ifndef LOGGER_H
    #define LOGGER_H
    
    #include <stdio.h>
    
    void log_info(const char *message);
    void log_error(const char *message);
    
    #endif // LOGGER_H
    

    logger.c

    #include "logger.h"
    
    static void log_with_prefix(const char *prefix, const char *message) {
        fprintf(stderr, "%s: %s\n", prefix, message);
    }
    
    void log_info(const char *message) {
        log_with_prefix("INFO", message);
    }
    
    void log_error(const char *message) {
        log_with_prefix("ERROR", message);
    }
    

    main.c

    #include "logger.h"
    
    int main(void) {
        log_info("Application started");
        log_error("Unable to open configuration file");
        return 0;
    }
    
    // Output
    // INFO: Application started
    // ERROR: Unable to open configuration file
    

    Key points:


    Commenting for Readability

    Comments should explain why, not restate what the code already says.

    Good Commenting Practices

    Example: Helpful vs. Redundant Comments

    #include <stdio.h>
    
    /*
     * Returns 1 if year is a leap year according to the Gregorian calendar,
     * otherwise returns 0.
     */
    int is_leap_year(int year) {
        // Year divisible by 400 is always a leap year
        if (year % 400 == 0) return 1;
    
        // Year divisible by 100 but not 400 is not a leap year
        if (year % 100 == 0) return 0;
    
        // Year divisible by 4 but not 100 is a leap year
        if (year % 4 == 0) return 1;
    
        // All other years are not leap years
        return 0;
    }
    
    int main(void) {
        int year = 2000;
        if (is_leap_year(year)) {
            printf("%d is a leap year\n", year);
        } else {
            printf("%d is not a leap year\n", year);
        }
        return 0;
    }
    
    // Output
    // 2000 is a leap year
    

    Notice there are no comments like // check if divisible by 400 right next to year % 400 == 0—the code is clear enough.


    Defensive Programming Techniques

    Defensive programming means assuming things will go wrong and handling them gracefully.

    Core Techniques

    Example: Validating Inputs and Using assert

    #include <stdio.h>
    #include <assert.h>
    
    int safe_divide(int numerator, int denominator, int *result) {
        if (result == NULL) {
            return 0; // invalid output pointer
        }
        if (denominator == 0) {
            return 0; // cannot divide by zero
        }
    
        *result = numerator / denominator;
        return 1; // success
    }
    
    int main(void) {
        int value;
        int ok;
    
        ok = safe_divide(10, 2, &value);
        assert(ok); // should be true in this scenario
        printf("10 / 2 = %d\n", value);
    
        ok = safe_divide(10, 0, &value);
        if (!ok) {
            printf("Error: division by zero\n");
        }
    
        ok = safe_divide(10, 2, NULL);
        if (!ok) {
            printf("Error: result pointer is NULL\n");
        }
    
        return 0;
    }
    
    // Output
    // 10 / 2 = 5
    // Error: division by zero
    // Error: result pointer is NULL
    

    Practices shown:


    Memory Management Best Practices

    In C, memory errors are a major source of bugs. Good habits dramatically reduce leaks and crashes.

    Key Rules

    Example: Safe Allocation and Cleanup

    #include <stdio.h>
    #include <stdlib.h>
    
    int read_integers(const char *filename, int **out_array, size_t *out_count) {
        if (filename == NULL || out_array == NULL || out_count == NULL) {
            return 0;
        }
    
        FILE *file = fopen(filename, "r");
        if (file == NULL) {
            return 0;
        }
    
        size_t capacity = 4;
        size_t count = 0;
        int *data = malloc(capacity * sizeof(int));
        if (data == NULL) {
            fclose(file);
            return 0;
        }
    
        while (1) {
            int value;
            int read = fscanf(file, "%d", &value);
            if (read != 1) break;
    
            if (count == capacity) {
                size_t new_capacity = capacity * 2;
                int *temp = realloc(data, new_capacity * sizeof(int));
                if (temp == NULL) {
                    free(data);
                    fclose(file);
                    return 0;
                }
                data = temp;
                capacity = new_capacity;
            }
    
            data[count++] = value;
        }
    
        fclose(file);
    
        *out_array = data;
        *out_count = count;
        return 1;
    }
    
    int main(void) {
        int *numbers = NULL;
        size_t count = 0;
    
        if (!read_integers("numbers.txt", &numbers, &count)) {
            printf("Failed to read integers.\n");
            return 1;
        }
    
        printf("Read %zu integers:\n", count);
        for (size_t i = 0; i < count; ++i) {
            printf("%d ", numbers[i]);
        }
        printf("\n");
    
        free(numbers);
        numbers = NULL;
    
        return 0;
    }
    
    // Example numbers.txt
    // 1 2 3 4 5
    
    // Output
    // Read 5 integers:
    // 1 2 3 4 5
    

    Good practices illustrated:


    Summary / Key Takeaways

    In this lesson on Best Coding Practices in C, you have learned:

    C Programming Fundamentals: From Zero to Confident C Coder — Advanced C Concepts and Best Practices

    Introduction to Programming and C

    Variables and Data Types

    Control Structures: Conditions and Loops

    Functions in C

    Arrays and Strings

    Working with Pointers

    File Handling in C

    Structures and Unions in C

    Project Development and Implementation

    Advanced C Concepts and Best Practices

    Tutorial Not Found