# C Programming Guidelines

## 1. Naming Conventions

### 1.1 Use snake_case for variables and functions
- Names should be descriptive but concise
- Use lowercase with underscores for separation

✅ **Good:**
```c
int user_count = 0;
float calculate_average(int *values, int count);
char *get_user_input(void);
```

❌ **Bad:**
```c
int UserCount = 0;      // Uses PascalCase instead of snake_case
float calcAvg(int *v, int c);  // Unclear abbreviated names
char *input(void);      // Too vague
```

### 1.2 Use UPPERCASE for constants and macros
- All capitals with underscores for separation

✅ **Good:**
```c
#define MAX_BUFFER_SIZE 1024
#define PI 3.14159
const int ERROR_CODE_FILE_NOT_FOUND = -1;
```

❌ **Bad:**
```c
#define maxbuffersize 1024  // Should be uppercase with underscores
#define Pi 3.14159          // Should be all uppercase
const int errorCode = -1;   // Should be uppercase with underscores
```

### 1.3 Use PascalCase for types
- First letter of each word should be capitalized
- This applies to struct, enum, union, and typedef names

✅ **Good:**
```c
typedef struct {
    int x;
    int y;
} Point;

typedef enum {
    FileStatusOk,
    FileStatusNotFound,
    FileStatusPermissionDenied
} FileStatus;
```

❌ **Bad:**
```c
typedef struct {
    int x;
    int y;
} point;  // Should start with capital P

typedef enum {
    FILE_STATUS_OK,  // Should use PascalCase, not screaming snake case for enum values
    FILE_STATUS_NOT_FOUND,
    FILE_STATUS_PERMISSION_DENIED
} file_status;  // Should start with capital F
```

## 2. Code Formatting

### 2.1 Use consistent indentation
- 4 spaces is the most common choice
- Be consistent throughout your project

✅ **Good:**
```c
if (condition) {
    printf("Condition is true\n");
    if (another_condition) {
        printf("Both conditions are true\n");
    }
}
```

❌ **Bad:**
```c
if (condition) {
  printf("Condition is true\n");
    if (another_condition) {
     printf("Both conditions are true\n");
        }
}
```

### 2.2 Use consistent brace style
- K&R style (opening brace on same line) is common in C
- Whatever style you choose, be consistent

✅ **Good (K&R style):**
```c
if (condition) {
    statement1;
    statement2;
} else {
    statement3;
    statement4;
}
```

✅ **Also Good (Allman style, if used consistently):**
```c
if (condition)
{
    statement1;
    statement2;
}
else
{
    statement3;
    statement4;
}
```

❌ **Bad (mixing styles):**
```c
if (condition) {
    statement1;
    statement2;
}
else {
    statement3;
    statement4;
}
```

### 2.3 Use proper spacing around operators and keywords
- Add spaces after commas and around operators
- Add space after keywords like if, while, for, but not after function names

✅ **Good:**
```c
for (int i = 0; i < 10; i++) {
    result = calculate(a + b, c * d);
    if (result > threshold) {
        break;
    }
}
```

❌ **Bad:**
```c
for(int i=0;i<10;i++){
    result=calculate(a+b,c*d);
    if(result>threshold){
        break;
    }
}
```

## 3. Function Guidelines

### 3.1 Keep functions short and focused
- Each function should have a single responsibility
- Aim for 20-30 lines maximum when possible

✅ **Good:**
```c
int validate_input(const char *input) {
    if (input == NULL) {
        return ERROR_NULL_INPUT;
    }
    
    if (strlen(input) > MAX_INPUT_LENGTH) {
        return ERROR_INPUT_TOO_LONG;
    }
    
    if (!contains_valid_chars(input)) {
        return ERROR_INVALID_CHARS;
    }
    
    return INPUT_VALID;
}
```

❌ **Bad:**
```c
int process_input(const char *input) {
    // 100+ lines of code that:
    // - validates input
    // - parses input
    // - processes data
    // - updates database
    // - generates report
    // - handles errors
    // All mixed together
}
```

### 3.2 Use clear parameter names and document function behavior
- Input parameters should come first, output parameters last
- Use consistent return value semantics

✅ **Good:**
```c
/**
 * Splits a string into tokens based on a delimiter.
 *
 * @param str String to tokenize (modified by this function)
 * @param delim Characters that serve as delimiters
 * @return Pointer to the next token or NULL if no more tokens found
 */
char *tokenize_string(char *str, const char *delim) {
    // Implementation
}
```

❌ **Bad:**
```c
// No documentation, unclear parameter names
char *ts(char *s, const char *d) {
    // Implementation
}
```

### 3.3 Check return values and handle errors
- Always check return values of functions that can fail
- Have explicit error paths

✅ **Good:**
```c
FILE *file = fopen("data.txt", "r");
if (file == NULL) {
    fprintf(stderr, "Error opening file: %s\n", strerror(errno));
    return ERROR_FILE_OPEN_FAILED;
}

// Use the file

fclose(file);
```

❌ **Bad:**
```c
FILE *file = fopen("data.txt", "r");
// No error checking!

// Use the file - potential NULL pointer dereference

fclose(file);
```

## 4. Memory Management

### 4.1 Always pair allocation with deallocation
- Every `malloc()` needs a matching `free()`
- Check for allocation failures

✅ **Good:**
```c
int *data = (int *)malloc(size * sizeof(int));
if (data == NULL) {
    fprintf(stderr, "Memory allocation failed\n");
    return ERROR_MEMORY_ALLOCATION;
}

// Use the allocated memory

free(data);
data = NULL;  // Prevent use after free
```

❌ **Bad:**
```c
int *data = (int *)malloc(size * sizeof(int));
// No NULL check!

// Use the allocated memory

// No free - memory leak!
```

### 4.2 Use consistent resource management patterns
- Release resources in reverse order of acquisition
- Consider using goto for error cleanup in C

✅ **Good:**
```c
int process_data(void) {
    FILE *input_file = NULL;
    FILE *output_file = NULL;
    char *buffer = NULL;
    int result = SUCCESS;
    
    buffer = (char *)malloc(BUFFER_SIZE);
    if (buffer == NULL) {
        result = ERROR_MEMORY_ALLOCATION;
        goto cleanup;
    }
    
    input_file = fopen("input.txt", "r");
    if (input_file == NULL) {
        result = ERROR_INPUT_FILE_OPEN;
        goto cleanup;
    }
    
    output_file = fopen("output.txt", "w");
    if (output_file == NULL) {
        result = ERROR_OUTPUT_FILE_OPEN;
        goto cleanup;
    }
    
    // Process data using all resources
    
cleanup:
    // Clean up in reverse order
    if (output_file != NULL) fclose(output_file);
    if (input_file != NULL) fclose(input_file);
    free(buffer);
    
    return result;
}
```

❌ **Bad:**
```c
int process_data(void) {
    // Inconsistent error handling and resource cleanup
    char *buffer = (char *)malloc(BUFFER_SIZE);
    FILE *input_file = fopen("input.txt", "r");
    
    if (input_file == NULL) {
        free(buffer);
        return ERROR_INPUT_FILE_OPEN;
    }
    
    FILE *output_file = fopen("output.txt", "w");
    // Forgot to check if output_file is NULL
    
    // Process data
    
    fclose(input_file);
    fclose(output_file);  // Might be NULL!
    // Forgot to free buffer - memory leak!
    
    return SUCCESS;
}
```

## 5. Header Files

### 5.1 Always use include guards
- Prevent multiple inclusion with include guards
- Use a unique name based on file path

✅ **Good:**
```c
#ifndef PROJECT_MODULE_HEADER_H
#define PROJECT_MODULE_HEADER_H

// Header content

#endif /* PROJECT_MODULE_HEADER_H */
```

❌ **Bad:**
```c
// No include guard - can cause multiple definition errors

typedef struct {
    int x;
    int y;
} Point;

void draw_point(Point p);
```

### 5.2 Organize includes consistently
- Include system headers first, then project headers
- Group related declarations

✅ **Good:**
```c
/* System includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Project includes */
#include "project/common.h"
#include "project/utils.h"

/* Module-specific includes */
#include "module/internal.h"
```

❌ **Bad:**
```c
#include "module/internal.h"
#include <string.h>
#include "project/common.h"
#include <stdio.h>
#include "project/utils.h"
#include <stdlib.h>
```

### 5.3 Declare what you use, use what you declare
- Include only what you need
- Don't rely on indirect includes

✅ **Good:**
```c
// In file.h
#ifndef FILE_H
#define FILE_H

#include <stddef.h>  // For size_t

// Declare only what other modules need
size_t read_file(const char *filename, char *buffer, size_t buffer_size);

#endif /* FILE_H */

// In file.c
#include "file.h"
#include <stdio.h>   // For FILE, fopen, etc.
#include <string.h>  // For strerror

size_t read_file(const char *filename, char *buffer, size_t buffer_size) {
    // Implementation using stdio functions
}
```

❌ **Bad:**
```c
// In file.h
#ifndef FILE_H
#define FILE_H

#include <stdio.h>    // Exposing implementation details
#include <string.h>   // Not needed in the interface
#include <stdlib.h>   // Not needed in the interface

// Using stdio in the interface
FILE *open_file(const char *filename);
size_t read_file(FILE *file, char *buffer, size_t buffer_size);

#endif /* FILE_H */
```

## 6. Comments and Documentation

### 6.1 Document function interfaces
- Describe purpose, parameters, return values, and side effects
- Document error conditions

✅ **Good:**
```c
/**
 * Searches for a substring within a string.
 *
 * @param haystack  The string to search in
 * @param needle    The substring to search for
 * @return Pointer to the first occurrence of needle in haystack,
 *         or NULL if needle is not found
 */
char *find_substring(const char *haystack, const char *needle) {
    // Implementation
}
```

❌ **Bad:**
```c
// Finds a string
char *find_substring(const char *s1, const char *s2) {
    // Implementation
}
```

### 6.2 Comment complex or non-obvious code
- Explain the "why" not the "what"
- Keep comments up to date with code changes

✅ **Good:**
```c
// Use binary search to find insertion point while maintaining ordering
// This is O(log n) rather than the O(n) approach of linear search
int insertion_index = find_insertion_point(array, value, 0, array_size - 1);

// Special case: The XDR protocol requires 4-byte alignment for data
// so we pad the buffer with zeros as needed
size_t padding = (4 - (buffer_size % 4)) % 4;
memset(buffer + buffer_size, 0, padding);
```

❌ **Bad:**
```c
// This function finds the substring
char *p = strstr(s, sub);

// Increment i
i++;

// Too much detail about "what" the code does, not "why"
// Loop through the array and add each element to sum
int sum = 0;
for (int i = 0; i < size; i++) {
    sum += array[i];  // Add element to sum
}
```

## 7. Error Handling

### 7.1 Use consistent error reporting
- Choose one approach (return codes, errno, error objects) and stick with it
- Document error codes

✅ **Good:**
```c
/**
 * Error codes for the file module
 */
#define FILE_ERROR_NONE              0
#define FILE_ERROR_NOT_FOUND        -1
#define FILE_ERROR_PERMISSION       -2
#define FILE_ERROR_INVALID_FORMAT   -3

/**
 * Opens a configuration file and validates its format.
 *
 * @param filename The path to the config file
 * @return FILE_ERROR_NONE on success, or appropriate error code on failure
 */
int open_config_file(const char *filename) {
    if (access(filename, F_OK) != 0) {
        return FILE_ERROR_NOT_FOUND;
    }
    
    if (access(filename, R_OK) != 0) {
        return FILE_ERROR_PERMISSION;
    }
    
    // Check file format
    if (!is_valid_config_format(filename)) {
        return FILE_ERROR_INVALID_FORMAT;
    }
    
    return FILE_ERROR_NONE;
}
```

❌ **Bad:**
```c
// Inconsistent error reporting
int open_file(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        printf("Error opening file\n");
        return -1;  // Generic error code
    }
    
    if (!is_valid_format(file)) {
        fprintf(stderr, "Invalid format\n");
        fclose(file);
        exit(1);  // Abrupt program termination!
    }
    
    // No error, but doesn't return the file handle!
    fclose(file);
    return 0;
}
```

### 7.2 Validate function inputs
- Check parameters for validity at the beginning of functions
- Return early for invalid inputs

✅ **Good:**
```c
int calculate_average(const int *values, size_t count, double *result) {
    // Validate inputs
    if (values == NULL || result == NULL) {
        return ERROR_NULL_POINTER;
    }
    
    if (count == 0) {
        return ERROR_DIVISION_BY_ZERO;
    }
    
    // Calculation is safe now
    double sum = 0;
    for (size_t i = 0; i < count; i++) {
        sum += values[i];
    }
    
    *result = sum / count;
    return SUCCESS;
}
```

❌ **Bad:**
```c
double calculate_average(int *values, int count) {
    // No input validation!
    
    double sum = 0;
    for (int i = 0; i < count; i++) {
        sum += values[i];  // Potential NULL pointer dereference!
    }
    
    return sum / count;  // Potential division by zero!
}
```

## 8. Defensive Programming

### 8.1 Check array bounds
- Never access arrays out of bounds
- Use size parameters to track array sizes

✅ **Good:**
```c
/**
 * Finds the maximum value in an array.
 */
int find_maximum(const int *array, size_t size) {
    if (array == NULL || size == 0) {
        return INT_MIN;  // Error value
    }
    
    int max = array[0];
    for (size_t i = 1; i < size; i++) {
        if (array[i] > max) {
            max = array[i];
        }
    }
    
    return max;
}
```

❌ **Bad:**
```c
int find_maximum(int *array, int size) {
    // No validation on array or size
    
    int max = array[0];  // Crash if array is NULL or size is 0
    
    // Could go out of bounds if size is incorrect
    for (int i = 1; i <= size; i++) {  // Note: <= is wrong!
        if (array[i] > max) {
            max = array[i];
        }
    }
    
    return max;
}
```

### 8.2 Initialize variables before use
- Always initialize variables
- Don't rely on default initialization

✅ **Good:**
```c
void process_data(void) {
    int count = 0;
    double total = 0.0;
    char buffer[MAX_SIZE] = {0};  // Zero-initialize array
    
    // Variables are initialized before use
}
```

❌ **Bad:**
```c
void process_data(void) {
    int count;      // Uninitialized
    double total;   // Uninitialized
    char buffer[MAX_SIZE];  // Uninitialized
    
    // Using variables without initialization
    total = total + 10.0;  // Undefined behavior
}
```

## 9. Performance Considerations

### 9.1 Prefer readability unless performance is critical
- Write clear, readable code first
- Optimize only when necessary and based on profiling

✅ **Good:**
```c
// Clear and readable implementation
double calculate_average(const int *values, size_t count) {
    if (values == NULL || count == 0) {
        return 0.0;
    }
    
    double sum = 0.0;
    for (size_t i = 0; i < count; i++) {
        sum += values[i];
    }
    
    return sum / count;
}
```

❌ **Bad (premature optimization):**
```c
// Trying to be clever with unnecessary optimization
double calculate_average(const int *values, size_t count) {
    if (!values || !count) return 0.0;
    
    register double sum = 0.0;  // 'register' rarely helps modern compilers
    register size_t i = count;
    
    // Reversed loop for "performance" but harder to understand
    while (i--) {
        sum += values[i];
    }
    
    return sum / count;
}
```

### 9.2 Document performance-critical code
- Explain the reasoning behind optimization
- Note any assumptions that affect performance

✅ **Good:**
```c
/**
 * Fast string hash function optimized for short strings.
 * Uses FNV-1a algorithm which has good distribution and
 * is quick for strings under 100 bytes.
 * 
 * Time complexity: O(n) where n is string length
 * Performance assumption: Most strings are under 20 chars
 */
unsigned int hash_string(const char *str) {
    const unsigned int FNV_PRIME = 16777619;
    const unsigned int FNV_OFFSET_BASIS = 2166136261;
    
    unsigned int hash = FNV_OFFSET_BASIS;
    
    while (*str) {
        hash ^= (unsigned char)*str++;
        hash *= FNV_PRIME;
    }
    
    return hash;
}
```

❌ **Bad:**
```c
// No documentation of algorithm choice or performance characteristics
unsigned int hash_string(const char *str) {
    unsigned int hash = 2166136261;
    
    while (*str) {
        hash ^= (unsigned char)*str++;
        hash *= 16777619;
    }
    
    return hash;
}
```