C Programming

Complete Guide: Basics, Pointers, Memory Management & More

C Basics

C is a general-purpose, procedural programming language developed by Dennis Ritchie at Bell Labs in 1972. It's the foundation for many modern languages.

C
#include <stdio.h>

int main() {
    // This is a comment
    printf("Hello, World!\n");
    
    /* Multi-line
       comment */
    
    return 0;  // Exit status
}

// Compile: gcc program.c -o program
// Run: ./program

Input/Output

C
#include <stdio.h>

int main() {
    int age;
    char name[50];
    float salary;
    
    // Input
    printf("Enter your name: ");
    scanf("%s", name);          // No & for arrays
    
    printf("Enter your age: ");
    scanf("%d", &age);          // & for address
    
    printf("Enter salary: ");
    scanf("%f", &salary);
    
    // Output
    printf("Name: %s\n", name);
    printf("Age: %d\n", age);
    printf("Salary: %.2f\n", salary);
    
    return 0;
}

Data Types

TypeSizeFormatRange
char1 byte%c-128 to 127
unsigned char1 byte%c0 to 255
short2 bytes%hd-32,768 to 32,767
int4 bytes%d-2³¹ to 2³¹-1
unsigned int4 bytes%u0 to 2³²-1
long8 bytes%ld-2⁶³ to 2⁶³-1
float4 bytes%f3.4E-38 to 3.4E+38
double8 bytes%lf1.7E-308 to 1.7E+308
C
// Variable declaration
int age = 25;
float price = 99.99f;
double pi = 3.14159265359;
char grade = 'A';

// Constants
const int MAX = 100;
#define PI 3.14159

// Type modifiers
unsigned int positive = 100;
signed int negative = -50;
long long bigNum = 9223372036854775807LL;

// sizeof operator
printf("Size of int: %zu bytes\n", sizeof(int));
printf("Size of double: %zu bytes\n", sizeof(double));

// Type casting
int a = 5, b = 2;
float result = (float)a / b;  // 2.5, not 2

Control Flow

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

// Ternary operator
int max = (a > b) ? a : b;

// Switch statement
switch (grade) {
    case 'A':
        printf("Excellent!\n");
        break;
    case 'B':
        printf("Good!\n");
        break;
    default:
        printf("Try harder!\n");
}

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

// while loop
int count = 0;
while (count < 5) {
    printf("%d ", count);
    count++;
}

// do-while loop
do {
    printf("%d ", count);
    count--;
} while (count > 0);

// break and continue
for (int i = 0; i < 10; i++) {
    if (i == 3) continue;  // Skip 3
    if (i == 7) break;     // Stop at 7
    printf("%d ", i);
}

// goto (avoid if possible)
goto label;
// ...
label:
    printf("Jumped here\n");

Functions

C
// Function declaration (prototype)
int add(int a, int b);
void greet(char name[]);

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

void greet(char name[]) {
    printf("Hello, %s!\n", name);
}

// Pass by value (copy)
void increment(int x) {
    x++;  // Doesn't affect original
}

// Pass by reference (pointer)
void incrementRef(int *x) {
    (*x)++;  // Affects original
}

// Multiple return values using pointers
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// Array as parameter
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

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

// Usage
int main() {
    int result = add(3, 5);
    greet("Alice");
    
    int a = 5, b = 10;
    swap(&a, &b);  // Pass addresses
    
    int nums[] = {1, 2, 3, 4, 5};
    printArray(nums, 5);
    
    return 0;
}

Pointers

A pointer is a variable that stores the memory address of another variable.
& = Address-of operator | * = Dereference operator
C
// Basic pointers
int x = 10;
int *ptr = &x;        // ptr holds address of x

printf("Value of x: %d\n", x);         // 10
printf("Address of x: %p\n", &x);      // 0x7fff...
printf("Value of ptr: %p\n", ptr);     // Same address
printf("Value at ptr: %d\n", *ptr);    // 10 (dereferencing)

*ptr = 20;            // Modify value through pointer
printf("x is now: %d\n", x);  // 20

// Pointer arithmetic
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;         // Points to first element

printf("%d\n", *p);       // 10
printf("%d\n", *(p + 1)); // 20
printf("%d\n", *(p + 2)); // 30

p++;                  // Move to next element
printf("%d\n", *p);   // 20

// Array and pointers
// arr[i] is equivalent to *(arr + i)
// &arr[i] is equivalent to (arr + i)

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

// NULL pointer
int *null_ptr = NULL;
if (null_ptr == NULL) {
    printf("Pointer is NULL\n");
}

// Void pointer (generic pointer)
void *generic = &x;
printf("%d\n", *(int*)generic);  // Cast before dereference

// Function pointers
int add(int a, int b) { return a + b; }
int (*funcPtr)(int, int) = add;
printf("%d\n", funcPtr(3, 5));  // 8

Structures

C
// Structure definition
struct Person {
    char name[50];
    int age;
    float salary;
};

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

int main() {
    // Declaration and initialization
    struct Person p1 = {"Alice", 25, 50000.00};
    
    // Access members
    printf("Name: %s\n", p1.name);
    printf("Age: %d\n", p1.age);
    
    // Modify members
    p1.age = 26;
    
    // Array of structures
    struct Person people[3] = {
        {"Alice", 25, 50000},
        {"Bob", 30, 60000},
        {"Charlie", 35, 70000}
    };
    
    // Pointer to structure
    struct Person *ptr = &p1;
    printf("Name: %s\n", ptr->name);  // Arrow operator
    printf("Age: %d\n", (*ptr).age);  // Equivalent
    
    // Using typedef
    Point origin = {0, 0};
    
    // Nested structures
    struct Address {
        char city[50];
        int zip;
    };
    
    struct Employee {
        char name[50];
        struct Address addr;
    };
    
    struct Employee emp = {"John", {"NYC", 10001}};
    printf("City: %s\n", emp.addr.city);
    
    return 0;
}

Dynamic Memory

C
#include <stdlib.h>

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

// Use the array
for (int i = 0; i < 5; i++) {
    arr[i] = i * 10;
}

// calloc - allocate and initialize to 0
int *arr2 = (int*) calloc(5, sizeof(int));

// realloc - resize allocated memory
arr = (int*) realloc(arr, 10 * sizeof(int));

// free - deallocate memory
free(arr);
arr = NULL;  // Good practice: set to NULL after free

free(arr2);
arr2 = NULL;

// Dynamic 2D array
int rows = 3, cols = 4;
int **matrix = (int**) malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
    matrix[i] = (int*) malloc(cols * sizeof(int));
}

// Use matrix
matrix[1][2] = 100;

// Free 2D array (in reverse order)
for (int i = 0; i < rows; i++) {
    free(matrix[i]);
}
free(matrix);
Common Memory Errors:
• Memory leak (not calling free)
• Dangling pointer (using after free)
• Double free
• Buffer overflow

File I/O

C
#include <stdio.h>

// File modes: "r" read, "w" write, "a" append
//            "r+" read/write, "rb" binary read

// Writing to file
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
    printf("Error opening file!\n");
    return 1;
}

fprintf(file, "Hello, File!\n");
fprintf(file, "Number: %d\n", 42);
fputs("Another line\n", file);
fputc('X', file);

fclose(file);

// Reading from file
FILE *readFile = fopen("output.txt", "r");
if (readFile == NULL) {
    printf("Error opening file!\n");
    return 1;
}

char buffer[100];

// Read line by line
while (fgets(buffer, sizeof(buffer), readFile) != NULL) {
    printf("%s", buffer);
}

// Or read character by character
int ch;
while ((ch = fgetc(readFile)) != EOF) {
    putchar(ch);
}

// Read formatted input
int num;
char str[50];
fscanf(readFile, "%s %d", str, &num);

fclose(readFile);

// Binary file operations
struct Data {
    int id;
    char name[20];
};

struct Data data = {1, "Test"};

// Write binary
FILE *binFile = fopen("data.bin", "wb");
fwrite(&data, sizeof(struct Data), 1, binFile);
fclose(binFile);

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