Structs + Pointers + Functions — 5 Examples
0%
Structures  ·  Pointers  ·  Functions  ·  5 Examples

Passing Structures via Pointers
to Functions — 5 Programs

Five focused programs that show every way structures and functions interact through pointers in C — read-only access, in-place modification, returning structs, array of structs, and a complete mini app.

1
Read via const ptr
2
Modify in-place
3
Return a struct
4
Array of structs
5
Complete mini app
Why pointers to structs?  When you pass a struct to a function by value, C makes a complete copy of every field — expensive for large structs. Passing a pointer to the struct instead sends only 8 bytes (the address) regardless of struct size. The function then reaches through the pointer to access the real fields with the arrow operator ->. Two rules to remember:

p->field  =  (*p).field  —  both read the field through a pointer. Arrow is preferred.
➤ To allow modification: pass Student *p. To prevent modification: pass const Student *p.
SyntaxWhat it meansCan modify struct?
display(Student s)Pass by value — full copy madeNo — copy only
display(Student *p)Pass pointer — no copyYes — original accessible
display(const Student *p)Pass pointer, read-onlyNo — compiler enforces
Student* build(...)Return a pointer to structCaller owns the struct
Student build(...)Return struct by valueCaller gets a copy
1
🔒 Read a Struct via const Pointer — Display Function
const Student *p — access fields with arrow, compiler blocks any modification
const ptr
Passing const Student *p tells the compiler: "I promise this function will not modify the struct." You can still read every field through p->field, but any attempt to write — like p->marks = 0 — causes a compile error. This is the correct signature for any display or print function that only needs to read. It documents intent and prevents accidental writes.
pass by pointer — no copy, arrow operator accesses original fields
main() Student s = {"Ananta",95}; display(&s); stack frame — holds real struct &s (address) 8 bytes only display(const Student *p) p->name → "Ananta" p->marks → 95 🔒 read-only
ex1_const_ptr_display.c
C
#include <stdio.h>
#include <string.h>

typedef struct {
    char  name[25];
    int   roll;
    float marks;
    char  grade;
} Student;

/* const ptr — read-only access, no copy made */
void display(const Student *p) {
    printf("------ Student Card ------\n");
    printf("Name  : %s\n",  p->name);    /* p->field = (*p).field */
    printf("Roll  : %d\n",  p->roll);
    printf("Marks : %.1f\n", p->marks);
    printf("Grade : %c\n",  p->grade);
    printf("--------------------------\n");
    /* p->marks = 0;  ← compile ERROR: const disallows this */
}

/* Assign grade based on marks — also read-only param */
char getGrade(const Student *p) {
    if      (p->marks >= 90) return 'A';
    else if (p->marks >= 75) return 'B';
    else if (p->marks >= 55) return 'C';
    else                      return 'F';
}

int main() {
    Student s1 = {"Ananta",  101, 88.5f, '\0'};
    Student s2 = {"Priya",   102, 95.0f, '\0'};
    Student s3 = {"Rahul",   103, 52.0f, '\0'};

    /* Assign grades — pointer to allow function to read marks */
    s1.grade = getGrade(&s1);
    s2.grade = getGrade(&s2);
    s3.grade = getGrade(&s3);

    /* Display — pass address, no copy */
    display(&s1);
    display(&s2);
    display(&s3);

    printf("\nsizeof(Student) = %zu bytes\n", sizeof(Student));
    printf("Passing pointer  = 8 bytes (address only)\n");
    return 0;
}
output
------ Student Card ------
Name  : Ananta
Roll  : 101
Marks : 88.5
Grade : B
--------------------------
------ Student Card ------
Name  : Priya
Roll  : 102
Marks : 95.0
Grade : A
--------------------------
------ Student Card ------
Name  : Rahul
Roll  : 103
Marks : 52.0
Grade : F
--------------------------

sizeof(Student) = 32 bytes
Passing pointer  = 8 bytes (address only)
const Student *p vs Student *const p — these are different. const Student *p means the struct the pointer points to is read-only (the pointer itself can be changed). Student *const p means the pointer is fixed but the struct can be changed. For display functions, always use const Student *p.
example 2
2
✏️ Modify a Struct In-Place via Pointer
Student *p — function writes directly to the original struct's fields
Modify via ptr
Drop the const and the function can write directly into the original struct through the pointer. The change is immediately visible to the caller — there is no copy. Three focused functions each accept a Student*: applyBonus adds marks, promote updates grade, and resetStudent zeros every field. Every change is permanent in the caller's variable.
ex2_modify_inplace.c
C
#include <stdio.h>
#include <string.h>

typedef struct {
    char   name[25];
    int    id;
    double salary;
    int    level;     /* 1=Junior 2=Mid 3=Senior */
} Employee;

/* Adds bonus — modifies salary through pointer */
void applyBonus(Employee *p, double pct) {
    p->salary += p->salary * (pct / 100.0);
}

/* Promote — increments level, adjusts salary */
void promote(Employee *p) {
    if (p->level < 3) {
        p->level++;
        p->salary *= 1.20;   /* 20% raise on promotion */
        printf("  %s promoted to level %d\n", p->name, p->level);
    } else {
        printf("  %s already at max level\n", p->name);
    }
}

/* Zero out — reset the entire struct */
void resetEmployee(Employee *p) {
    strcpy(p->name, "(vacant)");
    p->id     = 0;
    p->salary = 0.0;
    p->level  = 0;
}

void print(const Employee *p) {
    const char *lvl[] = {"-", "Junior", "Mid", "Senior"};
    printf("  %-12s ID:%-4d  Rs%8.2f  %s\n",
           p->name, p->id, p->salary, lvl[p->level]);
}

int main() {
    Employee e = {"Ananta", 1001, 40000.00, 1};

    printf("Original:\n");       print(&e);

    applyBonus(&e, 10);     /* 10% bonus */
    printf("\nAfter 10%% bonus:\n"); print(&e);

    promote(&e);
    printf("After promotion:\n"); print(&e);

    promote(&e);
    promote(&e);             /* already at max on 3rd call */
    printf("After 2 more promotions:\n"); print(&e);

    resetEmployee(&e);
    printf("\nAfter reset:\n");  print(&e);
    return 0;
}
output
Original:
  Ananta       ID:1001  Rs 40000.00  Junior

After 10% bonus:
  Ananta       ID:1001  Rs 44000.00  Junior

  Ananta promoted to level 2
After promotion:
  Ananta       ID:1001  Rs 52800.00  Mid

  Ananta promoted to level 3
  Ananta already at max level
After 2 more promotions:
  Ananta       ID:1001  Rs 76032.00  Senior

After reset:
  (vacant)     ID:0     Rs     0.00  -
The pointer is the key. Without &, applyBonus(e, 10) would pass a copy — the original salary stays unchanged. With &, applyBonus(&e, 10) gives the function the actual address — every change sticks in the original.
example 3
3
↩ Function Returns a Struct — Factory Pattern
Build a struct inside a function, return it by value — caller owns the result
Return struct
A function can return an entire struct by value. The function builds the struct internally, then returns it — the struct is copied to the caller at the return statement. This is called the factory pattern — the function is responsible for creating a properly-initialised struct. Combine it with a pointer parameter to also fill a struct the caller already owns (the "output pointer" pattern).
ex3_return_struct.c
C
#include <stdio.h>
#include <math.h>

typedef struct {
    float width;
    float height;
    float area;
    float diagonal;
} Rectangle;

/* Factory — returns a fully computed Rectangle */
Rectangle makeRect(float w, float h) {
    Rectangle r;
    r.width    = w;
    r.height   = h;
    r.area     = w * h;
    r.diagonal = sqrtf(w*w + h*h);
    return r;   /* struct copied to caller */
}

/* Output-pointer pattern — fills caller's variable */
void fillRect(Rectangle *out, float w, float h) {
    out->width    = w;
    out->height   = h;
    out->area     = w * h;
    out->diagonal = sqrtf(w*w + h*h);
    /* no return needed — wrote directly via pointer */
}

void printRect(const char *label, const Rectangle *r) {
    printf("%-12s  w=%-5.1f h=%-5.1f  area=%-8.2f diag=%.2f\n",
           label, r->width, r->height, r->area, r->diagonal);
}

/* Compare two rectangles — both passed as const ptrs */
void compare(const Rectangle *a, const Rectangle *b) {
    printf("\nBigger area : ");
    if      (a->area > b->area) printf("first (%.2f)\n",  a->area);
    else if (b->area > a->area) printf("second (%.2f)\n", b->area);
    else                        printf("equal\n");
}

int main() {
    /* Pattern 1 — return by value */
    Rectangle r1 = makeRect(10.0f, 5.0f);
    Rectangle r2 = makeRect(7.0f,  7.0f);

    /* Pattern 2 — output pointer */
    Rectangle r3;
    fillRect(&r3, 4.0f, 9.0f);

    printf("%-12s  %5s %5s  %8s %s\n",
           "Shape", "W", "H", "Area", "Diagonal");
    printf("--------------------------------------------------\n");
    printRect("Rectangle 1", &r1);
    printRect("Rectangle 2", &r2);
    printRect("Rectangle 3", &r3);

    compare(&r1, &r2);
    return 0;
}
output
Shape         W     H       Area     Diagonal
--------------------------------------------------
Rectangle 1   w=10.0 h=5.0   area=50.00   diag=11.18
Rectangle 2   w=7.0  h=7.0   area=49.00   diag=9.90
Rectangle 3   w=4.0  h=9.0   area=36.00   diag=9.85

Bigger area : first (50.00)
Return by value vs output pointer — when to use which: Return by value is cleaner and readable — use it when the function creates one result. The output pointer pattern is used when: (1) the function needs to fill multiple structs, (2) you want to avoid the copy cost on very large structs, or (3) you need to signal success/failure via the return value.
example 4
4
📊 Array of Structs Passed to Functions
arr[] decays to pointer — process, search, and find max across all records
Array + Func
When an array of structs is passed to a function, it decays to a pointer to its first element — just like a plain int array. The function signature Product arr[], int n is identical to Product *arr, int n. Inside, you index with arr[i].field or use pointer arithmetic (arr+i)->field. Three focused functions show the three most common patterns: print all, compute total, and find the most expensive item.
ex4_array_of_structs.c
C
#include <stdio.h>
#include <string.h>

typedef struct {
    char  name[20];
    float price;
    int   qty;
} Product;

/* Print all — const: read-only */
void printAll(const Product *arr, int n) {
    printf("%-16s %8s %5s %12s\n",
           "Product", "Price", "Qty", "Value");
    printf("-------------------------------------------\n");
    for (int i = 0; i < n; i++) {
        printf("%-16s %8.2f %5d %12.2f\n",
               arr[i].name, arr[i].price,
               arr[i].qty, arr[i].price * arr[i].qty);
    }
}

/* Total stock value */
float totalValue(const Product *arr, int n) {
    float total = 0;
    for (int i = 0; i < n; i++)
        total += arr[i].price * arr[i].qty;
    return total;
}

/* Returns POINTER to most expensive product in the array */
const Product* mostExpensive(const Product *arr, int n) {
    const Product *best = &arr[0];
    for (int i = 1; i < n; i++)
        if (arr[i].price > best->price) best = &arr[i];
    return best;   /* pointer into original array — no copy */
}

/* Apply discount — modifies price through pointer */
void applyDiscount(Product *arr, int n, float pct) {
    for (int i = 0; i < n; i++)
        arr[i].price -= arr[i].price * (pct / 100.0f);
}

int main() {
    Product shop[] = {
        {"Rice 1kg",    60.0f,  200},
        {"Cooking Oil", 180.0f,  80},
        {"Sugar 1kg",   45.0f, 150},
        {"Tea Powder",  220.0f,  60},
        {"Salt",         20.0f, 300}
    };
    int n = 5;

    printAll(shop, n);
    printf("-------------------------------------------\n");
    printf("Total stock value : Rs %.2f\n", totalValue(shop, n));

    const Product *top = mostExpensive(shop, n);
    printf("Most expensive    : %s (Rs %.2f)\n",
           top->name, top->price);

    printf("\nApplying 10%% discount...\n");
    applyDiscount(shop, n, 10);
    printAll(shop, n);
    printf("-------------------------------------------\n");
    printf("Total after discount : Rs %.2f\n", totalValue(shop, n));
    return 0;
}
output
Product           Price   Qty        Value
-------------------------------------------
Rice 1kg          60.00   200     12000.00
Cooking Oil      180.00    80     14400.00
Sugar 1kg         45.00   150      6750.00
Tea Powder       220.00    60     13200.00
Salt              20.00   300      6000.00
-------------------------------------------
Total stock value : Rs 52350.00
Most expensive    : Tea Powder (Rs 220.00)

Applying 10% discount...
Product           Price   Qty        Value
-------------------------------------------
Rice 1kg          54.00   200     10800.00
Cooking Oil      162.00    80     12960.00
Sugar 1kg         40.50   150      6075.00
Tea Powder       198.00    60     11880.00
Salt              18.00   300      5400.00
-------------------------------------------
Total after discount : Rs 47115.00
const Product *best = &arr[0]best is a pointer into the original shop[] array. When you do best = &arr[i], you're updating the pointer to point at a different element — not copying the struct. The caller accesses top->name and gets the real data, zero copies made.
example 5
5
🎓 Complete Mini App — Student Management System
All patterns together — create, display, update, swap, search, topper
Complete App
Every pointer-and-struct pattern from examples 1–4 combined into one cohesive program. Six functions each demonstrate a different technique: makeStudent returns a struct, displayAll uses a const pointer walk, updateMarks modifies in-place, swapStudents swaps two structs via pointers, findTopper returns a pointer to the best element, and applyGrades modifies every element of the array.
FunctionSignature patternTechnique
makeStudent()Student makeStudent(...)Return struct by value — factory
displayAll()void f(const Student*, int)const pointer — read-only array walk
updateMarks()void f(Student*, float)Non-const pointer — modify one field
swapStudents()void f(Student*, Student*)Two pointers — classic temp-swap
findTopper()Student* f(Student*, int)Return pointer into array — no copy
applyGrades()void f(Student*, int)Modify every element via pointer walk
ex5_complete_app.c
C
#include <stdio.h>
#include <string.h>

typedef struct {
    char  name[20];
    int   roll;
    float marks;
    char  grade;
} Student;

/* 1. Factory — return struct by value */
Student makeStudent(const char *nm, int roll, float marks) {
    Student s;
    strncpy(s.name, nm, 19); s.name[19] = '\0';
    s.roll  = roll;
    s.marks = marks;
    s.grade = '-';       /* assigned later by applyGrades */
    return s;
}

/* 2. Display all — const pointer walk */
void displayAll(const Student *arr, int n) {
    printf("  %-12s %5s %7s %6s\n",
           "Name", "Roll", "Marks", "Grade");
    printf("  -----------------------------------\n");
    for (int i = 0; i < n; i++)
        printf("  %-12s %5d %7.1f %6c\n",
               arr[i].name, arr[i].roll,
               arr[i].marks, arr[i].grade);
}

/* 3. Update marks in-place via pointer */
void updateMarks(Student *p, float newMarks) {
    printf("  Updating %s: %.1f -> %.1f\n",
           p->name, p->marks, newMarks);
    p->marks = newMarks;
}

/* 4. Swap two students using pointers */
void swapStudents(Student *a, Student *b) {
    Student tmp = *a;   /* copy all fields of *a */
    *a = *b;            /* copy all fields of *b into *a */
    *b = tmp;           /* copy saved fields into *b */
}

/* 5. Find topper — returns pointer into array */
Student* findTopper(Student *arr, int n) {
    Student *best = &arr[0];
    for (int i = 1; i < n; i++)
        if (arr[i].marks > best->marks)
            best = &arr[i];
    return best;
}

/* 6. Apply grades to entire array */
void applyGrades(Student *arr, int n) {
    for (int i = 0; i < n; i++) {
        float m = arr[i].marks;
        arr[i].grade = (m >= 90) ? 'A'
                     : (m >= 75) ? 'B'
                     : (m >= 55) ? 'C' : 'F';
    }
}

int main() {
    Student batch[5];

    /* Pattern 1 — factory */
    batch[0] = makeStudent("Ananta", 101, 88.5f);
    batch[1] = makeStudent("Priya",  102, 73.0f);
    batch[2] = makeStudent("Rahul",  103, 91.5f);
    batch[3] = makeStudent("Sneha",  104, 58.0f);
    batch[4] = makeStudent("Vikram", 105, 45.0f);

    /* Pattern 6 — grade entire array */
    applyGrades(batch, 5);

    printf("=== Initial Records ===\n");
    displayAll(batch, 5);           /* Pattern 2 */

    /* Pattern 3 — update one student */
    printf("\n=== Update Priya's marks to 96.0 ===\n");
    updateMarks(&batch[1], 96.0f);
    applyGrades(batch, 5);
    displayAll(batch, 5);

    /* Pattern 4 — swap first and last */
    printf("\n=== Swap Ananta and Vikram ===\n");
    swapStudents(&batch[0], &batch[4]);
    displayAll(batch, 5);

    /* Pattern 5 — find topper via returned pointer */
    Student *top = findTopper(batch, 5);
    printf("\n=== Topper ===\n");
    printf("  %s (Roll %d) — %.1f marks  Grade: %c\n",
           top->name, top->roll, top->marks, top->grade);

    /* Modifying through returned pointer changes real array */
    top->marks += 2.0f;   /* bonus marks for topper */
    printf("  After 2 bonus marks: %.1f\n", top->marks);
    return 0;
}
output
=== Initial Records ===
  Name          Roll   Marks  Grade
  -----------------------------------
  Ananta         101    88.5      B
  Priya          102    73.0      C
  Rahul          103    91.5      A
  Sneha          104    58.0      C
  Vikram         105    45.0      F

=== Update Priya's marks to 96.0 ===
  Updating Priya: 73.0 -> 96.0
  Name          Roll   Marks  Grade
  -----------------------------------
  Ananta         101    88.5      B
  Priya          102    96.0      A
  Rahul          103    91.5      A
  Sneha          104    58.0      C
  Vikram         105    45.0      F

=== Swap Ananta and Vikram ===
  Name          Roll   Marks  Grade
  -----------------------------------
  Vikram         105    45.0      F
  Priya          102    96.0      A
  Rahul          103    91.5      A
  Sneha          104    58.0      C
  Ananta         101    88.5      B

=== Topper ===
  Priya (Roll 102) — 96.0 marks  Grade: A
  After 2 bonus marks: 98.0
All six patterns at a glance: makeStudent returns a struct  ·  displayAll reads with const*  ·  updateMarks writes with *  ·  swapStudents uses Student tmp = *a to copy all fields  ·  findTopper returns &arr[i]  ·  modifying through the returned pointer changes the real array. These six cover every struct-pointer-function interaction you will encounter in C.
checklist
  • Ex 1 — const ptr: const Student *p = read-only access. Use p->field (arrow operator). Compiler blocks any write. Always use for display/print functions. 8 bytes sent, not sizeof(struct).
  • Ex 2 — modify in-place: Drop const → function can write p->salary = newVal. Changes survive the function return. Use strcpy(p->name, "x") for string fields — cannot assign directly.
  • Ex 3 — return struct: Rectangle makeRect(w, h) returns by value — struct copied to caller. Output pointer pattern: void fillRect(Rectangle *out, ...) writes directly via pointer, no return needed.
  • Ex 4 — array of structs: Array decays to pointer on passing. arr[i].field and (arr+i)->field are identical. Return &arr[i] for a pointer into the original — no copy. Use const for read-only functions.
  • Ex 5 — complete app: Factory (return struct) + const ptr walk + modify in-place + swap with temp (Student tmp = *a; *a = *b; *b = tmp) + return pointer + modify via returned pointer. All six patterns in one program.