The 'Self' Pointer
Part of the Object Oriented Programming section of Coddy's C journey — lesson 7 of 61.
In object-oriented languages like Python or Java, when you call object.method(), the language automatically passes the object itself to the method. In C, we must do this manually by passing a pointer to the struct as the first argument.
This pointer is conventionally named self (borrowed from Python) or this (from C++). It gives the function access to the struct's data so it can read or modify it:
typedef struct {
int health;
} Player;
void player_take_damage(Player *self, int amount) {
self->health -= amount;
}
The self pointer is what makes the function act like a method—it knows which player to modify. Without it, the function would have no way to access the specific struct instance. When calling this function, you pass the address of your struct:
Player hero = {100};
player_take_damage(&hero, 25);
// hero.health is now 75
Using a pointer (rather than passing the struct directly) is essential for modification. If you passed the struct by value, the function would receive a copy, and any changes would be lost when the function returns. The pointer ensures changes persist in the original struct.
Challenge
EasyLet's build a Timer module that tracks elapsed seconds. You'll practice using the "self" pointer pattern to modify a struct's internal state through functions that act as methods.
You'll create three files:
timer.h: Declare aTimerstruct with a singleint secondsmember. Also declare two functions:timer_tick(takes a pointer to Timer and adds a specified number of seconds) andtimer_reset(takes a pointer to Timer and sets seconds back to zero). Use include guards with the symbolTIMER_H.timer.c: Implement both functions. Thetimer_tickfunction should accept aTimer *selfand anint amount, then add the amount to the timer's seconds. Thetimer_resetfunction should accept aTimer *selfand set its seconds to zero.main.c: Create a Timer, perform operations on it, and display the results.
You will receive two integer inputs: the initial seconds for your timer and the amount to add via tick.
In your main file:
- Initialize a
Timerwith the initial seconds value - Call
timer_tickto add the specified amount - Print the current seconds
- Call
timer_resetto reset the timer - Print the seconds again (should be 0)
Print the results in this format:
After tick: {seconds}
After reset: {seconds}For example, with an initial value of 30 and adding 15 seconds, the output would be:
After tick: 45
After reset: 0Cheat sheet
In C, functions that operate on structs need explicit access to the struct instance through a pointer parameter, conventionally named self or this:
typedef struct {
int health;
} Player;
void player_take_damage(Player *self, int amount) {
self->health -= amount;
}
The self pointer allows the function to access and modify the struct's data. When calling such functions, pass the address of your struct:
Player hero = {100};
player_take_damage(&hero, 25);
// hero.health is now 75
Using a pointer is essential for modification—passing by value would create a copy, and changes would be lost when the function returns. The pointer ensures changes persist in the original struct.
Try it yourself
#include <stdio.h>
#include "timer.h"
int main() {
int initial_seconds, amount_to_add;
scanf("%d", &initial_seconds);
scanf("%d", &amount_to_add);
// TODO: Create a Timer and initialize it with initial_seconds
// TODO: Call timer_tick to add amount_to_add seconds
// TODO: Print the current seconds in format: "After tick: {seconds}"
// TODO: Call timer_reset to reset the timer
// TODO: Print the seconds again in format: "After reset: {seconds}"
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Modular Programming Basics
Header FilesInclude GuardsSource FilesStatic FunctionsRecap: Modular Calculator4Encapsulation
Opaque Pointers ConceptDefining Opaque StructsGetters and SettersValidation in SettersRecap: Secret Box2Objects and Methods
Structs as ObjectsThe 'Self' PointerConst CorrectnessPointer vs ValueHelper MethodsRecap: Point Manager5Project: Simple Bank Account
Project SetupImplementation of Account3Object Lifecycle
Constructor PatternDestructor PatternStack InitializationDeep CopyRecap: String Wrapper6Inheritance via Composition
Struct EmbeddingThe First Member RuleAccessing Parent MembersUpcastingRecap: Shape Hierarchy