C

Essential C programming syntax, pointers, memory management, and standard library functions.

languages
csystemsprogramminglow-level

Hello World

#include <stdio.h>               // Include standard I/O library

int main() {
    printf("Hello, World!\n");   // Print to console
    return 0;                    // Return success status
}

Compile and run:

gcc hello.c -o hello             # Compile with GCC
./hello                          # Execute program

clang hello.c -o hello           # Compile with Clang
./hello                          # Execute program

Variables & Types

// Basic data types
int age = 25;                    // Integer (typically 4 bytes)
float price = 19.99;             // Single-precision float (4 bytes)
double pi = 3.14159;             // Double-precision float (8 bytes)
char letter = 'A';               // Single character (1 byte)
char name[] = "Alice";           // String (array of chars)

// Type modifiers
unsigned int count = 100;        // Only positive values
long long big_num = 1234567890L; // Larger integer range
short small = 32767;             // Smaller integer range
const int MAX = 100;             // Constant (immutable)

// Type sizes
sizeof(int);                     // Returns size in bytes

Operators

// Arithmetic
int a = 10 + 5;                  // Addition
int b = 10 - 5;                  // Subtraction
int c = 10 * 5;                  // Multiplication
int d = 10 / 3;                  // Division (integer: 3)
int e = 10 % 3;                  // Modulus (remainder: 1)

// Increment/Decrement
a++;                             // Post-increment
++a;                             // Pre-increment
b--;                             // Post-decrement
--b;                             // Pre-decrement

// Comparison
a == b;                          // Equal to
a != b;                          // Not equal to
a < b;                           // Less than
a > b;                           // Greater than
a <= b;                          // Less than or equal
a >= b;                          // Greater than or equal

// Logical
(a && b);                        // AND
(a || b);                        // OR
!a;                              // NOT

// Bitwise
a & b;                           // AND
a | b;                           // OR
a ^ b;                           // XOR
~a;                              // NOT
a << 2;                          // Left shift
a >> 2;                          // Right shift

Control Flow

// If-else statement
if (age >= 18) {
    printf("Adult\n");
} else if (age >= 13) {
    printf("Teen\n");
} else {
    printf("Child\n");
}

// Ternary operator
int max = (a > b) ? a : b;       // Shorthand if-else

// Switch statement
switch (grade) {
    case 'A':
        printf("Excellent\n");
        break;                    // Prevent fall-through
    case 'B':
        printf("Good\n");
        break;
    default:
        printf("Keep trying\n");
}

// While loop
int i = 0;
while (i < 5) {
    printf("%d\n", i);
    i++;
}

// Do-while loop (executes at least once)
do {
    printf("%d\n", i);
    i++;
} while (i < 5);

// For loop
for (int i = 0; i < 5; i++) {
    printf("%d\n", i);
}

// Break and continue
for (int i = 0; i < 10; i++) {
    if (i == 5) break;           // Exit loop
    if (i % 2 == 0) continue;    // Skip to next iteration
    printf("%d\n", i);
}

Functions

// Function declaration (prototype)
int add(int a, int b);

// Function definition
int add(int a, int b) {
    return a + b;
}

// Void function (no return value)
void greet(char name[]) {
    printf("Hello, %s!\n", name);
}

// Function with default behavior
void print_number(int n) {
    if (n == 0) return;          // Early return
    printf("%d\n", n);
}

// Recursive function
int factorial(int n) {
    if (n <= 1) return 1;        // Base case
    return n * factorial(n - 1); // Recursive call
}

// Main function (program entry point)
int main(int argc, char *argv[]) {
    printf("Hello, World!\n");
    return 0;                    // Return status code
}

Pointers

// Pointer basics
int x = 10;
int *ptr = &x;                   // Pointer stores address of x
printf("%d\n", *ptr);            // Dereference: access value (10)
printf("%p\n", ptr);             // Print address

// Modifying through pointers
*ptr = 20;                       // Changes x to 20

// Null pointer
int *null_ptr = NULL;            // Safe initialization
if (null_ptr != NULL) {          // Always check before dereferencing
    printf("%d\n", *null_ptr);
}

// Pointer arithmetic
int arr[] = {1, 2, 3, 4, 5};
int *p = arr;                    // Points to first element
p++;                             // Move to next element
printf("%d\n", *p);              // Prints 2

// Double pointers
int **pp = &ptr;                 // Pointer to pointer
printf("%d\n", **pp);            // Double dereference

// Function pointers
int (*func_ptr)(int, int) = add; // Pointer to function
int result = func_ptr(5, 3);     // Call through pointer

Arrays

// Array declaration
int numbers[5];                  // Uninitialized array
int scores[5] = {90, 85, 88, 92, 78}; // Initialized array
int values[] = {1, 2, 3};        // Size inferred (3)

// Accessing elements
int first = scores[0];           // First element (index 0)
scores[2] = 95;                  // Modify element

// Array size
int size = sizeof(scores) / sizeof(scores[0]); // Calculate length

// Multidimensional arrays
int matrix[3][3] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
int value = matrix[1][2];        // Access element (6)

// Arrays and pointers
int *p = numbers;                // Array name is pointer to first element
printf("%d\n", *(p + 2));        // Access third element

Strings

// String declaration
char str1[] = "Hello";           // Array with null terminator
char str2[10] = "World";         // Fixed-size buffer
char *str3 = "Constant";         // Pointer to string literal

// String functions (string.h)
strlen(str1);                    // Length (excluding \0)
strcpy(str2, str1);              // Copy str1 to str2
strcat(str1, str2);              // Concatenate str2 to str1
strcmp(str1, str2);              // Compare (0 if equal)
strncpy(str2, str1, 5);          // Copy n characters
strncat(str1, str2, 3);          // Concatenate n characters

// String manipulation
char buffer[50];
sprintf(buffer, "Age: %d", 25);  // Format string into buffer
sscanf("42", "%d", &age);        // Parse string into variable

// Character operations (ctype.h)
isalpha('A');                    // Check if letter
isdigit('5');                    // Check if digit
toupper('a');                    // Convert to uppercase
tolower('A');                    // Convert to lowercase

Structs

// Struct definition
struct Person {
    char name[50];
    int age;
    float height;
};

// Struct initialization
struct Person person1 = {"Alice", 30, 5.6};
struct Person person2 = {.age = 25, .name = "Bob"}; // Designated initializers

// Accessing members
printf("%s\n", person1.name);    // Dot notation
person1.age = 31;                // Modify member

// Pointer to struct
struct Person *ptr = &person1;
printf("%s\n", ptr->name);       // Arrow notation for pointers
ptr->age = 32;                   // Modify through pointer

// Typedef for cleaner syntax
typedef struct {
    int x;
    int y;
} Point;

Point p1 = {10, 20};             // No need for "struct" keyword

// Nested structs
typedef struct {
    Point position;
    int radius;
} Circle;

Circle c = {{100, 200}, 50};
printf("%d\n", c.position.x);    // Access nested member

Memory Management

// Dynamic memory allocation
int *arr = (int *)malloc(5 * sizeof(int)); // Allocate memory
if (arr == NULL) {
    printf("Memory allocation failed\n");
    return 1;
}

// Using allocated memory
arr[0] = 10;
arr[1] = 20;

// Freeing memory
free(arr);                       // Always free allocated memory
arr = NULL;                      // Prevent dangling pointer

// Calloc (allocates and initializes to zero)
int *zeros = (int *)calloc(5, sizeof(int)); // 5 integers, all zero
free(zeros);

// Realloc (resize allocated memory)
int *resized = (int *)realloc(arr, 10 * sizeof(int)); // Expand to 10
if (resized != NULL) {
    arr = resized;
}
free(arr);

// Memory operations (string.h)
int buffer[10];
memset(buffer, 0, sizeof(buffer)); // Fill with value
memcpy(dest, src, size);         // Copy memory block
memmove(dest, src, size);        // Safe copy (handles overlap)
memcmp(buf1, buf2, size);        // Compare memory blocks

File I/O

// Opening files
FILE *file = fopen("data.txt", "r");  // Read mode
// Modes: "r" (read), "w" (write), "a" (append), "r+", "w+", "a+"
if (file == NULL) {
    perror("Error opening file");
    return 1;
}

// Reading from file
char buffer[100];
fgets(buffer, 100, file);        // Read line
fscanf(file, "%s %d", name, &age); // Formatted read
int ch = fgetc(file);            // Read single character

// Writing to file
FILE *out = fopen("output.txt", "w");
fprintf(out, "Name: %s\n", name); // Formatted write
fputs("Hello, World!\n", out);   // Write string
fputc('A', out);                 // Write character

// File positioning
fseek(file, 0, SEEK_SET);        // Move to beginning
fseek(file, 0, SEEK_END);        // Move to end
fseek(file, 10, SEEK_CUR);       // Move 10 bytes from current
long pos = ftell(file);          // Get current position
rewind(file);                    // Reset to beginning

// Closing files
fclose(file);                    // Always close files
fclose(out);

// Binary file I/O
struct Person p = {"Alice", 30, 5.6};
FILE *bin = fopen("data.bin", "wb");
fwrite(&p, sizeof(struct Person), 1, bin); // Write binary
fclose(bin);

FILE *bin_in = fopen("data.bin", "rb");
struct Person loaded;
fread(&loaded, sizeof(struct Person), 1, bin_in); // Read binary
fclose(bin_in);

Error Handling

Return Codes

// Function returning error codes
int divide(int a, int b, int *result) {
    if (b == 0) {
        return -1;               // Error code for division by zero
    }
    *result = a / b;
    return 0;                    // Success
}

// Using the function
int result;
if (divide(10, 2, &result) == 0) {
    printf("Result: %d\n", result);
} else {
    fprintf(stderr, "Error: Division by zero\n");
}

errno (Global Error Variable)

#include <errno.h>
#include <string.h>

// Opening a file with error checking
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
    printf("Error code: %d\n", errno);      // Print error number
    printf("Error: %s\n", strerror(errno)); // Convert to message
    perror("fopen");                        // Print error with prefix
    return 1;
}

// Common errno values
// ENOENT (2)    - No such file or directory
// EACCES (13)   - Permission denied
// ENOMEM (12)   - Out of memory
// EINVAL (22)   - Invalid argument
// EIO (5)       - Input/output error

perror() and strerror()

// perror() - prints error message with prefix
FILE *f = fopen("data.txt", "r");
if (f == NULL) {
    perror("Failed to open file"); // Prints: "Failed to open file: No such file or directory"
    return 1;
}

// strerror() - returns error message string
char *error_msg = strerror(errno);
fprintf(stderr, "Error: %s\n", error_msg);

// Custom error messages
if (age < 0) {
    fprintf(stderr, "Error: Invalid age %d\n", age);
    return EXIT_FAILURE;
}

assert() for Debugging

#include <assert.h>

// Assert fails if condition is false (debug only)
int array[10];
int index = 5;
assert(index >= 0 && index < 10);  // Verify bounds
array[index] = 42;

// Disable assertions in production
// Compile with -DNDEBUG flag: gcc -DNDEBUG file.c
assert(ptr != NULL);               // No-op in release builds

goto for Error Cleanup

// Clean up multiple resources on error
int process_file(const char *filename) {
    FILE *file = NULL;
    char *buffer = NULL;
    int result = -1;              // Assume failure
    
    file = fopen(filename, "r");
    if (file == NULL) {
        perror("fopen");
        goto cleanup;             // Jump to cleanup
    }
    
    buffer = malloc(1024);
    if (buffer == NULL) {
        fprintf(stderr, "Out of memory\n");
        goto cleanup;
    }
    
    // Process file...
    result = 0;                   // Success
    
cleanup:
    free(buffer);                 // Safe even if NULL
    if (file != NULL) fclose(file);
    return result;
}

Custom Error Enums

// Define custom error codes
typedef enum {
    SUCCESS = 0,
    ERR_FILE_NOT_FOUND = 1,
    ERR_OUT_OF_MEMORY = 2,
    ERR_INVALID_INPUT = 3,
    ERR_NETWORK_FAILURE = 4
} ErrorCode;

// Function using custom error codes
ErrorCode read_config(const char *path) {
    FILE *f = fopen(path, "r");
    if (f == NULL) {
        return ERR_FILE_NOT_FOUND;
    }
    
    char *buffer = malloc(512);
    if (buffer == NULL) {
        fclose(f);
        return ERR_OUT_OF_MEMORY;
    }
    
    // Process config...
    
    free(buffer);
    fclose(f);
    return SUCCESS;
}

// Convert error code to message
const char *error_to_string(ErrorCode err) {
    switch (err) {
        case SUCCESS: return "Success";
        case ERR_FILE_NOT_FOUND: return "File not found";
        case ERR_OUT_OF_MEMORY: return "Out of memory";
        case ERR_INVALID_INPUT: return "Invalid input";
        case ERR_NETWORK_FAILURE: return "Network failure";
        default: return "Unknown error";
    }
}

Error Handling Patterns

// Pattern 1: Check and return early
int safe_divide(int a, int b, int *result) {
    if (result == NULL) return -1;     // Null pointer check
    if (b == 0) return -1;             // Division by zero
    *result = a / b;
    return 0;
}

// Pattern 2: Cascading checks with goto
int init_resources(void) {
    void *res1 = NULL, *res2 = NULL, *res3 = NULL;
    
    res1 = malloc(100);
    if (!res1) goto error;
    
    res2 = malloc(200);
    if (!res2) goto error;
    
    res3 = malloc(300);
    if (!res3) goto error;
    
    return 0;

error:
    free(res3);
    free(res2);
    free(res1);
    return -1;
}

// Pattern 3: Wrapper functions
FILE *safe_fopen(const char *path, const char *mode) {
    FILE *f = fopen(path, mode);
    if (f == NULL) {
        fprintf(stderr, "Failed to open '%s': %s\n", 
                path, strerror(errno));
    }
    return f;
}

// Pattern 4: Defensive programming
void process_array(int *arr, size_t len) {
    if (arr == NULL || len == 0) {
        fprintf(stderr, "Invalid input\n");
        return;
    }
    
    for (size_t i = 0; i < len; i++) {
        // Process each element...
    }
}

Preprocessor

// Include directives
#include <stdio.h>               // Standard library
#include "myheader.h"            // Local header file

// Macro definitions
#define PI 3.14159               // Constant macro
#define MAX(a, b) ((a) > (b) ? (a) : (b)) // Function-like macro
#define SQUARE(x) ((x) * (x))    // Always use parentheses

// Conditional compilation
#ifdef DEBUG
    printf("Debug mode\n");
#endif

#ifndef HEADER_H
#define HEADER_H
    // Header file content
#endif

#if defined(LINUX)
    // Linux-specific code
#elif defined(WINDOWS)
    // Windows-specific code
#else
    // Default code
#endif

// Predefined macros
__FILE__                         // Current file name
__LINE__                         // Current line number
__DATE__                         // Compilation date
__TIME__                         // Compilation time

// Stringification
#define STRINGIFY(x) #x
printf("%s\n", STRINGIFY(Hello)); // Prints "Hello"

// Token pasting
#define CONCAT(a, b) a##b
int CONCAT(var, 123) = 10;       // Creates var123

Standard Library Functions

// stdio.h - Input/Output
printf("Format: %d\n", value);   // Print to stdout
scanf("%d", &value);             // Read from stdin
getchar();                       // Read single character
putchar('A');                    // Write single character
puts("Hello");                   // Print string with newline

// stdlib.h - Utilities
atoi("123");                     // String to int
atof("3.14");                    // String to float
abs(-5);                         // Absolute value
rand();                          // Random number
srand(time(NULL));               // Seed random generator
exit(0);                         // Exit program

// math.h - Mathematics
sqrt(16);                        // Square root
pow(2, 3);                       // Power (2^3)
ceil(3.2);                       // Round up (4)
floor(3.8);                      // Round down (3)
sin(angle);                      // Sine
cos(angle);                      // Cosine

// time.h - Date/Time
time_t now = time(NULL);         // Current time
struct tm *local = localtime(&now); // Convert to local time
char timestr[26];
strftime(timestr, 26, "%Y-%m-%d", local); // Format time

Common Patterns

// Error handling
if (ptr == NULL) {
    fprintf(stderr, "Error: %s\n", strerror(errno));
    return EXIT_FAILURE;
}

// Command-line arguments
int main(int argc, char *argv[]) {
    for (int i = 0; i < argc; i++) {
        printf("Arg %d: %s\n", i, argv[i]);
    }
    return 0;
}

// Dynamic array
typedef struct {
    int *data;
    int size;
    int capacity;
} DynamicArray;

void append(DynamicArray *arr, int value) {
    if (arr->size >= arr->capacity) {
        arr->capacity *= 2;
        arr->data = realloc(arr->data, arr->capacity * sizeof(int));
    }
    arr->data[arr->size++] = value;
}

// Swap function
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

Compiler Comparison

FeatureGCCClangMSVCTCC
PlatformLinux, macOS, Windows (MinGW)Linux, macOS, WindowsWindows onlyCross-platform
StandardsC89, C99, C11, C17, C23C89, C99, C11, C17, C23C89, C99, C11, C17C89, C99
OptimizationExcellentExcellentExcellentBasic
Compile SpeedModerateFastModerateVery fast
Error MessagesGoodExcellent (most detailed)GoodBasic
DebuggingGDB integrationLLDB integrationVisual Studio debuggerLimited
LicenseGPLApache 2.0ProprietaryLGPL
Best ForGeneral use, Linux devModern tooling, diagnosticsWindows native appsQuick prototyping

Common compiler flags:

gcc -Wall -Wextra file.c         # Enable all warnings
gcc -O2 file.c                   # Optimization level 2
gcc -g file.c                    # Include debug symbols
gcc -std=c11 file.c              # Use C11 standard
gcc -o output file.c             # Specify output name