Stat Mania
  • C Programming
  • All Courses
  1. Arrays and Structs
  2. Structs in C: Grouping Related Data (with Python and R Comparisons)
  • Course Home
  • Getting Started
    • C's "Hello, World!" Explained Line by Line (for R & Python Users)
    • How to Test a Small C Snippet Quickly
  • Variables and Data Types
    • Declaring Multiple Variables in One Line in C
    • float vs double in C: Precision, Memory, and When to Use Which
  • Operators and Input
    • Pre-Increment vs Post-Increment in C: ++i vs i++
    • Checking User Input in C: Is It Really a Number?
  • Making Decisions
    • Nested if in C, Explained with Real-Life Examples
    • Shortcut Ways to Write if in C: the Ternary Operator and More
    • The switch Statement in C: Syntax, break, Fall-Through and Examples
  • Loops
    • The do-while Loop in C: Run First, Check After
    • Why Use break in a while Loop If the Condition Already Stops It?
    • Printing a Comma-Separated List in C (No Trailing Comma, Clean Line Breaks)
    • 5 while Loop Practice Problems in C (with Hints and Solutions)
    • 5 for Loop Practice Problems in C (with Hints and Solutions)
    • 5 Nested for Loop Practice Problems in C (with Hints and Solutions)
  • Arrays and Structs
    • How to Save Loop Results in an Array in C
    • Structs in C: Grouping Related Data (with Python and R Comparisons)
  • Random Numbers
    • srand(time(NULL)) in C: Why Random Numbers Repeat and How to Seed Them
    • Tossing a Die in C Until We Get a 6

On this page

  • The Problem Structs Solve
  • Example 1: A Student Record
  • Example 2: Multiple Students, Passed to a Function
  • Example 3: A Point in 2D Space
  • Example 4: A Linked List Node (Preview of What’s Coming)
  • Structs vs. Arrays
  • Structs vs. Python Dicts / R Lists
  • A Few Rules and Gotchas
  • When Should We Use a Struct?
  • A Practical Summary
  1. Arrays and Structs
  2. Structs in C: Grouping Related Data (with Python and R Comparisons)

Structs in C: Grouping Related Data (with Python and R Comparisons)

How C structs group different types into one unit: student records, arrays of structs, points, linked-list nodes, typedef, ->, padding, and how structs compare with Python dicts and R lists.
Reading 10 min

By the end of this lesson you will

  • Group different types into one struct
  • Use arrays of structs, typedef and ->
  • Compare structs with Python dicts and R lists

Resources

  • Struct types

A struct Student occupies 60 bytes: name uses 50, then 2 bytes of padding, then id uses 4 and gpa uses 4.

A struct is one block of memory holding several fields, with padding between them

A struct (short for “structure”) lets us group related variables of different types into a single unit. It’s the C way of saying “these pieces of data belong together.”

This is something used more explicitly in R and Python. In Python, we’d write:

student = {"name": "Alice", "id": 101, "gpa": 3.8}

In R:

student <- list(name = "Alice", id = 101, gpa = 3.8)

In C, we use a struct to build this same idea — but with strict typing and a fixed layout.

The Problem Structs Solve

Suppose we want to track a student’s name, ID, and GPA. Without structs, we’d need three separate arrays:

char names[100][50];
int ids[100];
float gpas[100];

This works, but it has problems: - The data for one student is scattered across three arrays. - If we pass a student to a function, we must pass three separate arguments. - If we add a new field (say, email), we must update every function signature. - It’s easy to keep the arrays out of sync — e.g., swap ids[3] with names[3] accidentally.

A struct fixes all of this by keeping the fields together.

Example 1: A Student Record

#include <stdio.h>
#include <string.h>

struct Student {
    char name[50];
    int id;
    float gpa;
};

int main(void) {
    // Declare and initialize
    struct Student s1;
    strcpy(s1.name, "Alice");
    s1.id = 101;
    s1.gpa = 3.8;

    // Access and print
    printf("Name: %s\n", s1.name);
    printf("ID:   %d\n", s1.id);
    printf("GPA:  %.2f\n", s1.gpa);

    return 0;
}

Key points:

  • struct Student defines a new type. It’s a template — no memory is allocated until we declare a variable of that type.
  • s1 is a variable of type struct Student. It contains all three fields contiguously in memory.
  • The . (dot) operator accesses individual fields: s1.name, s1.id, s1.gpa.
  • The fields add up to 50 + 4 + 4 = 58 bytes, but sizeof(struct Student) is actually 60 on typical systems because of padding (see the diagram above and the gotchas below).

Example 2: Multiple Students, Passed to a Function

This is where structs really shine: we can pass an entire record as one argument.

#include <stdio.h>
#include <string.h>

struct Student {
    char name[50];
    int id;
    float gpa;
};

void printStudent(struct Student s) {
    printf("%-10s  ID: %3d  GPA: %.2f\n", s.name, s.id, s.gpa);
}

int main(void) {
    struct Student roster[3] = {
        {"Alice",   101, 3.8},
        {"Bob",     102, 3.2},
        {"Charlie", 103, 3.9}
    };

    for (int i = 0; i < 3; i++) {
        printStudent(roster[i]);
    }

    return 0;
}

Output:

Alice       ID: 101  GPA: 3.80
Bob         ID: 102  GPA: 3.20
Charlie     ID: 103  GPA: 3.90

Things to notice:

  • An array of structs (struct Student roster[3]) is how we build a “table” of records. This is the C equivalent of a data frame with 3 rows.
  • Initializer syntax: {"Alice", 101, 3.8} matches the fields in declaration order.
  • printStudent takes one argument and prints all three fields. Compare this to a function that would need char name[], int id, float gpa as three separate parameters — structs are cleaner.

Example 3: A Point in 2D Space

A simpler, more geometric example:

#include <stdio.h>
#include <math.h>

struct Point {
    double x;
    double y;
};

double distance(struct Point a, struct Point b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx*dx + dy*dy);
}

int main(void) {
    struct Point p1 = {0.0, 0.0};
    struct Point p2 = {3.0, 4.0};

    printf("Distance = %.2f\n", distance(p1, p2));   // Distance = 5.00

    return 0;
}

Compile with gcc point.c -o point -lm: on many systems sqrt lives in the math library, which must be linked explicitly.

This is a pattern we’ll use constantly in geometry, graphics, and simulation work. The distance function reads as “distance between two points” — much clearer than distance(x1, y1, x2, y2).

Example 4: A Linked List Node (Preview of What’s Coming)

Structs become essential when we build data structures. A linked list node in C looks like this:

struct Node {
    int data;
    struct Node *next;   // pointer to another Node
};

Notice the struct contains a pointer to its own type. This is how linked lists, trees, and graphs are built. We cannot do this with plain arrays. (The field must be a pointer to struct Node: a struct cannot contain itself by value.)

Structs vs. Arrays

Feature Array Struct
Holds elements of… Same type Different types
Access by… Index (arr[0]) Field name (s.name)
Size Determined by element count Sum of all field sizes
Purpose List of similar items One composite item

A useful way to think about it:

Array = many of the same thing. Struct = one thing made of many parts.

A struct Student roster[30] combines both: an array (many) of structs (composite).

Structs vs. Python Dicts / R Lists

C struct Python dict R list
Fields fixed at compile time Keys added at runtime Names can be added dynamically
Fields are typed Values can be any type Values can be any type
Access with . Access with [] Access with $ or [[]]
Cannot store in a JSON-like flexible way Very flexible Very flexible

The trade-off: structs are rigid but fast and type-safe. Python dicts and R lists are flexible but slower and error-prone. For performance-critical code (which C is used for), structs win.

A Few Rules and Gotchas

  1. Always end with a semicolon. struct Student { ... }; — the semicolon after } is mandatory. Forgetting it is a common beginner error.

  2. We can use typedef to shorten the name:

    typedef struct {
        char name[50];
        int id;
        float gpa;
    } Student;
    
    Student s1;   // no "struct" prefix needed

    This is very common in modern C code. Most real-world C uses typedef.

  3. We cannot compare structs with ==. This won’t work:

    if (s1 == s2) { ... }   // compile error

    We must compare field by field, or use memcmp (with caution due to padding).

  4. Structs can be passed by value or by pointer. Passing by value copies the whole struct (potentially many bytes). Passing by pointer avoids the copy:

    void printStudent(struct Student *s) {
        printf("%s\n", s->name);   // note the -> operator
    }

    The -> operator is shorthand for (*s).name. We’ll see it everywhere once we start using pointers with structs.

  5. Memory padding. The compiler may insert unused bytes between fields to align them properly. Here sizeof(struct Student) is 60 rather than 58: name ends at byte 50, but the int must start at a multiple of 4, so 2 bytes of padding are added (id at offset 52, gpa at 56). This usually doesn’t matter, but it’s why we shouldn’t assume a struct’s size is the sum of its field sizes.

When Should We Use a Struct?

We use a struct whenever two or more pieces of data logically belong together:

  • A point (x, y) or (x, y, z)
  • A date (year, month, day)
  • A student (name, id, gpa)
  • A bank account (number, balance, owner)
  • A network packet (source, destination, payload)
  • A configuration (width, height, color depth)
  • A complex number (real, imaginary)

If we find ourselves passing the same set of variables to multiple functions, or keeping parallel arrays in sync, that’s a signal to define a struct.

A Practical Summary

Let’s start with Example 1 (student record) and Example 3 (point): write them, compile them, run them. Then we can modify them:

  • Add a new field to the Student struct (e.g., char email[100]) and update the print function.
  • Write a function that takes two points and returns the midpoint (another struct Point).
  • Build an array of 5 students and sort them by GPA.

Once we’re comfortable with these, structs will become a natural tool for organizing our data, and we’ll be ready for pointer-based data structures such as linked lists, trees and hash tables.

  • A struct groups fields of different types into one value.
  • Use . on a struct and -> on a pointer to one.
  • typedef gives the struct a shorter name.

Padding. Unused bytes the compiler inserts between fields to keep them aligned.

Back to top
Stat Mania

Free, hands-on statistics and programming courses that run right in your browser.

Courses

All courses R Programming Statistics Probability

Resources

Practice quizzes Tools & games Quarto docs Course kit on GitHub

Company

Home About Contact Privacy policy Terms of use
© Stat Mania. All rights reserved.
How to Save Loop Results in an Array in C
Random Numbers