C Programming

Pointer Arithmetic; Dynamic Memory Allocation; Structures in C

C-CAT

Pointer Arithmetic

Valid Pointer Operations

int arr[] = {10, 20, 30, 40, 50};
int *p = arr;    // p points to arr[0]

// Pointer + integer: moves pointer n elements forward
p++;             // p now points to
arr[1]
printf("%d\n", *p);   // 20

p += 2;          // p now points to arr[3]
printf("%d\n", *p);   // 40

// Pointer - pointer: gives number of elements between
int *p1 = &arr[0]; 
int *p2 =
&arr[4];
ptrdiff_t diff = p2 - p1;
printf("Distance: %td\n", diff);  // 4

// Pointer comparison
int *pa = arr;
int *pb = arr + 3;
if (pb > pa) printf("pb is ahead of pa\n");

Pointer with sizeof

int x = 10;
int *p = &x;

printf("sizeof(int) = %zu\n", sizeof(int));       // 4
printf("sizeof(p) = %zu\n", sizeof(p));            // 8 (64-bit pointer)
printf("sizeof(*p) = %zu\n", sizeof(*p));          // 4 (sizeof int)

Dynamic Memory Allocation

Why Dynamic Memory?

  • Array size must be known at compile time (static allocation)
  • Dynamic allocation allows size to be determined at runtime
  • Memory allocated from the heap (large pool)

malloc — Memory Allocation

#include <stdlib.h>

// malloc(size_in_bytes) → returns void*; uninitialized memory
int *arr = (int *) malloc(5 *
sizeof(int));

if (arr == NULL) {
    printf("Memory allocation failed!\n");
    exit(1);
}

// Use the array
for (int i = 0; i < 5; i++) {
    arr[i] = i * 10;
}
for (int i = 0; i < 5;
i++) {
    printf("%d ", arr[i]);   // 0 10 20 30 40
}

// ALWAYS free when done!
free(arr);
arr = NULL;   // good practice: set to NULL after free

calloc — Contiguous Allocation

// calloc(count, size_each) → returns void*; ZERO-initialized memory
int *arr = (int *) calloc(5, sizeof(int));
// All elements are initialized to 0

for (int i = 0; i < 5; i++) {
    printf("%d ", arr[i]);   // 0 0 0 0 0
}
free(arr);

realloc — Resize Allocation

int *arr = (int *) malloc(5 * sizeof(int));
// ... use arr with 5 elements ...

// Need more space: resize to 10 elements
arr = (int *) realloc(arr, 10 * sizeof(int));
if (arr == NULL) {
    printf("Realloc failed!\n");
    exit(1);
}
// arr can now hold 10 elements
free(arr);

Dynamic 2D Array

// Allocate n x m 2D array dynamically
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 it like a normal 2D array
matrix[0][0] = 1;
matrix[1][2] = 42;

// Free: first free each row, then the array of pointers
for (int i = 0; i < rows; i++) {
    free(matrix[i]);
}
free(matrix);

Memory Leaks

A memory leak occurs when dynamically allocated memory is never freed.

void bad_function() {
    int *p = malloc(100);
    // ... forgot to free(p) ...
    return;    // memory is LEAKED! 100 bytes lost
}

void good_function() {
    int *p = malloc(100);
    // ... use p ...
    free(p);   // Always free before returning
}

Structures in C

What is a Structure?

A structure groups different data types into a single unit.

#include <stdio.h>
#include <string.h>

// Define structure
struct student {
    int rollno;
    char name[50];
    float gpa;
char grade;
};

// typedef makes usage cleaner
typedef struct student student_t;

// OR defined together:
typedef struct {
    int rollno;
    char name[50];
    float gpa;
char grade;
} student_t;

int main() {
    // Declare and initialize
    student_t s1 = {101, "Alice", 3.8, 'A'};

    // Access members using dot operator
    printf("Roll: %d\n", s1.rollno);
    printf("Name: %s\n", s1.name);
    printf("GPA: %.1f\n", s1.gpa);

    // Modify
    s1.gpa = 3.9;
    strcpy(s1.name, "Alice Johnson");

    // Another way to initialize
    student_t s2;
    s2.rollno = 102;
    strcpy(s2.name, "Bob");
    s2.gpa = 3.5;
    s2.grade = 'B';

    return 0;
}

Array of Structures

#define MAX 100

student_t students[MAX];
int count = 0;

void add_student(int rollno, const char *name, float gpa) {
    students[count].rollno =
rollno;
    strcpy(students[count].name, name);
    students[count].gpa = gpa;
    count++;
}

void print_all() {
    for (int i = 0; i < count; i++) {
        printf("Roll: %d, Name: %s, GPA: %.1f\n",
               students[i].rollno, students[i].name, students[i].gpa);
    }
}

Pointer to Structure

student_t s = {101, "Alice", 3.8, 'A'};
student_t *ptr = &s;

// Two ways to access members via pointer:
printf("%s\n", (*ptr).name);   // Using * and .
printf("%s\n", ptr->name);     // Using arrow operator -> (more common)

ptr->gpa = 4.0;   // modify via pointer

Structures as Function Arguments

// Pass by value (copy): changes don't affect original
void print_student(student_t s) {
    printf("Name: %s, GPA: %.1f\n", s.name, s.gpa);
}

// Pass by pointer (reference): changes affect original
void update_gpa(student_t *s, float
new_gpa) {
    s->gpa = new_gpa;
}

// Return structure from function
student_t create_student(int roll, const char *name, float gpa) {
    student_t s;
    s.rollno = roll;
    strcpy(s.name, name);
    s.gpa = gpa;
    return s;
}

Nested Structures

typedef struct {
    int day, month, year;
} date_t;

typedef struct {
    int id;
    char name[50];
    date_t dob;        // nested structure
float salary;
} employee_t;

employee_t emp = {1001, "John", {15, 3, 1990}, 55000.0};
printf("DOB: %d/%d/%d\n", emp.dob.day, emp.dob.month, emp.dob.year);
// 15/3/1990

Continue learning

Related notes

Put this topic into timed practice

Open mock tests when you want full-exam pacing, or keep drilling in practice mode.