Pointer to struct in C: solved exercise

Pointer to struct in C: solved exercise

If you searched for a solved pointer to struct in C, here is the difference between value access (s.field) and pointer access (p->field), together with dynamic struct allocation using malloc.

The arrow operator -> is equivalent to dereferencing the pointer and accessing the field: p->field is syntactic sugar for (*p).field.

Problem statement

Define a Product struct with fields name (32-character string), price (double), and stock (int). Write a function that receives a pointer to Product, applies a percentage discount to the price, and decrements the stock by 1. Show the result with both static and dynamic access.

C solution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME 32

typedef struct {
    char name[MAX_NAME];
    double price;
    int stock;
} Product;

/* Modifies the product pointed to by p */
void apply_discount(Product *p, double pct) {
    p->price *= (1.0 - pct / 100.0);
    p->stock--;
}

void print_product(const Product *p) {
    printf("%-20s  price=%.2f  stock=%d\n", p->name, p->price, p->stock);
}

int main(void) {
    /* --- Static access --- */
    Product a;
    strncpy(a.name, "Mechanical keyboard", MAX_NAME - 1);
    a.name[MAX_NAME - 1] = '\0';
    a.price = 89.99;
    a.stock = 10;

    printf("Before: "); print_product(&a);
    apply_discount(&a, 15.0);   /* 15% discount */
    printf("After:  "); print_product(&a);

    printf("\n");

    /* --- Dynamic allocation --- */
    Product *b = malloc(sizeof(Product));
    if (!b) { perror("malloc"); return 1; }
    strncpy(b->name, "Wireless mouse", MAX_NAME - 1);
    b->name[MAX_NAME - 1] = '\0';
    b->price = 34.50;
    b->stock = 5;

    printf("Before: "); print_product(b);
    apply_discount(b, 10.0);    /* 10% discount */
    printf("After:  "); print_product(b);

    free(b);
    return 0;
}

Expected output

1
2
3
4
5
Before: Mechanical keyboard    price=89.99  stock=10
After:  Mechanical keyboard    price=76.49  stock=9

Before: Wireless mouse         price=34.50  stock=5
After:  Wireless mouse         price=31.05  stock=4

Common mistakes

  • Using . instead of -> with a pointer: p.price with a Product * is a compile error; p->price is required.
  • Not initializing all fields before printing: malloc does not initialize memory; fields may contain garbage.
  • Not checking the return value of malloc: if it returns NULL and the pointer is dereferenced, the program crashes with a segmentation fault.
  • Forgetting free(b) at the end: every malloc must have a corresponding free to avoid memory leaks.

Practical use

Pointers to structs are the central pattern in C programming with complex data: linked lists, trees, hash tables, and software modules that pass large structures between functions without copying them. Passing const Product * is more efficient than passing Product by value when the struct is large.

Guided practice and full book

If you want a complete path with progressive difficulty:

FAQ

When should I use a struct by value and when by pointer?

Use by value when the struct is small (1–2 numeric fields) and you do not need to modify it in the called function. Use by pointer when the struct is large (avoids copying), when you need to modify its fields in the function, or when it is a node of a dynamic structure (list, tree).

What is the difference between malloc(sizeof(Product)) and declaring Product b?

Product b allocates the struct on the stack, automatically freed when the block exits. malloc allocates on the heap, with a lifetime independent of the creating block, but requires explicit free. The heap is appropriate when the struct’s lifetime must outlast the function that created it.

Can you have an array of pointers to structs?

Yes: Product *catalog[100] is an array of 100 pointers, each pointing to an independently allocated Product. This is useful when structs have varying sizes (simulated inheritance via composition) or when you want to relocate them with realloc without invalidating external pointers.