โ† All units

๐ŸŽ’ Unit 5 ยท Structures & Union

Packing different things into one box, chaining boxes like a train, and renting memory

1๏ธโƒฃ Structure: one card, many details

Your school ID card has your name (letters), roll number (whole number) and marks (decimal number), all on one card.

An array can only hold things of the same type, like 5 ints. A structure can hold different types together. Think of it as a school bag that can carry a book, a pencil box and a water bottle at the same time.

struct Student {      // 1. design the card (the plan)
    char name[20];    //    each line inside is a "member"
    int roll;
    float marks;
};                    //    semicolon at the end!

struct Student s1 = {"Anu", 101, 92.5};  // 2. make a card and fill it
s1.marks = 95;                            // 3. use the DOT to reach a member
WordMeaning
structKeyword that starts a structure
Tag (Student)The name of the design
MemberOne item inside (name, roll, marks)
Dot ."Open the card and take this member"
Making the design does not use memory. Memory is given only when you make a variable like struct Student s1;.
You can't do s2.name = "Ravi";. Strings inside a structure need strcpy(s2.name, "Ravi");.
๐Ÿชช Make an ID card
Name Roll Marks

    

  
A structure is a user-defined data type that groups variables of different data types under one name. Syntax: struct tag { members; };. Members are accessed with the dot operator (var.member). Memory is given only when a variable is declared, and its size is the sum of the members plus padding. For a 16-mark answer, write the syntax, declaration, initialization, accessing members and an example program like the one below.

File: 01_structure_basics.c

/*
 * Program 1: Structure basics
 * ---------------------------
 * Real-life story: Your school ID card has your NAME, ROLL NUMBER and MARKS
 * all on ONE card. A structure is like that card: it keeps different kinds
 * of information about ONE thing together.
 *
 *   struct = a "school bag" that can hold a book (string),
 *            a pencil box (int) and a water bottle (float) together.
 */
#include <stdio.h>
#include <string.h>

/* Step 1: DESIGN the card (this makes no variable yet, just the plan) */
struct Student {
    char name[20];   /* member 1: name  */
    int roll;        /* member 2: roll number */
    float marks;     /* member 3: marks */
};                   /* don't forget this semicolon! */

int main() {
    /* Step 2: make a card and fill it while making it (initialization) */
    struct Student s1 = {"Anu", 101, 92.5};

    /* Step 3: make an empty card and fill it later using the DOT (.) */
    struct Student s2;
    strcpy(s2.name, "Ravi");   /* strings need strcpy, not = */
    s2.roll = 102;
    s2.marks = 88.0;

    /* Step 4: read the members using the dot */
    printf("Card 1 -> Name: %s, Roll: %d, Marks: %.1f\n", s1.name, s1.roll, s1.marks);
    printf("Card 2 -> Name: %s, Roll: %d, Marks: %.1f\n", s2.name, s2.roll, s2.marks);

    /* Change one member */
    s2.marks = s2.marks + 5;   /* Ravi got 5 bonus marks */
    printf("After bonus, %s has %.1f marks\n", s2.name, s2.marks);

    /* Copy a whole structure in one go */
    struct Student s3 = s1;
    printf("Copy of card 1 -> %s %d %.1f\n", s3.name, s3.roll, s3.marks);

    printf("Size of one Student card = %zu bytes\n", sizeof(struct Student));
    return 0;
}

Output

Card 1 -> Name: Anu, Roll: 101, Marks: 92.5
Card 2 -> Name: Ravi, Roll: 102, Marks: 88.0
After bonus, Ravi has 93.0 marks
Copy of card 1 -> Anu 101 92.5
Size of one Student card = 28 bytes

2๏ธโƒฃ Nested structure: a box inside a box

A student card has a small box for date of birth. The date has its own day, month and year. So we keep a little Date card inside the big Student card.
struct Date    { int day, month, year; };
struct Student { char name[20]; int roll; struct Date dob; };

s.dob.year      // big box . small box . item
Student s
name = "Meena"
roll = 7
dob (a Date)
day = 15
month = 8
year = 2010
How to reach things
s.name โ†’ "Meena"
s.dob โ†’ the whole date
s.dob.day โ†’ 15
s.dob.year โ†’ 2010
You can also write the inner structure directly inside the outer one (an embedded structure), but keeping it separate lets you reuse Date for other things, like a joining date.
A nested structure is a structure that has another structure as a member. You reach the inner members with more than one dot: outer.inner.member. You can define it in two ways: (1) separate structures, or (2) an embedded structure declared inside the outer one.

File: 02_nested_structure.c

/*
 * Program 2: Nested structure (a structure inside a structure)
 * ------------------------------------------------------------
 * Real-life story: A student card has a small box for DATE OF BIRTH.
 * The date itself has day, month and year. So we put a small
 * "Date" card INSIDE the big "Student" card, like a small box inside a big box.
 */
#include <stdio.h>

struct Date {          /* small box */
    int day;
    int month;
    int year;
};

struct Student {       /* big box */
    char name[20];
    int roll;
    struct Date dob;   /* the small box lives inside the big box */
};

int main() {
    struct Student s = {"Meena", 7, {15, 8, 2010}};   /* inner { } fills the date */

    /* use the dot TWICE: big box . small box . item */
    printf("Name : %s\n", s.name);
    printf("Roll : %d\n", s.roll);
    printf("Born : %02d/%02d/%d\n", s.dob.day, s.dob.month, s.dob.year);

    /* birthday party next year! */
    s.dob.year = s.dob.year + 1;
    printf("Changed year to: %d\n", s.dob.year);

    return 0;
}

Output

Name : Meena
Roll : 7
Born : 15/08/2010
Changed year to: 2011

3๏ธโƒฃ Pointer to structure: the arrow ->

Instead of carrying your friend's whole toy box around, you keep a note with the box's address. With that note you can go and open the box any time.
struct Book b1;
struct Book *p = &b1;   // p remembers WHERE b1 lives

p->price      // go to that address and take price
(*p).price    // the same thing, the long way
You have...UseExample
The structure itselfdot .b1.price
A pointer to the structurearrow ->p->price
Brackets matter: *p.price is wrong because . runs before *. Write (*p).price or simply p->price.
Sending a pointer to a function (giveDiscount(&b1)) lets the function change the real book. It's also faster than copying the whole structure.
A structure pointer holds the address of a structure variable: struct Book *p = &b1;. Members are accessed with the arrow operator (p->member), which means the same as (*p).member. Uses: passing structures to functions by reference, dynamic structures and linked lists.

File: 03_pointer_to_structure.c

/*
 * Program 3: Pointer to structure (the arrow ->)
 * ----------------------------------------------
 * Real-life story: Instead of carrying your friend's whole toy box,
 * you keep a NOTE with the box's address. Using that note you can
 * open the box and play with the toys.
 *
 *   p = &box;       -> write the address of the box on the note
 *   p->toy          -> "go to the address and open the toy"
 *   (*p).toy        -> same thing, longer way of writing it
 */
#include <stdio.h>

struct Book {
    char title[30];
    int pages;
    float price;
};

/* A function that gets the ADDRESS, so it can change the real book */
void giveDiscount(struct Book *b) {
    b->price = b->price - 50;    /* changes the original book */
}

int main() {
    struct Book b1 = {"Panchatantra", 120, 250.0};
    struct Book *p = &b1;        /* p holds the address of b1 */

    printf("Using dot   : %s, %d pages, Rs %.2f\n", b1.title, b1.pages, b1.price);
    printf("Using arrow : %s, %d pages, Rs %.2f\n", p->title, p->pages, p->price);
    printf("Using (*p). : %s\n", (*p).title);

    p->pages = 130;              /* change through the pointer */
    printf("\nNew page count (b1.pages) = %d\n", b1.pages);

    giveDiscount(&b1);           /* send the address */
    printf("Price after discount       = Rs %.2f\n", b1.price);

    return 0;
}

Output

Using dot   : Panchatantra, 120 pages, Rs 250.00
Using arrow : Panchatantra, 120 pages, Rs 250.00
Using (*p). : Panchatantra

New page count (b1.pages) = 130
Price after discount       = Rs 200.00

4๏ธโƒฃ Array of structures: a stack of report cards

The teacher has a stack of 5 report cards. Each card is a structure, and the stack is an array. Let's find the topper!
struct Student cls[5];   // 5 cards in a row
cls[2].marks             // card number 2, its marks
๐Ÿ… Find the topper, step by step
Press "Next" to look at each card.
An array of structures is a collection of structure variables of the same type: struct Student s[50];. You reach a member with s[i].member and usually go through all of them with a loop. It's used for records like a student list or an employee list. Typical programs: find the topper, average marks, sort by marks.

File: 04_array_of_structures.c

/*
 * Program 4: Array of structures (find the topper)
 * ------------------------------------------------
 * Real-life story: The teacher has a STACK of 5 report cards.
 * Each card is a structure. The whole stack is an array.
 * Let's find who got the highest marks (the topper!).
 */
#include <stdio.h>

struct Student {
    char name[20];
    int marks;
};

int main() {
    struct Student cls[5] = {      /* 5 cards in a row */
        {"Anu",   78},
        {"Ravi",  91},
        {"Meena", 85},
        {"Kiran", 95},
        {"Divya", 66}
    };
    int i, total = 0, top = 0;     /* top = position of the best card so far */

    printf("%-8s %s\n", "Name", "Marks");
    printf("--------------\n");
    for (i = 0; i < 5; i++) {
        printf("%-8s %d\n", cls[i].name, cls[i].marks);   /* card i, member marks */
        total = total + cls[i].marks;
        if (cls[i].marks > cls[top].marks) {
            top = i;               /* found a better card */
        }
    }

    printf("\nClass average : %.2f\n", (float)total / 5);
    printf("Topper        : %s with %d marks\n", cls[top].name, cls[top].marks);

    printf("\nStudents who passed (marks >= 70):\n");
    for (i = 0; i < 5; i++) {
        if (cls[i].marks >= 70) {
            printf("  %s\n", cls[i].name);
        }
    }
    return 0;
}

Output

Name     Marks
--------------
Anu      78
Ravi     91
Meena    85
Kiran    95
Divya    66

Class average : 83.00
Topper        : Kiran with 95 marks

Students who passed (marks >= 70):
  Anu
  Ravi
  Meena
  Kiran

5๏ธโƒฃ Self-referential structure: the treasure hunt

In a treasure hunt, every clue card has a message and tells you where the next clue card is. The last card says "no more clues".
struct Clue {
    char message[40];
    struct Clue *next;   // a pointer to the SAME kind of structure
};
startโ†’
"door"next
โ†’
"kitchen"next
โ†’
"bed!"next
โ†’ NULL
A structure can't contain itself (struct Clue next;). That would be a box containing a box containing a box... forever! It can only hold a pointer to itself, which is just an address.
A self-referential structure has at least one member that is a pointer to the same structure type. It's used to build linked lists, stacks, queues and trees. Example: struct node { int data; struct node *next; };

File: 05_self_referential.c

/*
 * Program 5: Self-referential structure
 * -------------------------------------
 * Real-life story: A TREASURE HUNT. Each clue card has a message AND
 * tells you where the NEXT clue card is hidden. The last card says
 * "no more clues" (NULL).
 *
 * A self-referential structure has a pointer to ITS OWN type inside it.
 * This is the building block of a linked list.
 */
#include <stdio.h>
#include <stddef.h>   /* for NULL */

struct Clue {
    char message[40];
    struct Clue *next;   /* points to another Clue (same type!) */
};

int main() {
    /* make 3 clue cards */
    struct Clue c3 = {"Treasure is under the bed!", NULL};  /* last card */
    struct Clue c2 = {"Look in the kitchen", &c3};          /* points to c3 */
    struct Clue c1 = {"Start at the door", &c2};            /* points to c2 */

    struct Clue *now = &c1;   /* we start with the first card */
    int step = 1;

    while (now != NULL) {     /* keep going until "no more clues" */
        printf("Clue %d: %s\n", step, now->message);
        now = now->next;      /* jump to the next card */
        step++;
    }
    printf("Hunt finished!\n");
    return 0;
}

Output

Clue 1: Start at the door
Clue 2: Look in the kitchen
Clue 3: Treasure is under the bed!
Hunt finished!

6๏ธโƒฃ Dynamic Memory Allocation: renting chairs

At a birthday party you don't know how many friends will come. Instead of buying 100 chairs, you rent chairs when friends arrive and give them back when the party ends.

Static memory (like int a[100];) is fixed when you write the program. Dynamic memory is asked for while the program runs, from a big area called the heap. These functions live in <stdlib.h>.

FunctionParty storySyntaxStarts with
mallocRent chairs (they may be dusty)p = (int*)malloc(n * sizeof(int));Garbage
callocRent cleaned chairsp = (int*)calloc(n, sizeof(int));All zeros
reallocMore friends came, swap for a bigger setp = (int*)realloc(p, newSize);Old values kept
freeGive the chairs backfree(p);Nothing
๐Ÿช‘ Chair rental shop (heap)
The shop has 8 chairs. Rent some!
Memory leak: if you forget free(), those chairs are never returned. In a long-running program the shop eventually runs out of chairs. Also, always check whether malloc returned NULL (the shop said "no chairs left").
DMA means allocating memory at run time from the heap using malloc(), calloc(), realloc() and free() (header stdlib.h). malloc takes one argument and leaves garbage values. calloc takes two arguments (n, size) and sets everything to zero. realloc changes the size of an existing block. free releases it. Advantages: no wasted memory, and the size is decided at run time.

File: 06_dynamic_memory.c

/*
 * Program 6: Dynamic Memory Allocation (malloc, calloc, realloc, free)
 * --------------------------------------------------------------------
 * Real-life story: At a birthday party you don't know how many friends
 * will come. Instead of buying 100 chairs, you RENT chairs when
 * friends arrive, and RETURN them when the party ends.
 *
 *   malloc  -> rent chairs (they may be dusty: garbage values)
 *   calloc  -> rent chairs that are already cleaned (set to 0)
 *   realloc -> more friends came, rent a bigger set of chairs
 *   free    -> return the chairs (or the shop runs out = memory leak!)
 */
#include <stdio.h>
#include <stdlib.h>   /* malloc, calloc, realloc, free live here */

int main() {
    int n = 3, i;

    /* ---- malloc: ask for space for 3 ints ---- */
    int *marks = (int *)malloc(n * sizeof(int));
    if (marks == NULL) {                 /* always check! the shop may say NO */
        printf("No memory!\n");
        return 1;
    }
    for (i = 0; i < n; i++) {
        marks[i] = (i + 1) * 10;         /* fill: 10 20 30 */
    }
    printf("malloc  : ");
    for (i = 0; i < n; i++) printf("%d ", marks[i]);
    printf("\n");

    /* ---- realloc: 2 more friends came, grow to 5 ---- */
    int *bigger = (int *)realloc(marks, 5 * sizeof(int));
    if (bigger == NULL) {
        free(marks);
        return 1;
    }
    marks = bigger;
    marks[3] = 40;
    marks[4] = 50;
    printf("realloc : ");
    for (i = 0; i < 5; i++) printf("%d ", marks[i]);
    printf("\n");

    /* ---- calloc: 4 ints, all cleaned to 0 ---- */
    int *scores = (int *)calloc(4, sizeof(int));
    if (scores == NULL) {
        free(marks);
        return 1;
    }
    printf("calloc  : ");
    for (i = 0; i < 4; i++) printf("%d ", scores[i]);
    printf(" (all zero!)\n");

    /* ---- free: give the memory back ---- */
    free(marks);
    free(scores);
    marks = NULL;      /* good habit: don't keep an old address */
    scores = NULL;
    printf("All memory returned with free(). Party over!\n");
    return 0;
}

Output

malloc  : 10 20 30 
realloc : 10 20 30 40 50 
calloc  : 0 0 0 0  (all zero!)
All memory returned with free(). Party over!

7๏ธโƒฃ Singly Linked List: the train

A train: each coach (node) carries passengers (data) and is hooked to the next coach. The engine (head) only knows the first coach. The last coach is hooked to nothing (NULL).
Array ๐Ÿงฑ
Boxes glued side by side. Fixed size. To insert in the middle you must shift everything.
Linked list ๐Ÿš‚
Coaches can be anywhere in memory. Grows and shrinks. To insert you just change hooks (pointers).
๐Ÿš‚ Build your own train
Value:

  

How each operation works

OperationSteps
Insert at front1. Make a new node. 2. new->next = head. 3. head = new.
Insert at end1. Make a new node. 2. Walk to the last node (t->next == NULL). 3. t->next = new.
Delete1. Find the node and remember the one before it (prev). 2. prev->next = t->next. 3. free(t).
Display / countStart at head and keep doing t = t->next until NULL.
A singly linked list is a linear collection of nodes, where each node has data and a pointer to the next node. The last node points to NULL. Operations: create, insert (at the beginning, end or a position), delete, traverse/display, search, count. Advantages: dynamic size and easy insertion/deletion. Disadvantages: no random access (you can't jump to the 5th node directly), and the pointers take extra memory. For a 16-mark answer, draw the diagrams for insert and delete and write the program below.

File: 07_linked_list.c

/*
 * Program 7: Singly Linked List
 * -----------------------------
 * Real-life story: A TRAIN. Each coach (node) carries passengers (data)
 * and is hooked to the NEXT coach. The engine knows only the first
 * coach (head). The last coach is hooked to nothing (NULL).
 *
 *   head -> [10|*] -> [20|*] -> [30|NULL]
 *
 * We can add coaches at the front or back and remove a coach,
 * without moving the other coaches. Arrays can't do that easily!
 */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;            /* passengers in this coach */
    struct Node *next;   /* hook to the next coach */
};

/* make a brand new coach */
struct Node *createNode(int value) {
    struct Node *n = (struct Node *)malloc(sizeof(struct Node));
    if (n == NULL) {
        printf("No memory!\n");
        exit(1);
    }
    n->data = value;
    n->next = NULL;
    return n;
}

/* add a coach at the FRONT (right behind the engine) */
struct Node *insertFront(struct Node *head, int value) {
    struct Node *n = createNode(value);
    n->next = head;      /* new coach hooks to the old first coach */
    return n;            /* new coach is now the first */
}

/* add a coach at the END */
struct Node *insertEnd(struct Node *head, int value) {
    struct Node *n = createNode(value);
    if (head == NULL) {
        return n;        /* empty train: this is the first coach */
    }
    struct Node *t = head;
    while (t->next != NULL) {   /* walk to the last coach */
        t = t->next;
    }
    t->next = n;         /* hook the new coach at the back */
    return head;
}

/* remove the first coach that has this value */
struct Node *deleteValue(struct Node *head, int value) {
    struct Node *t = head, *prev = NULL;
    while (t != NULL && t->data != value) {
        prev = t;
        t = t->next;
    }
    if (t == NULL) {
        printf("%d not found\n", value);
        return head;
    }
    if (prev == NULL) {
        head = t->next;  /* removing the first coach */
    } else {
        prev->next = t->next;   /* unhook t, join prev to the one after t */
    }
    free(t);             /* give the coach's memory back */
    return head;
}

void display(struct Node *head) {
    printf("head -> ");
    while (head != NULL) {
        printf("[%d] -> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

int count(struct Node *head) {
    int c = 0;
    while (head != NULL) {
        c++;
        head = head->next;
    }
    return c;
}

void freeList(struct Node *head) {
    while (head != NULL) {
        struct Node *nextOne = head->next;
        free(head);
        head = nextOne;
    }
}

int main() {
    struct Node *head = NULL;    /* empty train */

    head = insertEnd(head, 20);
    head = insertEnd(head, 30);
    printf("Insert 20, 30 at end    : "); display(head);

    head = insertFront(head, 10);
    printf("Insert 10 at front      : "); display(head);

    head = insertEnd(head, 40);
    printf("Insert 40 at end        : "); display(head);
    printf("Number of coaches       : %d\n", count(head));

    head = deleteValue(head, 30);
    printf("Delete 30               : "); display(head);

    head = deleteValue(head, 10);
    printf("Delete 10 (first coach) : "); display(head);
    printf("Number of coaches       : %d\n", count(head));

    freeList(head);              /* return all memory */
    return 0;
}

Output

Insert 20, 30 at end    : head -> [20] -> [30] -> NULL
Insert 10 at front      : head -> [10] -> [20] -> [30] -> NULL
Insert 40 at end        : head -> [10] -> [20] -> [30] -> [40] -> NULL
Number of coaches       : 4
Delete 30               : head -> [10] -> [20] -> [40] -> NULL
Delete 10 (first coach) : head -> [20] -> [40] -> NULL
Number of coaches       : 2

8๏ธโƒฃ typedef: giving a nickname

Your friend's full name is "Venkata Subramanian", but everyone calls him "Venky". typedef gives a short nickname to a long type name.
typedef old_name NickName;

typedef unsigned int Age;     // now "Age x;" means "unsigned int x;"
typedef struct { char name[20]; Age age; } Person;
Person p;                     // no need to write "struct" every time!
typedef does not make a new type. It only gives an existing type another name. It makes code shorter and easier to read.
typedef is a keyword used to create an alias (another name) for an existing data type. Syntax: typedef existing_type new_name;. It's commonly used with structures, for example typedef struct node Node;. Benefits: readability, shorter code, and easier to change later.

File: 08_typedef.c

/*
 * Program 8: typedef (giving a nickname)
 * --------------------------------------
 * Real-life story: Your friend's full name is "Venkata Subramanian",
 * but everyone calls him "Venky". typedef gives a SHORT NICKNAME
 * to a long type name.
 *
 *   typedef  old_long_name  NickName;
 */
#include <stdio.h>

typedef unsigned int Age;          /* nickname for unsigned int */

typedef struct {                   /* nickname for a structure */
    char name[20];
    Age age;
} Person;                          /* now write "Person" instead of "struct ..." */

typedef struct Node {              /* typedef also works for linked list nodes */
    int data;
    struct Node *next;
} Node;

int main() {
    Age myAge = 12;
    Person p = {"Arjun", 12};
    Node n1 = {5, NULL};

    printf("My age      : %u\n", myAge);
    printf("Person      : %s, %u years\n", p.name, p.age);
    printf("Node data   : %d\n", n1.data);
    printf("Without typedef we would write: struct Node n1;\n");
    printf("With typedef we just write    : Node n1;\n");
    return 0;
}

Output

My age      : 12
Person      : Arjun, 12 years
Node data   : 5
Without typedef we would write: struct Node n1;
With typedef we just write    : Node n1;

9๏ธโƒฃ Union: one cup, one drink at a time

Structure = a lunch box with separate compartments, so rice, curd and sweet all fit together.
Union = one cup. Fill it with milk or juice or water. Pour juice in and the milk is gone!

Memory picture (1 square = 1 byte)

int i (4)float f (4)char c (1)padding

struct LunchBox: each member gets its own room, so the size is 12 bytes

union Cup: all members share the same room, so the size is 4 bytes (the biggest member)

Why 12 and not 9? The computer likes to read in groups of 4, so it adds 3 empty "padding" bytes after char c.
๐Ÿฅค Fill the cup (union)
StructureUnion
Keyword structKeyword union
Each member has its own memoryAll members share one memory
Size = sum of members (+ padding)Size = biggest member
All members can be used at onceOnly one member is valid at a time
Changing one doesn't affect othersChanging one spoils the others
Used for records (a student)Used to save memory (a value that's either int or float)
A union is a user-defined type like a structure, but all members share the same memory location. Its size equals the size of its largest member, and only one member holds a valid value at a time. The structure vs union difference table above is a very common 2- and 16-mark question.

File: 09_union.c

/*
 * Program 9: Union vs Structure
 * -----------------------------
 * Real-life story:
 *   STRUCTURE = a lunch box with SEPARATE compartments.
 *               Rice, curd and sweet can all be inside at the same time.
 *   UNION     = ONE single cup. You can fill it with milk OR juice OR water,
 *               but only ONE at a time. Pour juice in, the milk is gone!
 *
 * A union's size = size of its BIGGEST member (one shared space).
 * A structure's size = sum of all members (+ some padding).
 */
#include <stdio.h>

struct LunchBox {      /* separate rooms */
    int i;
    float f;
    char c;
};

union Cup {            /* one shared room */
    int i;
    float f;
    char c;
};

int main() {
    struct LunchBox box;
    union Cup cup;

    printf("Size of struct LunchBox = %zu bytes\n", sizeof(box));
    printf("Size of union  Cup      = %zu bytes\n\n", sizeof(cup));

    /* structure: everything stays */
    box.i = 10;
    box.f = 2.5;
    box.c = 'A';
    printf("Struct: i=%d f=%.1f c=%c  (all are safe)\n\n", box.i, box.f, box.c);

    /* union: only the LAST one you filled is correct */
    cup.i = 10;
    printf("Union after cup.i = 10   -> i = %d\n", cup.i);
    cup.f = 2.5;
    printf("Union after cup.f = 2.5  -> f = %.1f\n", cup.f);
    printf("   ...now i is spoiled   -> i = %d (garbage!)\n", cup.i);
    cup.c = 'A';
    printf("Union after cup.c = 'A'  -> c = %c\n", cup.c);

    return 0;
}

Output (the garbage number may differ on your computer)

Size of struct LunchBox = 12 bytes
Size of union  Cup      = 4 bytes

Struct: i=10 f=2.5 c=A  (all are safe)

Union after cup.i = 10   -> i = 10
Union after cup.f = 2.5  -> f = 2.5
   ...now i is spoiled   -> i = 1075838976 (garbage!)
Union after cup.c = 'A'  -> c = A

๐Ÿ”Ÿ Storage classes & visibility

auto: a sand drawing on the beach. Each wave (function call) wipes it, and you draw it fresh.
static: a drawing in your notebook. It stays after you close the book, and you keep adding to it.
register: keeping your pencil in your hand instead of in your bag, so it's faster to grab.
extern: "The bat is at my friend's house. I'm only telling you its name."

A storage class tells 4 things about a variable: where it is stored, its default value, its scope (who can see it) and its lifetime (how long it lives).

ClassStored inDefault valueScope (visibility)Lifetime
autoMemory (stack)GarbageInside its block { }Until the block ends
registerCPU register (if free)GarbageInside its block { }Until the block ends
staticMemory (data area)0Inside its block (local) or file (global)The whole program
externMemory (data area)0Global, across filesThe whole program
๐Ÿ” static vs auto counter
Each click calls the function once.
auto int count = 0;
count++ โ†’ -
Starts again at 0 every call
static int count = 0;
count++ โ†’ -
Remembers the old value
You can't take the address of a register variable: &fast is an error. Modern compilers usually decide about registers by themselves.
Storage classes in C are auto, register, static and extern. Each one decides the storage location, default initial value, scope and lifetime (draw the table). static local variables keep their value between function calls. extern declares a global variable that is defined in another file. Visibility means scope: block scope, function scope, file scope and program scope.

File: 10_storage_classes.c

/*
 * Program 10: Storage classes (auto, register, static, extern)
 * ------------------------------------------------------------
 * Real-life story:
 *   auto     -> a SAND drawing on the beach. Every time the wave (function call)
 *               comes, it is wiped and drawn fresh.
 *   static   -> a drawing in your NOTEBOOK. It stays even after you close the book,
 *               and you can keep adding to it.
 *   register -> keeping your pencil IN YOUR HAND instead of in the bag (faster to reach).
 *   extern   -> "this toy is in my friend's house, I'm just telling you its name".
 */
#include <stdio.h>

int schoolBell = 0;        /* global: everyone in this file can see it (default 0) */

void visitAuto() {
    auto int count = 0;    /* 'auto' is the default for local variables */
    count++;
    printf("  auto   count = %d\n", count);
}

void visitStatic() {
    static int count = 0;  /* made only ONCE, remembers its value */
    count++;
    printf("  static count = %d\n", count);
}

int main() {
    int i;
    for (i = 1; i <= 3; i++) {
        printf("Call %d:\n", i);
        visitAuto();
        visitStatic();
    }

    register int fast;     /* hint: keep it in a CPU register (can't use &fast) */
    int sum = 0;
    for (fast = 1; fast <= 5; fast++) {
        sum += fast;
    }
    printf("\nregister loop sum 1..5 = %d\n", sum);

    schoolBell++;
    printf("global schoolBell = %d\n", schoolBell);

    {   /* a small inner block */
        int secret = 99;   /* visible only inside these { } */
        printf("inner block secret = %d\n", secret);
    }
    /* printf("%d", secret);  <- ERROR: secret is not visible here */
    return 0;
}

Output

Call 1:
  auto   count = 1
  static count = 1
Call 2:
  auto   count = 1
  static count = 2
Call 3:
  auto   count = 1
  static count = 3

register loop sum 1..5 = 15
global schoolBell = 1
inner block secret = 99

1๏ธโƒฃ1๏ธโƒฃ extern: sharing between two files

The cricket bat (score) is kept at your friend's house (11_extern_helper.c). In your file you say extern int score;, which means "it's real, it lives in another file, please find it."
DeclarationDefinition
extern int score;int score = 10;
Only tells the name and typeActually creates the memory
Can appear many timesOnly once in the whole program
This program has two files. Compile them together:
gcc 11_extern_main.c 11_extern_helper.c -o program then ./program

File: 11_extern_main.c

/*
 * Program 11: extern (sharing a variable between two files)
 * ---------------------------------------------------------
 * Real-life story: The CRICKET BAT is kept at your friend's house
 * (11_extern_helper.c). You say "extern int score;" which means:
 * "score is real, but it is made in another file. Please find it."
 *
 * Compile BOTH files together:
 *     gcc 11_extern_main.c 11_extern_helper.c -o program
 *     ./program
 */
#include <stdio.h>

extern int score;          /* declared here, CREATED in the helper file */
void addRuns(int runs);    /* function from the helper file */

int main() {
    printf("Score at start   : %d\n", score);
    addRuns(4);
    addRuns(6);
    printf("Score after 4, 6 : %d\n", score);
    score = score + 1;     /* we can change it here too */
    printf("After 1 extra run: %d\n", score);
    return 0;
}

File: 11_extern_helper.c

/*
 * Helper file for Program 11 (extern).
 * The variable "score" is really CREATED here (memory is given here).
 * Compile with: gcc 11_extern_main.c 11_extern_helper.c -o program
 */
int score = 10;            /* the real variable lives in this file */

void addRuns(int runs) {
    score = score + runs;
}

Output

Score at start   : 10
Score after 4, 6 : 20
After 1 extra run: 21

๐Ÿ“ 2-mark questions & answers

1. Define structure. Give its syntax.A structure is a user-defined data type that groups related variables of different types under one name. struct tag { type member1; type member2; };
2. How do you access structure members?Use the dot operator with a structure variable (s.roll) and the arrow operator with a structure pointer (p->roll).
3. What is a nested structure?A structure that has another structure variable as one of its members, for example a Student with a Date dob. You reach the inner members with s.dob.day.
4. What is a self-referential structure?A structure with a member that is a pointer to the same structure type, like struct node { int data; struct node *next; };. It's used in linked lists and trees.
5. Differentiate malloc() and calloc().malloc(size) takes 1 argument and leaves garbage values. calloc(n, size) takes 2 arguments and sets all bytes to zero.
6. What is a memory leak?When dynamically allocated memory is never released with free(), it stays reserved and can't be reused. The program then wastes memory.
7. What is the use of realloc()?It changes the size of a block that was already allocated (bigger or smaller) and keeps the old contents: p = realloc(p, newsize);
8. What is a linked list? Give its advantages.A linear collection of nodes, where each node has data and a pointer to the next node. Advantages: dynamic size, and insertion/deletion don't need shifting.
9. Difference between structure and union?In a structure every member has separate memory (size = sum). In a union all members share one memory (size = largest member) and only one is valid at a time.
10. What is typedef?A keyword that creates a new name (alias) for an existing type: typedef struct student Student;
11. List the storage classes in C.auto, register, static and extern.
12. Why is static different from auto?A static local variable is made only once, starts at 0 and keeps its value between function calls. An auto variable is made fresh (with a garbage value) on every call.
13. What do scope and lifetime mean?Scope is the part of the program where a variable can be used (visibility). Lifetime is how long the variable exists in memory while the program runs.

๐Ÿ† Mini quiz

๐ŸŽฏ Practice homework

  1. Make a struct Employee with name, id and salary. Store 3 employees and print the one with the highest salary.
  2. Add a nested struct Address (street, city, pin) to the Employee and print the city of each employee.
  3. Use malloc to make an array whose size the user types in. Read the numbers and print their sum. Don't forget free!
  4. Add a search(head, value) function to the linked list program that prints the position of the value or "not found".
  5. Add insertAtPosition(head, pos, value) to the linked list.
  6. Make a union with int, double and char[10]. Predict its size, then check with sizeof.
  7. Write a function with a static counter that prints "You called me N times".