A structure in C is a user-defined type that groups related variables of different types under one name. Where an array holds many values of the same type, a struct holds a fixed set of named fields — each with its own type. This is what makes it possible to represent a student record, a geometric point, a network packet header, or any compound object in a single variable that can be passed around, stored in arrays, and pointed to by a pointer.
Defining and Using a Struct
The struct keyword introduces the definition. Each field is a normal variable declaration inside the braces:
#include <stdio.h>
struct Point {
int x;
int y;
};
int main(void)
{
struct Point p; /* declare a variable of type struct Point */
p.x = 3;
p.y = 4;
printf("Point: (%d, %d)\n", p.x, p.y);
return 0;
}
Point: (3, 4)
The dot (.) operator accesses a member. p.x reads or writes the x field of p.
Initializing a Struct
Structs can be initialised at the point of declaration using a brace-enclosed list, in field order:
#include <stdio.h>
struct Student {
int id;
char name[32];
float gpa;
};
int main(void)
{
struct Student s = {101, "Alice", 3.8f};
printf("ID: %d\n", s.id);
printf("Name: %s\n", s.name);
printf("GPA: %.1f\n", s.gpa);
return 0;
}
ID: 101
Name: Alice
GPA: 3.8
typedef — Naming the Type
typedef lets you refer to the struct without repeating the struct keyword every time:
typedef struct {
int x;
int y;
} Point;
/* Now you can write: */
Point p = {3, 4}; /* instead of: struct Point p = {3, 4}; */
Both styles are valid. In larger codebases the typedef form is more common; in system code and K&R style the explicit struct tag is preferred because it is always explicit about the type.
Passing Structs to Functions
Passing a struct by value copies all its fields. Modifying the copy does not change the original:
#include <stdio.h>
struct Point { int x; int y; };
double distance_from_origin(struct Point p)
{
double dx = p.x, dy = p.y;
/* simple integer-only Euclidean for demo */
return dx * dx + dy * dy; /* returns squared distance */
}
int main(void)
{
struct Point p = {3, 4};
printf("Distance squared: %.0f\n", distance_from_origin(p));
return 0;
}
Distance squared: 25
For large structs, copying is expensive. Pass a pointer instead — and use const if the function only reads the struct:
#include <stdio.h>
struct Student {
int id;
char name[32];
float gpa;
};
void print_student(const struct Student *s)
{
printf("%d %-20s %.2f\n", s->id, s->name, s->gpa);
}
void raise_gpa(struct Student *s, float amount)
{
s->gpa += amount;
}
int main(void)
{
struct Student s = {42, "Bob", 3.2f};
print_student(&s);
raise_gpa(&s, 0.3f);
print_student(&s);
return 0;
}
42 Bob 3.20
42 Bob 3.50
The arrow operator (->) is shorthand for (*ptr).field. Use . when you have a struct variable; use -> when you have a pointer to a struct.
Arrays of Structs
An array of structs is the natural way to represent a table of records:
#include <stdio.h>
struct Student {
int id;
char name[32];
float gpa;
};
int main(void)
{
struct Student class[3] = {
{1, "Alice", 3.8f},
{2, "Bob", 3.2f},
{3, "Carol", 3.9f}
};
int i;
printf("%-4s %-20s %s\n", "ID", "Name", "GPA");
printf("----------------------------------------\n");
for (i = 0; i < 3; i++)
printf("%-4d %-20s %.2f\n",
class[i].id, class[i].name, class[i].gpa);
return 0;
}
ID Name GPA
----------------------------------------
1 Alice 3.80
2 Bob 3.20
3 Carol 3.90
Nested Structs
A struct field can itself be a struct. This is useful for composing larger types from smaller reusable ones:
#include <stdio.h>
struct Date {
int day;
int month;
int year;
};
struct Employee {
int id;
char name[32];
struct Date joining_date;
};
int main(void)
{
struct Employee e = {101, "Alice", {15, 3, 2024}};
printf("Name: %s\n", e.name);
printf("Joined: %02d/%02d/%04d\n",
e.joining_date.day,
e.joining_date.month,
e.joining_date.year);
return 0;
}
Name: Alice
Joined: 15/03/2024
Structs with Pointer Fields — Dynamic Strings
A struct field can be a pointer. This is common when the data size is not known at compile time:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Employee {
int id;
char *name; /* pointer to heap-allocated string */
};
int main(void)
{
struct Employee e;
e.id = 7;
e.name = (char *)malloc(32);
if (e.name == NULL) {
fprintf(stderr, "malloc failed\n");
return 1;
}
strcpy(e.name, "Diana");
printf("ID: %d Name: %s\n", e.id, e.name);
free(e.name);
e.name = NULL;
return 0;
}
ID: 7 Name: Diana
Self-Referential Structs — The Foundation of Linked Lists
A struct can contain a pointer to its own type. This is how linked lists, trees, and graphs are built in C:
struct Node {
int data;
struct Node *next; /* pointer to the next node */
};
A full working linked list built on this foundation is in the Priority Queue using Structures post and the Singly Linked List in C guide.
Common Struct Mistakes
| Mistake | What goes wrong | Fix |
|---|---|---|
Comparing structs with == |
Compilation error — == is not defined for structs |
Compare field by field: a.x == b.x && a.y == b.y |
Using . on a pointer |
Compilation error: ptr.field when ptr is struct S * |
Use ptr->field or (*ptr).field |
| Passing large struct by value to every function call | Unnecessary copy of every field on every call | Pass const struct S * for read-only access |
| Forgetting to free pointer fields | Memory leak | Free every heap-allocated field before the struct goes out of scope |
| Padding assumptions | Struct size is not always the sum of field sizes; compiler may insert padding bytes | Use sizeof(struct S), never hand-calculate |
Struct Size and Padding
#include <stdio.h>
struct A { char c; int i; }; /* likely 8 bytes, not 5 */
struct B { int i; char c; }; /* likely 8 bytes, not 5 */
struct C { char a; char b; int i; }; /* likely 8 bytes */
int main(void)
{
printf("sizeof A: %zu\n", sizeof(struct A));
printf("sizeof B: %zu\n", sizeof(struct B));
printf("sizeof C: %zu\n", sizeof(struct C));
return 0;
}
sizeof A: 8
sizeof B: 8
sizeof C: 8
The compiler inserts padding to align fields to their natural alignment boundary. Order your fields from largest to smallest to minimise wasted space.
How to Compile
gcc -ansi -Wall -Wextra -o structs structs.c
Related C Programs
- Pointers in C – A Complete Guide — the arrow operator and pointer-to-struct patterns
- Priority Queue Using Structures
- Add Two Polynomials Using Structures
- Complex Number Operations Using Structures
- All C Programs
📖 Chapter 6 of The C Programming Language by K&R (Amazon.in) · Amazon.com covers structures in depth, including bit-fields, unions, and typedef — all the patterns used in real systems code.
Preparing for a C interview? Practice with the C Programming Quiz — 150+ questions, free on Android.