Structs and Pointers
When we pass a struct to a function, C copies the entire struct. For a small struct that's fine. But for a struct with a 1000-character buffer and dozens of fields, copying is wasteful. The solution: pass a pointer to the struct
#include <stdio.h>
#include <string.h>
struct Terminal {
char id[50];
int sessions;
int errors;
};
void print_terminal(struct Terminal *p) {
printf("%s: %d sessions, %d errors\n", (*p).id, (*p).sessions, (*p).errors);
}
int main(void) {
struct Terminal t = {"tty7", 3, 0};
print_terminal(&t);
return 0;
}
We pass &t (the address of the struct), and the function receives a struct Terminal *. To access fields through a pointer, we write (*p).sessions: first dereference, then access the field
Writing (*p).sessions everywhere is ugly. C gives us a shortcut: the arrow operator ->
#include <stdio.h>
struct Terminal {
char id[50];
int sessions;
int errors;
};
void print_terminal(struct Terminal *p) {
printf("%s: %d sessions, %d errors\n", p->id, p->sessions, p->errors);
}
int main(void) {
struct Terminal t = {"tty7", 3, 0};
print_terminal(&t);
return 0;
}
p->sessions is exactly the same thing as (*p).sessions. It's cleaner and everyone uses it. The rule is simple: dot for structs, arrow for pointers to structs
The real power: functions that modify a struct through a pointer
#include <stdio.h>
struct Terminal {
char id[50];
int sessions;
int errors;
};
void log_error(struct Terminal *p) {
p->errors += 1;
if (p->errors >= 3) {
p->sessions = 0;
}
}
void open_session(struct Terminal *p) {
p->sessions += 1;
}
int main(void) {
struct Terminal t = {"tty7", 0, 0};
open_session(&t);
open_session(&t);
log_error(&t);
printf("%s: %d sessions, %d errors\n", t.id, t.sessions, t.errors);
return 0;
}
Output: tty7: 2 sessions, 1 errors
The functions modified the original struct, not a copy. It's the same "pass by reference" pattern we learned with pointers to int, but now with structs. This is how real C programs manage state
Mission: Compute-Time Account Ledger
The computing center bills processor time by the hour. Each user has an account with a balance in hours, and shift operators make allocations and withdrawals throughout the day. All updates must go through a pointer, so the original balance in the ledger changes in place.
- Define a struct HourAccount with fields owner (char array) and balance (int)
- Write the function void allocate(struct HourAccount *acc, int hours): adds hours to the balance through the pointer
- Write the function void withdraw(struct HourAccount *acc, int hours): subtracts hours from the balance if there's enough, otherwise print "Insufficient funds"
- Read from input: the owner's name, the initial balance, then three operations. Each operation has a code (1 = allocate, 2 = withdraw) followed by a value
- After all operations, print "Balance: X"
Example
Input
op7
1000
1 500
2 200
2 2000
Output
Insufficient funds
Balance: 1300
Example
Input
op12
200
2 50
1 100
2 300
Output
Insufficient funds
Balance: 250