1
📋 Pass Struct to Function — By Value
A copy is made — original struct is never changed inside the function
Pass by Value
When you pass a struct to a function by value, C makes a complete copy. Any changes inside the function affect only the copy — the caller's original struct remains unchanged. Here
display() receives a full copy of Student s and prints it. tryChange() changes the copy's marks — but back in main() the marks are still the original value.
#include <stdio.h> typedef struct { char name[20]; int roll; float marks; } Student; /* Receives a COPY — original is untouched */ void display(Student s) { printf("Name : %s\n", s.name); printf("Roll : %d\n", s.roll); printf("Marks : %.1f\n", s.marks); } /* Tries to change marks — but only changes the local copy */ void tryChange(Student s) { s.marks = 0; /* affects the copy only */ printf("Inside tryChange — marks = %.1f\n", s.marks); } int main() { Student s = {"Ananta", 101, 88.5}; printf("--- Before function call ---\n"); display(s); tryChange(s); /* pass by value — s is unchanged */ printf("--- After tryChange() ---\n"); printf("Marks in main : %.1f\n", s.marks); /* still 88.5 */ return 0; }
--- Before function call --- Name : Ananta Roll : 101 Marks : 88.5 Inside tryChange — marks = 0.0 --- After tryChange() --- Marks in main : 88.5
Key rule: Pass by value = safe read-only. The function gets its own copy. Large structs passed by value every time are slow — use a pointer instead (next example).
example 2
2
📌 Pass Struct to Function — By Pointer
Pass the address — function modifies the original struct directly
Pass by Pointer
When you pass a pointer to a struct, the function works on the original — no copy is made. Use the arrow operator
p->field to access members through a pointer. Here applyBonus() receives &emp and adds 500 to the original salary. The change is visible back in main().
#include <stdio.h> typedef struct { char name[20]; int id; double salary; } Employee; /* Receives a POINTER — modifies the original */ void applyBonus(Employee *p, double bonus) { p->salary += bonus; /* arrow operator: (*p).salary */ printf("Bonus applied! New salary: %.2f\n", p->salary); } void displayEmp(Employee *p) { printf("ID : %d\n", p->id); printf("Name : %s\n", p->name); printf("Salary : %.2f\n", p->salary); } int main() { Employee emp = {"Priya", 1002, 32000.00}; printf("--- Original ---\n"); displayEmp(&emp); /* pass address */ applyBonus(&emp, 5000); /* modifies original */ printf("--- After bonus ---\n"); displayEmp(&emp); /* shows updated salary */ return 0; }
--- Original --- ID : 1002 Name : Priya Salary : 32000.00 Bonus applied! New salary: 37000.00 --- After bonus --- ID : 1002 Name : Priya Salary : 37000.00
p->salary is shorthand for (*p).salary. Both do the same thing — dereference the pointer, then access the field. Arrow (->) is almost always preferred because it's cleaner to read.example 3
3
🔁 Function Returns a Struct
Build a struct inside a function and return it to main
Return Struct
A function can return a struct — not just int or float. Here
createStudent() takes name, roll, and marks as arguments, fills a local Student struct, assigns the grade, and returns the whole struct. In main() the returned struct is caught in a variable and printed. Clean, reusable object creation.
#include <stdio.h> #include <string.h> typedef struct { char name[20]; int roll; float marks; char grade; } Student; /* Function returns a complete Student struct */ Student createStudent(char *nm, int roll, float marks) { Student s; strcpy(s.name, nm); s.roll = roll; s.marks = marks; /* Assign grade */ if (marks >= 90) s.grade = 'A'; else if (marks >= 75) s.grade = 'B'; else if (marks >= 55) s.grade = 'C'; else s.grade = 'F'; return s; /* return the whole struct */ } void print(Student s) { printf("%-12s Roll:%d Marks:%.1f Grade:%c\n", s.name, s.roll, s.marks, s.grade); } int main() { /* Catch the returned struct in a variable */ Student a = createStudent("Ananta", 101, 88.5); Student b = createStudent("Priya", 102, 93.0); Student c = createStudent("Rahul", 103, 52.0); printf("--- Student Cards ---\n"); print(a); print(b); print(c); return 0; }
--- Student Cards --- Ananta Roll:101 Marks:88.5 Grade:B Priya Roll:102 Marks:93.0 Grade:A Rahul Roll:103 Marks:52.0 Grade:F
Returning a struct from a function is perfectly valid in C. The struct is copied to the caller. This is the foundation of object-construction patterns — think of
createStudent() like a constructor in other languages.example 4
4
✏️ Update Struct Fields via Pointer
Functions that take struct pointer and change specific fields
Modify via Ptr
Multiple focused functions each accept a
Point* or Rectangle* and modify one or more fields. setPoint() sets coordinates, scale() multiplies the dimensions, area() reads without modifying (marked const). This is the C equivalent of member functions — small functions that operate on a struct through a pointer.
#include <stdio.h> typedef struct { float x; float y; } Point; typedef struct { float w; float h; } Rect; /* Set both fields via pointer */ void setPoint(Point *p, float x, float y) { p->x = x; p->y = y; } /* Move point by offset */ void movePoint(Point *p, float dx, float dy) { p->x += dx; p->y += dy; } /* Scale rectangle — modifies w and h */ void scaleRect(Rect *r, float factor) { r->w *= factor; r->h *= factor; } /* Read-only — const pointer, won't modify */ float areaRect(const Rect *r) { return r->w * r->h; } int main() { Point p; setPoint(&p, 3.0, 4.0); printf("Point : (%.1f, %.1f)\n", p.x, p.y); movePoint(&p, 2.0, -1.0); printf("Moved : (%.1f, %.1f)\n", p.x, p.y); Rect r = {10.0, 5.0}; printf("\nRect : %.1f x %.1f area=%.1f\n", r.w, r.h, areaRect(&r)); scaleRect(&r, 2.0); printf("Scaled: %.1f x %.1f area=%.1f\n", r.w, r.h, areaRect(&r)); return 0; }
Point : (3.0, 4.0) Moved : (5.0, 3.0) Rect : 10.0 x 5.0 area=50.0 Scaled: 20.0 x 10.0 area=200.0
const Rect *r means "pointer to a Rect I promise not to modify." The compiler will error if you try to assign to r->w inside that function. Use it for read-only functions — documents intent and prevents bugs.example 5
5
📊 Array of Structs Passed to Function
Pass entire array to functions — display, find average, find topper
Array + Func
An array of structs passed to a function decays to a pointer to its first element — just like a plain array. Three functions all take
Student arr[], int n: one prints all records, one computes the average marks, one finds and returns a pointer to the topper. The pointer return from findTopper() lets us access all the topper's fields directly.
#include <stdio.h> typedef struct { char name[20]; float marks; } Student; void printAll(Student arr[], int n) { printf("%-12s %s\n", "Name", "Marks"); printf("-------------------\n"); for (int i = 0; i < n; i++) printf("%-12s %.1f\n", arr[i].name, arr[i].marks); } float average(Student arr[], int n) { float sum = 0; for (int i = 0; i < n; i++) sum += arr[i].marks; return sum / n; } /* Returns a POINTER to the topper inside the array */ Student* findTopper(Student arr[], int n) { int top = 0; for (int i = 1; i < n; i++) if (arr[i].marks > arr[top].marks) top = i; return &arr[top]; /* pointer to the actual element */ } int main() { Student batch[] = { {"Ananta", 88.5}, {"Priya", 95.0}, {"Rahul", 72.0}, {"Sneha", 91.5} }; int n = 4; printAll(batch, n); printf("\nAverage : %.2f\n", average(batch, n)); Student *top = findTopper(batch, n); printf("Topper : %s (%.1f)\n", top->name, top->marks); return 0; }
Name Marks ------------------- Ananta 88.5 Priya 95.0 Rahul 72.0 Sneha 91.5 Average : 86.75 Topper : Priya (95.0)
Student *top = findTopper(...) — the returned pointer points directly into the batch array. So top->name and top->marks access the actual element, not a copy. Efficient: no struct is ever duplicated.example 6
6
🔄 Swap Two Structs Using Pointers
Classic swap via temporary variable — on whole structs using pointers
Swap via Ptr
The classic swap pattern extended to entire structs.
swapStudents() receives two Student* pointers and uses a temporary Student variable to exchange all fields at once. Without pointers, the swap would be local only — the original variables would be unchanged.
#include <stdio.h> typedef struct { char name[20]; int roll; float marks; } Student; void print(Student *s) { printf(" %-10s roll=%-4d marks=%.1f\n", s->name, s->roll, s->marks); } /* Swap the entire struct contents via pointers */ void swapStudents(Student *a, Student *b) { Student temp = *a; /* copy all fields of *a into temp */ *a = *b; /* copy all fields of *b into *a */ *b = temp; /* copy temp into *b */ } int main() { Student s1 = {"Ananta", 101, 88.5}; Student s2 = {"Priya", 102, 95.0}; printf("Before swap:\n"); printf("s1: "); print(&s1); printf("s2: "); print(&s2); swapStudents(&s1, &s2); printf("\nAfter swap:\n"); printf("s1: "); print(&s1); printf("s2: "); print(&s2); return 0; }
Before swap: s1: Ananta roll=101 marks=88.5 s2: Priya roll=102 marks=95.0 After swap: s1: Priya roll=102 marks=95.0 s2: Ananta roll=101 marks=88.5
Student temp = *a; — the * dereferences the pointer, giving you the full struct value. Assignment on structs copies every field at once. This one-line swap idiom works on any struct of any size.example 7
7
🏠 Nested Struct — Address Inside Person
Struct inside a struct — pass outer struct pointer, access inner fields
Nested Struct
A
Person struct contains an Address struct as one of its fields. When the outer struct is passed by pointer, inner fields are reached by chaining the dot or arrow operator: p->addr.city. updateCity() changes only the city inside the nested struct without touching any other field.
#include <stdio.h> #include <string.h> typedef struct { char city[20]; char state[20]; int pin; } Address; typedef struct { char name[20]; int age; Address addr; /* nested struct */ } Person; void displayPerson(const Person *p) { printf("Name : %s (age %d)\n", p->name, p->age); printf("City : %s\n", p->addr.city); /* arrow then dot */ printf("State : %s\n", p->addr.state); printf("PIN : %d\n", p->addr.pin); } /* Update only the city inside the nested struct */ void updateCity(Person *p, char *newCity, int newPin) { strcpy(p->addr.city, newCity); p->addr.pin = newPin; } int main() { Person per = { "Ananta", 21, {"Haridwar", "Uttarakhand", 249401} }; printf("--- Original ---\n"); displayPerson(&per); updateCity(&per, "Dehradun", 248001); printf("\n--- After updateCity() ---\n"); displayPerson(&per); return 0; }
--- Original --- Name : Ananta (age 21) City : Haridwar State : Uttarakhand PIN : 249401 --- After updateCity() --- Name : Ananta (age 21) City : Dehradun State : Uttarakhand PIN : 248001
Access pattern for nested structs via pointer:
p->addr.city means — follow pointer p (arrow), reach addr (which is a plain struct, not a pointer), then access city (dot). Arrow then dot.example 8
8
⬆️ Pointer Arithmetic on Struct Array
Walk an array of structs using a pointer — increment the pointer each step
Ptr Arithmetic
A pointer to a struct array can be incremented just like an int pointer.
p++ advances by sizeof(Product) bytes — landing on the next struct. This is exactly how the compiler implements arr[i] internally. Both the index method and the pointer method produce identical output — they compile to the same machine code.
#include <stdio.h> typedef struct { char name[20]; float price; int qty; } Product; /* Walk array using index — traditional */ void printByIndex(Product arr[], int n) { printf("--- Index method ---\n"); for (int i = 0; i < n; i++) printf("%-12s Rs%.2f qty=%d\n", arr[i].name, arr[i].price, arr[i].qty); } /* Walk array using pointer arithmetic */ void printByPointer(Product *p, int n) { printf("--- Pointer method ---\n"); for (int i = 0; i < n; i++, p++) /* p++ = move to next struct */ printf("%-12s Rs%.2f qty=%d\n", p->name, p->price, p->qty); } int main() { Product shop[] = { {"Rice", 60.0, 100}, {"Oil", 180.0, 50}, {"Sugar", 45.0, 200} }; int n = 3; printByIndex(shop, n); printf("\n"); printByPointer(shop, n); /* Pointer arithmetic demo */ Product *p = shop; printf("\nAddress of shop[0] : %p\n", (void*)p); printf("Address of shop[1] : %p\n", (void*)(p+1)); printf("Difference : %zu bytes (= sizeof Product)\n", (char*)(p+1) - (char*)p); return 0; }
--- Index method --- Rice Rs60.00 qty=100 Oil Rs180.00 qty=50 Sugar Rs45.00 qty=200 --- Pointer method --- Rice Rs60.00 qty=100 Oil Rs180.00 qty=50 Sugar Rs45.00 qty=200 Address of shop[0] : 0x7ffce1b0 Address of shop[1] : 0x7ffce1cc Difference : 28 bytes (= sizeof Product)
Why 28 bytes?
char name[20] = 20 bytes, float price = 4 bytes, int qty = 4 bytes. Total = 28 bytes. p++ skips exactly 28 bytes — jumping to the start of the next Product in memory.example 9
9
🏅 Return Pointer to Max Struct in Array
Function scans array, returns pointer to the highest-salary employee
Return Ptr
findHighestPaid() takes an array and size, scans all employees, and returns a Employee* pointing to the one with the highest salary. The caller gets a pointer directly into the array — no copy. Change best->salary and the real array element changes too. Shows why returning pointers is both powerful and requires care.
#include <stdio.h> typedef struct { char name[20]; char dept[15]; double salary; } Employee; /* Returns pointer to the highest-paid employee */ Employee* findHighestPaid(Employee *arr, int n) { Employee *best = &arr[0]; for (int i = 1; i < n; i++) if (arr[i].salary > best->salary) best = &arr[i]; return best; /* pointer into the array — no copy */ } void printAll(Employee *arr, int n) { printf("%-12s %-12s %10s\n", "Name", "Dept", "Salary"); printf("--------------------------------------\n"); for (int i = 0; i < n; i++) printf("%-12s %-12s %10.2f\n", arr[i].name, arr[i].dept, arr[i].salary); } int main() { Employee team[] = { {"Ananta", "Engineering", 55000}, {"Priya", "Design", 62000}, {"Rahul", "Marketing", 48000}, {"Sneha", "Engineering", 71000}, {"Vikram", "HR", 39000} }; int n = 5; printAll(team, n); Employee *best = findHighestPaid(team, n); printf("\n🏅 Highest paid: %s (%s) — Rs %.2f\n", best->name, best->dept, best->salary); /* Modifying through returned pointer changes the real array */ best->salary += 10000; printf("After raise: %s now earns Rs %.2f\n", best->name, best->salary); return 0; }
Name Dept Salary -------------------------------------- Ananta Engineering 55000.00 Priya Design 62000.00 Rahul Marketing 48000.00 Sneha Engineering 71000.00 Vikram HR 39000.00 🏅 Highest paid: Sneha (Engineering) — Rs 71000.00 After raise: Sneha now earns Rs 81000.00
Warning: Never return a pointer to a local variable inside a function — that memory is gone after the function returns. Here we return a pointer into the
team[] array which lives in main() — perfectly safe.example 10
10
🏪 Mini Student Management — Complete Program
All concepts together — add, display, update marks, find topper
Complete App
Everything combined: struct definition,
createStudent() returns a struct, displayAll() takes array + size, updateMarks() takes a pointer and modifies in place, getTopper() returns a pointer to the best. A real mini application that uses every technique from examples 1–9 in one cohesive program.
#include <stdio.h> #include <string.h> #define MAX 5 typedef struct { char name[20]; int roll; float marks; char grade; } Student; /* 1. Factory function — returns a struct */ Student makeStudent(char *nm, int roll, float marks) { Student s; strcpy(s.name, nm); s.roll = roll; s.marks = marks; s.grade = (marks>=90)?'A':(marks>=75)?'B':(marks>=55)?'C':'F'; return s; } /* 2. Display all — struct array by pointer */ void displayAll(Student *arr, int n) { printf("\n%-12s %5s %7s %6s\n","Name","Roll","Marks","Grade"); printf("-----------------------------------\n"); for (int i = 0; i < n; i++, arr++) /* pointer walk */ printf("%-12s %5d %7.1f %6c\n", arr->name, arr->roll, arr->marks, arr->grade); } /* 3. Update marks by pointer — modifies original */ void updateMarks(Student *s, float newMarks) { s->marks = newMarks; s->grade = (newMarks>=90)?'A':(newMarks>=75)?'B':(newMarks>=55)?'C':'F'; } /* 4. Find topper — returns pointer into array */ Student* getTopper(Student *arr, int n) { Student *top = arr; for (int i = 1; i < n; i++) if ((arr+i)->marks > top->marks) top = arr + i; return top; } int main() { Student db[MAX]; db[0] = makeStudent("Ananta", 101, 88.5); db[1] = makeStudent("Priya", 102, 73.0); db[2] = makeStudent("Rahul", 103, 91.5); db[3] = makeStudent("Sneha", 104, 58.0); db[4] = makeStudent("Vikram", 105, 45.0); printf("=== Initial Records ==="); displayAll(db, MAX); /* Update Priya's marks via pointer */ updateMarks(&db[1], 95.0); printf("\n=== After updating Priya's marks to 95.0 ==="); displayAll(db, MAX); /* Find and print topper */ Student *top = getTopper(db, MAX); printf("\n🏆 Topper: %s | Marks: %.1f | Grade: %c\n", top->name, top->marks, top->grade); return 0; }
=== 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 === After updating Priya's marks to 95.0 === Name Roll Marks Grade ----------------------------------- Ananta 101 88.5 B Priya 102 95.0 A Rahul 103 91.5 A Sneha 104 58.0 C Vikram 105 45.0 F 🏆 Topper: Priya | Marks: 95.0 | Grade: A
All four patterns in one program:
makeStudent() returns a struct · displayAll() reads via pointer walk · updateMarks() modifies via pointer · getTopper() returns a pointer to an element. This is how real C programs are structured.checklist
- Ex 1 — Pass by value makes a copy. Changes inside the function don't affect the original.
- Ex 2 — Pass
&structso the function can modify the original. Usep->fieldinside. - Ex 3 — A function can return a whole struct. Assign it:
Student s = createStudent(...); - Ex 4 —
const Rect *r= read-only pointer. Compiler prevents accidental modification. - Ex 5 — An array of structs decays to a pointer. Return
&arr[i]to point to a specific element. - Ex 6 — Swap with
Student temp = *a; *a = *b; *b = temp;— copies all fields at once. - Ex 7 — Nested struct access via pointer:
p->addr.city(arrow then dot). - Ex 8 —
p++on a struct pointer moves bysizeof(struct)bytes — lands on the next element. - Ex 9 — Return a pointer into an array, not a local variable. Local variables die when function returns.
- Ex 10 — Factory (return struct) + display (ptr walk) + update (ptr modify) + find (return ptr) in one program.