1. The Core Idea: Creating Your Own Data Type
Think about the
basic data types you know: int (for numbers), char (for letters), float (for decimals). These are great, but they only store *one
piece* of information.
What if you want to store information about a Student? A student isn't just one thing. A student has:
·
A name (a char array)
·
A roll number (an int)
·
A GPA (a float)
It's very inconvenient to store these as separate variables:
char s1_name[50] = "Alice";
int s1_roll = 101;
float s1_gpa = 3.8;
char s2_name[50] = "Bob";
int s2_roll = 102;
float s2_gpa = 3.5;
This is messy, hard to manage, and impossible to pass to a function easily. A structure solves this problem.
What is a Structure (struct)?
A struct is a user-defined data type that
groups together *different* data types into a single unit. It's like a
blueprint for a custom "package" of variables.
Instead of three separate variables, you
can create one struct Student variable that *contains* the name,
roll number, and GPA all in one place.
2. Defining a Structure
You define a
structure using the struct keyword, followed by a "tag" (the name of
your new type), and a list of "members" (the variables inside) in
curly braces.
Syntax (Blueprint):
struct <tag_name> {
<data_type> member1; <data_type> member2; ...};Example (Our Student Blueprint):
// This is just a blueprint. No memory is used yet.
struct Student {
char name[50];
int roll;
float gpa;
};This code tells C:
"I've invented a new type called struct Student. Any variable of this type will hold a
50-char array, an int, and a float."
3. Creating and Using Structure Variables
Once you have the blueprint, you can create *variables* of that type.
A. Creating Variables
int main() {
// Create a variable 's1' of type 'struct Student'
// This reserves space on the stack for one char[50], one int, and one float.
struct Student s1;
// You can also create and initialize at the same time
struct Student s2 = {"Bob", 102, 3.5};
return 0;
}B. Accessing
Members (The Dot . Operator)
To get or set the
data *inside* a structure variable, you use the dot operator (.).
You read it as "variable.member".
// Using the 's1' variable we created earlier
// Assign values to the members of s1
// Note: We use strcpy for strings!
strcpy(s1.name, "Alice");
s1.roll = 101; s1.gpa = 3.8; // Read (print) the values from s1
printf("Student Name: %s\n", s1.name);
printf("Student Roll: %d\n", s1.roll);
printf("Student GPA: %.2f\n", s1.gpa);
// You can also read from s2
printf("Student 2 Name: %s\n", s2.name);
4. Extra
Content: typedef (Making Code Cleaner)
Writing struct
Student every
time is annoying. C provides a keyword called typedef to create an *alias* or *nickname*
for a data type.
We can combine our struct definition with typedef:
// Define 'struct Student' and give it the nickname 'Student'
typedef struct Student {
char name[50];
int roll;
float gpa;
} Student; // <-- 'Student' is now the new name
Now, our code from before becomes *much* cleaner:
int main() {
// Instead of 'struct Student s1;'
Student s1; // Instead of 'struct Student s2 = ...'
Student s2 = {"Bob", 102, 3.5};
strcpy(s1.name, "Alice");
s1.roll = 101; printf("Name: %s\n", s1.name);
return 0;
}This is the standard, modern way to use structures in C.
5. Extra
Content: Pointers to Structures (-> Operator)
This is where
everything comes together. You will *rarely* use structure variables directly.
You will almost *always* use pointers to structures,
especially with dynamic memory (malloc).
Let's create a
pointer to our s1 variable:
Student s1;strcpy(s1.name, "Alice");
s1.roll = 101; // Create a pointer 'ptr' that holds the address of 's1'
Student* ptr = &s1;How do we
access the members using the pointer ptr?
Method 1: The "Clunky" Way (Dereference and Dot)
We can dereference
the pointer (*ptr) to get the *actual struct*, and then use the dot operator.
// The parentheses are CRITICAL due to operator precedence
printf("Name: %s\n", (*ptr).name);
(*ptr).gpa = 3.9; // We can also assign values
Method 2:
The "Clean" Way (The Arrow -> Operator)
The (*ptr).member syntax is ugly and error-prone. C
provides a special operator, the arrow operator (->), as a
shortcut.
The expression ptr->member is exactly identical to (*ptr).member.
// This is the preferred way!
printf("Name: %s\n", ptr->name);
printf("Roll: %d\n", ptr->roll);
// Assign a new value
ptr->gpa = 3.9;Rule of Thumb:
·
If you have a structure variable, use
the dot operator (.). (e.g., s1.name)
·
If you have a pointer to a structure, use
the arrow operator (->). (e.g., ptr->name)
This is why in the "Self-Referential Structures" note,
we used head->data and head->next. head was a *pointer* to a struct Node.
6. Complete
Example (with typedef and malloc)
This code ties it
all together: typedef, struct, malloc, and the -> operator.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 1. Define the blueprint with typedef
typedef struct Student {
char name[50];
int roll;
float gpa;
} Student; int main() {
// 2. Create a POINTER, not a variable
Student* s1_ptr; // 3. Allocate memory from the HEAP
s1_ptr = (Student*) malloc(sizeof(Student));
// 4. Check if malloc failed
if (s1_ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
} // 5. Assign values using the ARROW (->) operator
strcpy(s1_ptr->name, "Alice");
s1_ptr->roll = 101; s1_ptr->gpa = 3.8; // 6. Read values using the ARROW (->) operator
printf("--- Student Record ---\n");
printf("Name: %s\n", s1_ptr->name);
printf("Roll: %d\n", s1_ptr->roll);
printf("GPA: %.2f\n", s1_ptr->gpa);
// 7. Don't forget to free the memory!
free(s1_ptr);
s1_ptr = NULL; return 0;
}