advanced-c Lesson 1 22 min read

How Do I Define and Use Structs and Unions?

C's version of classes — data without methods, and why that's enough

Reading: C Text: Ch. 10 §1–4 (pp. 593–640, Structs, Members, Structs as Arguments)

After this lesson, you will be able to:

  • Define a struct using typedef and initialize struct variables
  • Access struct fields with the dot operator and use strcpy for char[] 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 Student class bundles name, id, gpa with methods like getName() and toString(). A C struct is just the data. You write print_student(s) as a standalone function, not s.print(). Both use dot syntax for field access; C just doesn’t have methods.

Check Your Understanding
Given the declaration Student s1;, which line correctly sets the student's name to "Alice"?
A s1.name = "Alice";
B s1->name = "Alice";
C strcpy(s1.name, "Alice");
D strcpy(s1->name, "Alice");
Answer: C. You can't assign to a 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.

Check Your Understanding
What happens when you write Student s2 = s1; where s1 has char name[50] as a field?
A All bytes of the struct are copied, including the 50-byte name array
B Only the pointer to the name is copied — both structs share the same string
C Compile error — you can't assign structs with =
D Only numeric fields are copied; string fields require strcpy
Answer: A. Struct assignment copies every byte. With 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.


Check Your Understanding
You have Student roster[30]; and want to print the GPA of the third student. Which expression is correct?
A roster.gpa[2]
B roster[3].gpa
C roster->gpa[2]
D roster[2].gpa
Answer: D. Arrays are zero-indexed, so the third student is 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.