Docs

Structures and Unions

Structure Basics in C

Table of Contents

  1. •Introduction
  2. •What is a Structure?
  3. •Declaring Structures
  4. •Creating Structure Variables
  5. •Accessing Structure Members
  6. •Initializing Structures
  7. •Structure Assignment
  8. •Size of Structures
  9. •Structure Padding and Alignment
  10. •typedef with Structures
  11. •Anonymous Structures
  12. •Structures vs Arrays
  13. •Common Use Cases
  14. •Best Practices
  15. •Summary

Introduction

Structures are one of the most powerful features in C programming. They allow you to group related variables of different data types under a single name, creating custom data types that can represent real-world entities.

Why Do We Need Structures?

Consider storing information about a student:

  • •Name (string)
  • •Roll number (integer)
  • •GPA (float)
  • •Age (integer)

Without structures, you would need separate arrays for each attribute:

char names[100][50];
int roll_numbers[100];
float gpas[100];
int ages[100];

This approach has problems:

  • •Data is scattered across multiple arrays
  • •Difficult to manage related data together
  • •Easy to make indexing mistakes
  • •Cannot pass all student data as a single unit

With structures, all related data stays together:

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

What is a Structure?

A structure is a user-defined data type that groups together variables of different types under a single name. Each variable within a structure is called a member or field.

Key Characteristics

FeatureDescription
HeterogeneousCan contain different data types
Contiguous MemoryMembers stored in consecutive memory locations
Named AccessMembers accessed by name, not index
User-DefinedYou define the structure template
Value TypeStructures are passed by value (copied)

Visual Representation

struct Student
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  name[50]     │  roll_number  │  gpa    │  age    │
│  (50 bytes)   │  (4 bytes)    │(4 bytes)│(4 bytes)│
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Declaring Structures

Basic Syntax

struct structure_name {
    data_type member1;
    data_type member2;
    // ... more members
};

Example: Student Structure

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

Example: Point Structure

struct Point {
    int x;
    int y;
};

Example: Date Structure

struct Date {
    int day;
    int month;
    int year;
};

Example: Complex Structure

struct Employee {
    char name[100];
    int emp_id;
    char department[50];
    float salary;
    struct Date joining_date;  // Nested structure
    int is_active;
};

Important Notes

  1. •Semicolon Required: Structure declaration MUST end with semicolon
  2. •No Memory Allocated: Declaration only creates a template
  3. •Scope: Structure declaration follows normal scoping rules
  4. •Self-Reference: Structure can contain pointer to itself

Creating Structure Variables

Method 1: Declare Then Define Variables

// Declaration
struct Student {
    char name[50];
    int roll_number;
    float gpa;
};

// Define variables
struct Student s1;
struct Student s2, s3;

Method 2: Declare and Define Together

struct Student {
    char name[50];
    int roll_number;
    float gpa;
} s1, s2, s3;  // Variables declared here

Method 3: Using typedef

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

Student s1;  // No 'struct' keyword needed
Student s2;

Global vs Local Structure Variables

struct Point {
    int x;
    int y;
};

struct Point global_point;  // Global variable (initialized to 0)

int main() {
    struct Point local_point;   // Local variable (uninitialized)
    static struct Point static_point;  // Static (initialized to 0)

    return 0;
}

Accessing Structure Members

Dot Operator (.)

The dot operator is used to access members of a structure variable.

struct Student s1;

// Assigning values
s1.roll_number = 101;
s1.gpa = 3.75;
s1.age = 20;
strcpy(s1.name, "John Doe");

// Reading values
printf("Name: %s\n", s1.name);
printf("Roll: %d\n", s1.roll_number);
printf("GPA: %.2f\n", s1.gpa);

Accessing Nested Structure Members

struct Date {
    int day, month, year;
};

struct Employee {
    char name[50];
    struct Date dob;
    struct Date joining;
};

struct Employee emp;

// Access nested members with multiple dots
emp.dob.day = 15;
emp.dob.month = 8;
emp.dob.year = 1990;

emp.joining.year = 2020;

Arrow Operator (->)

Used with structure pointers (covered in Pointers to Structures topic):

struct Student *ptr = &s1;

// Using arrow operator
ptr->roll_number = 101;
printf("%s\n", ptr->name);

// Equivalent to
(*ptr).roll_number = 101;

Initializing Structures

Method 1: Member-by-Member

struct Student s1;
s1.roll_number = 101;
s1.gpa = 3.75;
s1.age = 20;
strcpy(s1.name, "John");

Method 2: Initializer List

struct Student s1 = {"John Doe", 101, 3.75, 20};

Members must be in the same order as declared.

Method 3: Designated Initializers (C99)

struct Student s1 = {
    .name = "John Doe",
    .roll_number = 101,
    .gpa = 3.75,
    .age = 20
};

Benefits:

  • •Order doesn't matter
  • •Self-documenting code
  • •Unspecified members initialized to zero

Method 4: Partial Initialization

struct Student s1 = {"John"};  // Only name initialized
// roll_number, gpa, age are set to 0

Method 5: Zero Initialization

struct Student s1 = {0};  // All members set to zero/null

Initializing Nested Structures

struct Employee emp = {
    .name = "Alice",
    .dob = {15, 8, 1990},
    .joining = {.day = 1, .month = 6, .year = 2020}
};

Structure Assignment

Direct Assignment

Structures can be directly assigned to each other (unlike arrays).

struct Student s1 = {"John", 101, 3.75, 20};
struct Student s2;

s2 = s1;  // Complete copy - all members copied

What Happens During Assignment

struct Student s1 = {"John", 101, 3.75, 20};
struct Student s2 = s1;  // Copy initialization

// After assignment:
// s2.name = "John" (copied character by character)
// s2.roll_number = 101
// s2.gpa = 3.75
// s2.age = 20

// s1 and s2 are independent - changing one doesn't affect other
s2.roll_number = 102;  // s1.roll_number still 101

Important: Shallow Copy

Structure assignment performs a shallow copy:

struct Data {
    int value;
    char *ptr;  // Pointer member
};

struct Data d1;
d1.value = 100;
d1.ptr = malloc(50);
strcpy(d1.ptr, "Hello");

struct Data d2 = d1;  // Shallow copy

// d1.ptr and d2.ptr point to SAME memory!
// Modifying d2.ptr affects d1.ptr
// Freeing one leaves the other as dangling pointer

Comparing Structures

Structures cannot be compared directly with == operator:

struct Point p1 = {10, 20};
struct Point p2 = {10, 20};

// if (p1 == p2)  // ERROR! Won't compile

// Must compare member by member
if (p1.x == p2.x && p1.y == p2.y) {
    printf("Points are equal\n");
}

Size of Structures

Using sizeof Operator

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

printf("Size: %zu bytes\n", sizeof(struct Student));

Size May Not Be Sum of Members

Due to padding, structure size can be larger than the sum of member sizes:

struct Example {
    char a;     // 1 byte
    int b;      // 4 bytes
    char c;     // 1 byte
};

// Expected: 1 + 4 + 1 = 6 bytes
// Actual: 12 bytes (due to padding)

Structure Padding and Alignment

Why Padding?

CPUs access memory most efficiently when data is aligned to certain boundaries. The compiler adds padding bytes to ensure proper alignment.

Alignment Rules

Data TypeTypical Alignment
char1 byte
short2 bytes
int4 bytes
float4 bytes
double8 bytes
pointer4 or 8 bytes

Visual Example

struct Padded {
    char a;     // 1 byte
    // 3 bytes padding
    int b;      // 4 bytes
    char c;     // 1 byte
    // 3 bytes padding
};
// Total: 12 bytes

Memory Layout:
ā”Œā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  a  │  padding    │       b         │  c  │  padding    │
│ 1B  │    3B       │       4B        │ 1B  │     3B      │
ā””ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
Offset: 0    1-3          4-7           8       9-11

Minimizing Padding

Order members from largest to smallest:

// Poorly ordered - 12 bytes
struct Bad {
    char a;     // 1 + 3 padding
    int b;      // 4
    char c;     // 1 + 3 padding
};

// Well ordered - 8 bytes
struct Good {
    int b;      // 4
    char a;     // 1
    char c;     // 1 + 2 padding
};

Packing Structures

Force no padding (may reduce performance):

// GCC specific
struct __attribute__((packed)) Packed {
    char a;
    int b;
    char c;
};
// Size: 6 bytes (no padding)

// Or use pragma
#pragma pack(push, 1)
struct Packed {
    char a;
    int b;
    char c;
};
#pragma pack(pop)

typedef with Structures

Basic Usage

// Without typedef
struct Student {
    char name[50];
    int id;
};
struct Student s1;  // Need 'struct' keyword

// With typedef
typedef struct {
    char name[50];
    int id;
} Student;
Student s1;  // No 'struct' keyword needed

Named Structure with typedef

typedef struct Student {
    char name[50];
    int id;
    struct Student *next;  // Self-reference using struct name
} Student;

// Can use either:
struct Student s1;
Student s2;

Multiple typedefs

typedef struct Point {
    int x, y;
} Point, *PointPtr;

Point p1;           // Structure variable
PointPtr ptr = &p1; // Pointer to structure

typedef for Function Pointers in Structures

typedef void (*Callback)(int);

typedef struct {
    int value;
    Callback on_change;
} Observable;

Anonymous Structures

Basic Anonymous Structure

struct {
    int x;
    int y;
} point1, point2;  // Only these two variables can exist

point1.x = 10;
point2.y = 20;

// Cannot create more variables of this type!
// struct ??? point3;  // Error - no name to refer to

Anonymous Structure with typedef

typedef struct {
    int x;
    int y;
} Point;

Point p1, p2, p3;  // Can create as many as needed

Anonymous Structures Inside Structures (C11)

struct Container {
    int id;
    struct {
        int x;
        int y;
    };  // Anonymous member
};

struct Container c;
c.id = 1;
c.x = 10;   // Direct access - no intermediate name
c.y = 20;

Structures vs Arrays

FeatureStructureArray
Data TypesDifferent typesSame type
AccessBy member nameBy index
AssignmentDirect copy allowedMust copy element by element
ComparisonMust compare membersMust compare elements
SizeMay include paddingExact: n Ɨ element_size
FlexibilityVery flexibleFixed structure
Use CaseRelated different dataCollection of same data

Example Comparison

// Array - homogeneous data
int scores[5] = {90, 85, 78, 92, 88};
for (int i = 0; i < 5; i++) {
    printf("%d ", scores[i]);
}

// Structure - heterogeneous data
struct Result {
    char subject[20];
    int score;
    char grade;
};
struct Result r = {"Math", 90, 'A'};
printf("%s: %d (%c)\n", r.subject, r.score, r.grade);

Common Use Cases

1. Representing Real-World Entities

struct Car {
    char brand[30];
    char model[30];
    int year;
    float price;
    int mileage;
};

2. Grouping Related Configuration

struct Config {
    int max_connections;
    int timeout_seconds;
    char log_file[256];
    int debug_mode;
};

3. Function Return Multiple Values

struct DivResult {
    int quotient;
    int remainder;
};

struct DivResult divide(int a, int b) {
    struct DivResult result;
    result.quotient = a / b;
    result.remainder = a % b;
    return result;
}

4. Linked Data Structures

struct Node {
    int data;
    struct Node *next;  // Self-referential
};

5. Date/Time Representation

struct DateTime {
    int year, month, day;
    int hour, minute, second;
};

6. Geometric Shapes

struct Rectangle {
    struct Point top_left;
    struct Point bottom_right;
};

struct Circle {
    struct Point center;
    float radius;
};

Best Practices

1. Use Meaningful Names

// Bad
struct S {
    int a, b;
    char c[50];
};

// Good
struct Employee {
    int employee_id;
    int department_id;
    char full_name[50];
};

2. Use typedef for Convenience

typedef struct {
    int x, y;
} Point;

// Cleaner function signatures
Point add_points(Point p1, Point p2);

3. Initialize All Members

// Good - explicit initialization
struct Config cfg = {
    .max_connections = 100,
    .timeout = 30,
    .debug = 0
};

// Or zero-initialize
struct Config cfg = {0};

4. Order Members to Minimize Padding

// Order from largest to smallest
struct Optimized {
    double big;      // 8 bytes
    int medium;      // 4 bytes
    short small;     // 2 bytes
    char tiny;       // 1 byte
    char tiny2;      // 1 byte
};  // 16 bytes (minimal padding)

5. Document Structure Purpose

/**
 * Represents a 2D point in Cartesian coordinates.
 * Used for graphics operations and collision detection.
 */
struct Point {
    int x;  /**< X coordinate */
    int y;  /**< Y coordinate */
};

6. Use const for Read-Only Parameters

void print_student(const struct Student *s) {
    printf("Name: %s\n", s->name);
    // Cannot modify s members here
}

7. Create Initialization Functions

struct Student create_student(const char *name, int id, float gpa) {
    struct Student s;
    strncpy(s.name, name, sizeof(s.name) - 1);
    s.name[sizeof(s.name) - 1] = '\0';
    s.id = id;
    s.gpa = gpa;
    return s;
}

Summary

Key Points

  1. •Definition: Structures group related variables of different types
  2. •Declaration: Use struct keyword with member definitions
  3. •Access: Use dot operator (.) for variables, arrow (->) for pointers
  4. •Initialization: Use initializer lists or designated initializers
  5. •Assignment: Direct assignment copies all members (shallow copy)
  6. •Size: May be larger than sum of members due to padding
  7. •typedef: Simplifies structure type names

Common Operations

OperationSyntax
Declare structurestruct Name { members };
Create variablestruct Name var;
Access membervar.member
Initialize{value1, value2, ...}
Assigns2 = s1;
Get sizesizeof(struct Name)

Memory Diagram

struct Student s1 = {"John", 101, 3.75};

Memory:
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ "John\0"...     │  101   │  3.75  │ padding │
│   (50 bytes)    │(4 bytes)│(4 bytes)│        │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
       ↑              ↑          ↑
    s1.name     s1.roll    s1.gpa

When to Use Structures

  • •Grouping related data of different types
  • •Representing real-world entities
  • •Returning multiple values from functions
  • •Creating custom data types
  • •Building complex data structures

Next Topics

  • •Nested Structures
  • •Array of Structures
  • •Pointers to Structures
  • •Passing Structures to Functions
  • •Unions
  • •Bit Fields
Structures And Unions - C Programming Tutorial | DeepML