Project Overview
Each bank account is a struct holding an account number, name, and balance. All accounts live in one array — but instead of passing array indices around everywhere, every operation works directly on pointers to Account. This is the key design choice of this project: once you find an account, you get its address, and every function after that modifies the real account directly — no copies, no re-searching.
- Create/Initialize Accounts — pre-loads a small array of accounts
- Find Account — returns a
pointerto the matching account, orNULL - Deposit / Withdraw — functions that take an
Account*and modify it directly - Transfer — calls withdraw and deposit on two different pointers
- Display All — loops through the array showing every account
| Concept | Where it's used |
|---|---|
| struct | Account record: number, name, balance |
| Array | Account bank[TOTAL_ACCOUNTS] holds every account |
| Pointers | findAccount() returns Account*; deposit/withdraw/transfer all take pointers |
| Functions | One function per operation, each modifying data through a pointer |
| -> operator | Accessing struct fields through a pointer: acc->balance |
Account Struct & Bank Initialization
Every account is one Account struct. initBank() pre-loads 4 accounts so the program has data to work with immediately.
#include <stdio.h> #include <string.h> #define TOTAL_ACCOUNTS 4 typedef struct { int accNo; char name[40]; float balance; } Account; Account bank[TOTAL_ACCOUNTS]; // the entire bank lives in this array void initBank() { char *names[] = {"Asha Rao", "Vikram Singh", "Meera Nair", "Karan Mehta"}; float balances[] = {5000, 12000, 750, 30000}; for (int i = 0; i < TOTAL_ACCOUNTS; i++) { bank[i].accNo = 1001 + i; strcpy(bank[i].name, names[i]); bank[i].balance = balances[i]; } }
Finding an Account — Returning a Pointer
findAccount() is the function every other operation depends on. Instead of returning an index or a copy of the account, it returns &bank[i] — the actual address of the matching account. Whatever the caller does to that pointer changes the real data in the array.
Account* findAccount(int accNo) { for (int i = 0; i < TOTAL_ACCOUNTS; i++) { if (bank[i].accNo == accNo) return &bank[i]; // return the ADDRESS, not a copy } return NULL; // not found } void displayAll() { printf("\n%-8s %-16s %-12s\n", "AccNo", "Name", "Balance"); for (int i = 0; i < TOTAL_ACCOUNTS; i++) { printf("%-8d %-16s Rs %-10.2f\n", bank[i].accNo, bank[i].name, bank[i].balance); } } void searchAccount() { int accNo; printf("Enter account number: "); scanf("%d", &accNo); Account *acc = findAccount(accNo); // acc now points directly at the account if (acc == NULL) { printf("Account not found.\n"); return; } printf("Account %d | %s | Balance: Rs %.2f\n", acc->accNo, acc->name, acc->balance); // -> reads through the pointer }
findAccount() doesn't find a match, it returns NULL. Using acc->balance without checking for NULL first would crash the program (a "null pointer dereference").Deposit, Withdraw & Transfer — All Through Pointers
Each of these functions receives an Account* and changes the balance directly through the pointer — no return value is needed, because the change happens at the account's real address in the array. transferMoney() reuses both functions on two different pointers.
void deposit(Account *acc, float amount) { acc->balance += amount; // modifies the real account through the pointer printf("Deposited Rs %.2f. New balance: Rs %.2f\n", amount, acc->balance); } int withdraw(Account *acc, float amount) { if (amount > acc->balance) { printf("Insufficient balance.\n"); return 0; // failed } acc->balance -= amount; printf("Withdrew Rs %.2f. New balance: Rs %.2f\n", amount, acc->balance); return 1; // success } void transferMoney() { int fromNo, toNo; float amount; printf("From account: "); scanf("%d", &fromNo); printf("To account: "); scanf("%d", &toNo); printf("Amount: "); scanf("%f", &amount); Account *from = findAccount(fromNo); Account *to = findAccount(toNo); if (from == NULL || to == NULL) { printf("One or both accounts not found.\n"); return; } if (withdraw(from, amount)) { // only deposit if withdraw succeeded deposit(to, amount); printf("Transferred Rs %.2f from %d to %d.\n", amount, fromNo, toNo); } }
withdraw() and deposit() on two separate pointers — this is exactly why passing pointers matters: the same two functions work for a single account OR for moving money between two.The Main Menu
Same pattern as the Hotel, Library, and Hospital projects — a thin main() that loops and routes choices. The difference here is that deposit and withdraw need an account pointer first, obtained via findAccount() right inside the menu case.
int main() { initBank(); int choice, accNo; float amount; Account *acc; do { printf("\n===== SIMPLE BANKING SYSTEM =====\n"); printf("1. Display All Accounts\n"); printf("2. Search Account\n"); printf("3. Deposit\n"); printf("4. Withdraw\n"); printf("5. Transfer Between Accounts\n"); printf("6. Exit\n"); printf("Enter choice: "); scanf("%d", &choice); switch (choice) { case 1: displayAll(); break; case 2: searchAccount(); break; case 3: printf("Account number: "); scanf("%d", &accNo); acc = findAccount(accNo); if (acc) { printf("Amount: "); scanf("%f", &amount); deposit(acc, amount); } else printf("Account not found.\n"); break; case 4: printf("Account number: "); scanf("%d", &accNo); acc = findAccount(accNo); if (acc) { printf("Amount: "); scanf("%f", &amount); withdraw(acc, amount); } else printf("Account not found.\n"); break; case 5: transferMoney(); break; case 6: printf("Exiting... Thank you!\n"); break; default: printf("Invalid choice.\n"); } } while (choice != 6); return 0; }
===== SIMPLE BANKING SYSTEM ===== 1. Display All Accounts 2. Search Account 3. Deposit 4. Withdraw 5. Transfer Between Accounts 6. Exit Enter choice: 1 AccNo Name Balance 1001 Asha Rao Rs 5000.00 1002 Vikram Singh Rs 12000.00 1003 Meera Nair Rs 750.00 1004 Karan Mehta Rs 30000.00 Enter choice: 5 From account: 1004 To account: 1003 Amount: 2000 Withdrew Rs 2000.00. New balance: Rs 28000.00 Deposited Rs 2000.00. New balance: Rs 2750.00 Transferred Rs 2000.00 from 1004 to 1003. Enter choice: 4 Account number: 1003 Amount: 10000 Insufficient balance. Enter choice: 6 Exiting... Thank you!
Ways to Extend This Project
Each upgrade below reuses a concept from earlier lessons — and most of them lean even further into pointers:
- Transaction history — a linked list per account, storing every deposit/withdraw as a node
- Save/load accounts to a file —
fprintf/fscanf, exactly like the Hotel/Library/Hospital projects - Dynamic account count — replace the fixed array with
malloc-allocatedAccount*memory - PIN/password protection — add a PIN field to the struct, check it before deposit/withdraw
- Interest calculation — a function that takes an array of
Account*pointers and applies interest to each - Sort accounts by balance — plug in any algorithm from the Sorting lesson, swapping whole structs or pointers
| Extension | Concept it reuses |
|---|---|
| Transaction history | Linked Lists lesson |
| Save/load to file | File I/O lesson |
| Dynamic accounts | Dynamic memory (malloc/realloc) lesson |
| Sort by balance | Sorting Algorithms lesson |
Project Checklist
- I understand the Account struct and what each field stores
- I understand why findAccount() returns a pointer, not a copy or index
- I always check for NULL before dereferencing a found account
- I understand how -> accesses struct fields through a pointer
- I can trace deposit() and withdraw() modifying data via a pointer
- I understand how transferMoney() reuses withdraw() and deposit() on two pointers
- I can trace the do-while + switch main menu loop
- I compiled and ran the full program myself
- I picked at least one extension to try next