Tutorials Logic, IN info@tutorialslogic.com

C Structures struct, typedef, Nested Structs

Record Type Design

A structure defines one object type whose named members can have different types. It is the normal C tool for records such as coordinates, protocol headers, configuration values, and domain entities. Each structure object contains storage for all of its members, with possible padding inserted to satisfy alignment requirements.

This lesson covers declaration, initialization, nested structures, member access, function interfaces, copying, padding, and ownership. You will learn why a copied structure may still share pointed-to data and why raw structure bytes are usually the wrong format for files or networks.

Define and Initialize

A tag such as struct Student names the structure type. Members are declared inside the definition and accessed with the dot operator on an object. A typedef can provide an alias, but it does not create a second representation. Use names that reveal the domain rather than hiding whether a type owns resources.

Designated initializers bind values to member names and remain readable when members have the same scalar type. Members not explicitly initialized in an initializer are zero-initialized. Assignment after declaration does not automatically initialize untouched members, so establish a valid state before passing the object onward.

What is a Structure?

What is a Structure?
// Define a structure
struct Student {
    char name[50];
    int  age;
    float gpa;
};

// Declare a variable
struct Student s1;

// Access members with dot operator
s1.age = 20;
strcpy(s1.name, "Alice");

// Initialize at declaration
struct Student s2 = {"Bob", 22, 3.8f};

Nested Records

A structure member can itself be a structure or array. Nest by value when the child has the same lifetime and is logically part of the parent. Use a pointer when the relationship is optional, shared, dynamically sized, or managed elsewhere, and document who owns the pointed-to object.

Designated and Nested Initialization

Designated and Nested Initialization
#include <stdio.h>

typedef struct { int year, month, day; } Date;
typedef struct {
    int id;
    char name[32];
    Date joined;
} Employee;

int main(void) {
    Employee employee = {
        .id = 17,
        .name = "Mira",
        .joined = {.year = 2026, .month = 7, .day = 12}
    };
    printf("%d %s %04d-%02d-%02d\n", employee.id, employee.name,
           employee.joined.year, employee.joined.month, employee.joined.day);
    return 0;
}
Output
17 Mira 2026-07-12

Pointer Member Access

Use object.member when you have a structure object and pointer->member when you have a pointer to one. The arrow expression is equivalent to (*pointer).member with the required grouping. Check whether a pointer may be null before dereferencing it.

Pass a pointer when a function should modify the caller object or avoid copying a large record. Pass a pointer to const when the function only reads it. Passing a small structure by value creates an independent memberwise copy and can make ownership clearer when every member is self-contained.

Basic struct - Student Record

Basic struct - Student Record
struct Student *ptr = &s1;
printf("%s", ptr->name);   // same as (*ptr).name

Array of Structs and Pointer to Struct

Array of Structs and Pointer to Struct
#include <stdio.h>
#include <string.h>

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

void printStudent(struct Student s) {
    printf("Name:  %s\n",  s.name);
    printf("Age:   %d\n",  s.age);
    printf("GPA:   %.2f\n", s.gpa);
    printf("Grade: %c\n",  s.grade);
}

int main() {
    struct Student s1;
    strcpy(s1.name, "Alice");
    s1.age   = 20;
    s1.gpa   = 3.85f;
    s1.grade = 'A';

    // Initialize at declaration
    struct Student s2 = {"Bob", 22, 3.2f, 'B'};

    printf("--- Student 1 ---\n");
    printStudent(s1);

    printf("\n--- Student 2 ---\n");
    printStudent(s2);

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

    return 0;
}

typedef struct and Nested Struct

typedef struct and Nested Struct
#include <stdio.h>
#include <string.h>

struct Employee {
    char name[50];
    int  id;
    float salary;
};

int main() {
    // Array of structures
    struct Employee team[3] = {
        {"Alice", 101, 75000.0f},
        {"Bob",   102, 68000.0f},
        {"Carol", 103, 82000.0f}
    };

    printf("%-10s %5s %10s\n", "Name", "ID", "Salary");
    printf("---------------------------\n");
    for (int i = 0; i < 3; i++) {
        printf("%-10s %5d %10.2f\n",
               team[i].name, team[i].id, team[i].salary);
    }

    // Pointer to struct - use arrow operator ->
    struct Employee *ptr = &team[0];
    printf("\nFirst employee via pointer:\n");
    printf("Name: %s, Salary: %.2f\n", ptr->name, ptr->salary);

    ptr++;  // move to next struct
    printf("Second employee via pointer:\n");
    printf("Name: %s, ID: %d\n", ptr->name, ptr->id);

    return 0;
}

Pointer to Structure

Pointer to Structure
#include <stdio.h>
#include <string.h>

// typedef lets you use the type without 'struct' keyword
typedef struct {
    int day;
    int month;
    int year;
} Date;

typedef struct {
    char name[50];
    Date birthdate;   // nested struct
    float salary;
} Person;

int main() {
    Person p;
    strcpy(p.name, "Alice");
    p.birthdate.day   = 15;
    p.birthdate.month = 6;
    p.birthdate.year  = 1995;
    p.salary = 60000.0f;

    printf("Name:      %s\n", p.name);
    printf("Birthdate: %02d/%02d/%d\n",
           p.birthdate.day, p.birthdate.month, p.birthdate.year);
    printf("Salary:    %.2f\n", p.salary);

    // No need to write 'struct Person' - just 'Person'
    Person p2 = {"Bob", {20, 3, 1990}, 55000.0f};
    printf("\nName: %s, Born: %d\n", p2.name, p2.birthdate.year);

    return 0;
}

Padding and Layout

Compilers may insert padding between members and after the final member so each field has suitable alignment and arrays of the structure work correctly. sizeof(struct Type) therefore may exceed the sum of member sizes. Reordering members can change size, but optimize layout only when measurement or an external ABI requires it.

Do not write a structure object directly to a portable file or send its bytes over a network. Padding bytes, integer widths, byte order, floating representation, and compiler layout can differ. Serialize each field into a defined format and validate values while decoding.

Inspect Member Offsets

Inspect Member Offsets
#include <stddef.h>
#include <stdio.h>

typedef struct {
    char active;
    int count;
    double total;
} Report;

int main(void) {
    printf("size=%zu active=%zu count=%zu total=%zu\n",
           sizeof(Report), offsetof(Report, active),
           offsetof(Report, count), offsetof(Report, total));
    return 0;
}

The exact numbers are implementation-dependent. offsetof reveals the actual member offsets selected by the current implementation.

Copy and Ownership

Structure assignment and pass-by-value copy each member. Embedded arrays are copied as part of the object. Pointer members copy only the address, so the original and copy refer to the same allocation. Freeing or modifying shared pointee data through one copy affects the other and can cause double-free or dangling-pointer bugs.

For an owning structure, provide explicit initialize, clone, and destroy functions. Initialize every pointer to null or valid owned storage, make cleanup safe for partially initialized objects, and document whether functions borrow, take, or return ownership.

  • A shallow structure copy is sufficient only when shared pointer targets are intended or no owning pointers exist.
  • Compare structures member by member; the == operator does not compare complete structure objects.
  • Avoid memcmp for semantic equality because padding bytes need not represent member values.
  • Keep serialization separate from in-memory layout.
Before you move on

Structure Design Review

5 checks
  • Initialize every member needed for a valid object state.
  • Choose dot or arrow according to whether you hold an object or pointer.
  • Use const pointers for read-only function parameters.
  • Document ownership for every pointer member.
  • Serialize named fields instead of copying raw structure bytes.

Structure Ownership Traps

  • Assuming sizeof equals member totals

    Expect alignment padding and inspect layout only when a real interface requires it.
  • Copying an owning pointer member

    Implement an explicit deep-copy operation or make shared ownership intentional.
  • Sending raw struct bytes

    Encode each field into a specified portable representation.

Try this next

Model and Inspect Records

0 of 3 completed

  1. Define a Product with an id, bounded name array, and price, then write an initialization function.
  2. Reorder mixed-size members and compare sizeof and offsetof results on your compiler.
  3. Clone a structure that owns a dynamically allocated string and prove both objects can be destroyed independently.

Structure Model Questions

No. typedef adds an alias; it does not change member layout or alignment.

Yes. Returning by value copies the structure result, subject to normal lifetime and ownership rules for its members.

Padding bytes may differ even when all member values are equal, and pointer members need semantic rather than address comparison.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.