C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
61: Defining and Using Structs
Up until now, we've been handling variables as isolated pieces of data. That works fine for a simple script, but imagine you're building a small RPG. You have a player with a name, a level, and some health. If you keep those as separate variables—char name[50], int level, float health—you'll quickly find yourself passing three or four arguments into every single function you write. It gets messy, fast.
Grouping player data together
This is where structs come in. A struct lets us wrap different data types into one single package. I like to think of it as creating my own custom data type. Instead of managing three different variables, I can just manage one "Player" object.
struct Player {
char name[50];
int level;
float health;
};
Now, whenever I need a player, I can just declare it like this: struct Player hero;. To get to the actual data inside, I use the dot operator. For example, hero.level = 1;. It's clean, it's logical, and it keeps the related data glued together.
Dealing with the "struct" keyword fatigue
One thing that always annoyed me when I first started with C is that you have to write the word struct every single time you declare a variable. Writing struct Player hero1; struct Player hero2; feels redundant. We can get around this using typedef. This basically tells the compiler, "From now on, when I say Player, I mean struct Player."
typedef struct {
char name[50];
int level;
float health;
} Player;
// Now I can just do this:
Player hero;
hero.level = 1;
I almost always use typedef for my structs. It just makes the code read more like a modern language and saves a few keystrokes.
Passing the player to a function
Now, let's say we want a function that heals the player. Since we've grouped the data, we only need to pass one thing to the function. However, if I pass the struct by value, C makes a complete copy of the whole struct. For a small struct, that's fine, but for larger ones, it's a waste of memory and slow. The professional way to do this is to pass a pointer to the struct.
void heal_player(Player *p) {
p->health += 10.0f;
}
Fixing the pointer access bug
Here is where I usually tripped up for the first few months. In my first attempt at this function, I tried to do this: p.health += 10.0f;. The compiler immediately screamed at me. Why? Because p is a pointer (an address), not the actual struct. You can't use the dot operator on a pointer.
To fix this, you have two choices. You could dereference the pointer first: (*p).health += 10.0f;. But honestly, nobody does that because the parentheses make it look clunky. Instead, C gives us the arrow operator ->. It's shorthand for "dereference this pointer and then access this member."
// The right way
void heal_player(Player *p) {
p->health += 10.0f; // Much cleaner
}
Once you get used to the arrow operator, you'll find it's the bread and butter of working with structs in C. Just remember: . for the actual variable, -> for the pointer to the variable.
📋 Practical Task
Build a Library Book Inventory System
Create a program that manages a book in a library. Your task is to:
- Define a
Bookstruct usingtypedef. It should contain atitle(string),author(string), andpages(integer). - Create a function called
print_book_detailsthat takes a pointer to aBookstruct and prints all its information to the console. - In your
mainfunction, instantiate oneBook, assign it some values, and call your print function.
There are no comments for now.