Introduction
Hello! In our last lesson, we tackled the most common and dangerous bugs associated with manual memory management in C: null dereferences, dangling pointers, and memory leaks. We also introduced Valgrind, a powerful tool for detecting these issues. This gave you a solid foundation in writing safer, more robust C code.
Today, we'll shift from managing raw, anonymous blocks of memory to organizing data in a more structured and meaningful way. This lesson addresses the learning outcome: Define and use structs to group related data.
In languages you're familiar with, like Java or Python, you use a class to bundle related data and functionality. C's struct is a simpler ancestor of this concept, focusing purely on grouping data. Think of it as creating a custom blueprint for a data record, which is a concept you've likely encountered in your data analysis work. We will cover:
- The syntax for defining a
struct. - How to declare
structvariables and access their members. - Efficient ways to initialize
structs. - Using
typedefto create cleaner, more readable code. - An "under the hood" look at how
structsare laid out in memory, a concept known as padding.
This is your first step towards building complex data structures and organizing your C programs effectively.
1. What is a Struct?
So far, we've worked with individual primitive types (int, char, etc.) and arrays of those types. But what if you need to represent a more complex entity? For your music notation project, a single musical note has several related attributes: a pitch (like "C#4"), a duration (like 0.5 seconds), and a velocity (how hard the key was pressed).
An array isn't a good fit here because the data types are different (string, float, int). This is exactly where struct comes in. A struct (short for structure) is a user-defined data type that groups together variables of potentially different types under a single name.

The video below provides a great introduction to the basic syntax and usage.
struct Basics | C Programming Tutorial
Let's watch the first part of the 'struct Basics' video from Portfolio Courses. It clearly explains how to define a struct and how to declare variables and access their members.
Please watch from the beginning to 04:55. Pay attention to: The motivation for using a struct over separate variables. The syntax for defining a struct Student. How to declare a variable of this new type (struct student kevin;). The use of the dot operator (.) to access and modify individual members (e.g., kevin.age).
As you saw, the dot operator (.) is the key to working with struct variables. You use it to "get inside" the structure and access its individual members, whether you're assigning a value to them or reading a value from them.
2. Initialization and typedef
Assigning values to each member one by one can be tedious. C provides more convenient ways to initialize a struct when you declare it. Furthermore, you might have noticed the struct student ... syntax is a bit verbose. There's a common C idiom to make this cleaner.
The next segment of the video covers these two important topics.
struct Basics | C Programming Tutorial
Let's continue with the same video to learn about initialization shortcuts and using typedef to simplify your code.
Please watch the following two segments: Cleaner Declarations with typedef (06:44 - 08:20): This part explains how typedef can create an alias for your struct type, allowing you to declare variables more concisely (e.g., Student kevin; instead of struct student kevin;). Initialization Methods (10:43 - 13:23): This section demonstrates two powerful ways to initialize structs: using an initializer list {...} and using designated initializers {.member = value, ...}. The designated initializer method is particularly useful for readability and avoiding errors.
To summarize the initialization methods:
- Initializer List:
Point p1 = {5, 10};- Values are assigned to members in the order they are defined in the
struct. - Simple, but fragile. If you reorder members in the
structdefinition, you must update all your initializations.
- Values are assigned to members in the order they are defined in the
- Designated Initializers (C99+):
Point p2 = {.y = 8, .x = 2};- Explicitly names which member gets which value.
- The order doesn't matter.
- Highly recommended for clarity and robustness.
3. Under the Hood: Structure Padding
Your interest in how things work at a low level will make this next topic particularly relevant. Let's consider a simple struct:
struct Example {
char a; // sizeof(char) is 1
int b; // sizeof(int) is 4
};
You might logically assume that sizeof(struct Example) would be 1 + 4 = 5 bytes. Let's test that.
#include <stdio.h>
struct Example {
char a;
int b;
};
int main() {
printf("Size of char: %zu\n", sizeof(char));
printf("Size of int: %zu\n", sizeof(int));
printf("Size of struct Example: %zu\n", sizeof(struct Example));
return 0;
}
// Probable Output:
// Size of char: 1
// Size of int: 4
// Size of struct Example: 8
Why 8 bytes instead of 5? The answer is structure padding.
CPUs are most efficient when they read data from memory addresses that are multiples of their "word size" (e.g., multiples of 4 or 8). To ensure members are properly aligned for fast access, the compiler often inserts unused "padding" bytes into a struct.
This video gives an excellent visual explanation of why this happens and how you can influence it.
Structure Padding | C Programming Tutorial
The video 'Structure Padding' by Portfolio Courses explains this memory-vs-performance trade-off.
Watch the following parts: The sizeof Mystery (00:00 - 01:59): See the problem demonstrated with sizeof. Why Padding Exists (01:59 - 04:06): Understand how padding helps the CPU perform fewer reads, improving performance. Minimizing Padding (05:34 - 06:28): Learn the practical tip of ordering struct members from largest to smallest to reduce memory usage.
The key takeaway for practical purposes is this: As a general rule, define your struct members in descending order of size (e.g., double, int, short, char). This minimizes the need for padding and results in a more compact memory layout, which can be particularly important in memory-constrained environments like the embedded systems you're interested in.
For a comprehensive text reference on all the topics we've covered today—defining, initializing, typedef, and padding—the GeeksforGeeks article on C Structures (LINK) is an excellent resource to bookmark.
Practical Challenge
Let's apply these concepts to your graduation project. Your task is to define a struct that can represent a single musical note.
- Define a
structnamedNotethat contains the following members:- A character array to store the pitch (e.g., "C#4"). Let's give it a size of 4 to be safe.
- A
floatfor the duration in beats (e.g., 1.0 for a quarter note). - An
intfor the MIDI velocity (0-127).
- Use
typedefso you can declare variables asNote my_note;. - In your
mainfunction, declare aNotevariable and initialize it using designated initializers. - Print out the members of your
Notevariable to confirm it was initialized correctly.
Click here for a sample solution
#include <stdio.h>
// 1 & 2: Define the struct and use typedef
typedef struct {
char pitch[4];
float duration;
int velocity;
} Note;
int main() {
// 3: Declare and initialize a Note variable using designated initializers
Note note1 = {
.pitch = "G5",
.duration = 0.5,
.velocity = 110
};
// 4: Print the members to verify
printf("Musical Note:\n");
printf(" Pitch: %s\n", note1.pitch);
printf(" Duration: %.2f beats\n", note1.duration);
printf(" Velocity: %d\n", note1.velocity);
return 0;
}
Conclusion
In this lesson, you've learned how to create your own custom data types in C using struct. This is a fundamental tool for organizing complex data, making your code more readable, and laying the groundwork for advanced data structures.
Key Takeaways:
structis used to group related variables (members) of different types into a single unit.- The dot operator (
.) is used to access the members of astructvariable. - Designated initializers (
.member = value) are the safest and most readable way to initialize astruct. typedefis a common idiom used to create a simple alias for astructtype, cleaning up variable declarations.- Structure padding is an optimization where the compiler adds bytes to a
structto align members in memory, improving performance at the cost of some space. Ordering members by size can minimize this.
In our next lesson, we will see how these new data types interact with functions. We'll explore what it means to pass structs to functions by value and by reference (using pointers), a topic that will tie together everything you've learned about pointers, memory, and now, data structures.