How Do I Define and Use Structs and Unions?
C's version of classes — data without methods, and why that's enough
After this lesson, you will be able to:
- Define a struct using
typedefand initialize struct variables - Access struct fields with the dot operator and use
strcpyforchar[]fields - Create and iterate over an array of structs
- Explain why structs cannot be compared with
==
Data Without Methods
In Java, a Student is a class with fields and methods bundled together. In C, a struct is just the fields — the data, with no methods attached. Functions that operate on structs are written separately.
This feels like a step backward from OOP. But it’s actually how most systems software works: data structures are defined in headers, and functions that manipulate them live in source files. Clean separation of data and behavior.
Defining and Using Structs
Basic Struct Definition
typedef struct
{
char name[50];
int id;
double gpa;
} Student;
typedef lets you use Student as the type name instead of struct Student:
Student s1; // Declare a Student variable
s1.id = 12345; // Access fields with dot operator
s1.gpa = 3.85;
strcpy(s1.name, "Alice Chen"); // Can't assign strings with =
Initialization
Student s2 = {"Bob Park", 67890, 3.42}; // Positional
Student s3 = {.name = "Carol", .id = 11111}; // Designated (C99)
Arrays of Structs
Student roster[30];
roster[0].id = 12345;
strcpy(roster[0].name, "Alice");
roster[0].gpa = 3.85;
// Loop through
for (int i = 0; i < num_students; i++)
{
printf("%-20s %d %.2f\n", roster[i].name, roster[i].id, roster[i].gpa);
}
From Java: A Java
Studentclass bundlesname,id,gpawith methods likegetName()andtoString(). A C struct is just the data. You writeprint_student(s)as a standalone function, nots.print(). Both use dot syntax for field access; C just doesn’t have methods.
Student s1;, which line correctly sets the student's name to "Alice"?char array with = — that only works at initialization. You need strcpy. Option A fails because array assignment isn't allowed in C. Options B and D use ->, which requires a pointer to a struct, not a struct value.
Struct Assignment
Assigning one struct to another copies all fields (shallow copy):
Student s1 = {"Alice", 12345, 3.85};
Student s2 = s1; // Copies all fields: name, id, gpa
Common Pitfall: You cannot compare structs with
==.if (s1 == s2)is a compile error. You must compare field by field:if (s1.id == s2.id && strcmp(s1.name, s2.name) == 0 && ...).
Using Fixed-Size char Arrays
Notice we used char name[50] instead of char *name. This keeps things simpler — the string is stored directly inside the struct, avoiding separate heap allocation:
typedef struct
{
char name[50]; // String lives inside the struct
int id;
double gpa;
} Student;
With char *name, you’d need to separately calloc space for the string and free it — an extra level of memory management. For labs, fixed-size arrays keep things manageable.
Passing Structs to Functions
Pass by value (copies the entire struct):
void print_student(Student s)
{
printf("%-20s %5d %.2f\n", s.name, s.id, s.gpa);
}
For large structs, pass by pointer to avoid copying:
void print_student(const Student *s)
{
printf("%-20s %5d %.2f\n", s->name, s->id, s->gpa);
}
The -> operator is covered in the next lesson.
Student s2 = s1; where s1 has char name[50] as a field?char name[50], the entire 50-byte array lives inside the struct and gets copied. Option B would be a concern with char *name (a pointer field), not a fixed array. Option C is wrong — C does allow struct assignment. Option D is a common misconception; struct assignment copies all fields regardless of type.
Why does this matter?
Understanding shallow vs. deep copying of structs is critical when you start passing structs to functions and returning them. If your struct has pointer fields instead of fixed arrays, a simple assignment creates shared references — modifying one copy silently corrupts the other.
Quick Check: Why can't you compare two structs with ==?
C doesn’t define == for structs. The compiler can’t know which fields matter for equality, and there might be padding bytes between fields that could differ. You must compare each field individually.
Quick Check: What's the difference between char name[50] and char *name in a struct?
char name[50] stores the string directly inside the struct — no separate allocation needed. char *name stores a pointer; the actual string must be allocated separately on the heap and freed when done. Fixed arrays are simpler but waste space if the string is short.
Quick Check: Does Student s2 = s1 make a deep copy?
It makes a shallow copy — all bytes of the struct are copied. For fixed-size char arrays inside the struct, this works correctly (the bytes are copied). But if the struct contains char * pointers, both copies point to the same string — modifying one affects the other.
Student roster[30]; and want to print the GPA of the third student. Which expression is correct?roster[2]. Access its field with dot notation: roster[2].gpa. Option A has the syntax backwards. Option B accesses the fourth student (off by one). Option C incorrectly uses the arrow operator on an array.
Big Picture: Structs are C’s mechanism for grouping related data into a single unit. They’re the foundation for everything in Series 4 — file I/O reads records into structs, sorting rearranges struct arrays, and generic containers store structs through void pointers. Every serious C program uses structs to organize its data.
Data Structures Start Here
Structs are the foundation for everything in Series 4. With a Student struct, you can build arrays of students, read them from files, sort them by different fields, and eventually store them in linked lists.
Next: struct pointers and the arrow operator. When you dynamically allocate structs or pass them to functions by pointer, s.field becomes s->field.