A struct in C groups items of possibly different types into a single composite type. Where an array holds many of the same thing, a struct holds a few of different things — the building block for representing records, nodes in linked structures, and any “object-like” data.
struct Student {
char name[50];
int studentNumber;
char section;
};This declares a type — struct Student — but doesn’t allocate memory. To use it, declare a variable:
struct Student s1; // a single Student
struct Student s2 = {.name = "Alice", .section = 'A'}; // designated initializerThe members are accessed with the dot operator:
s1.studentNumber = 12345;
strcpy(s1.name, "Bob");For a more detailed walkthrough, see how a complete struct gets defined, declared, populated, and accessed:

Typedef
typedef creates an alias for an existing type. With structs it’s the standard way to avoid having to write struct everywhere:
typedef struct {
char name[50];
int class;
char section;
} Student; // Student is now a type alias
Student s1, s2; // no "struct" prefix neededBehind the scenes there’s still a struct; typedef just gives it a shorter name.
typedef works on any type — not just structs:
typedef int studentNumberType;
studentNumberType studentNumber1; // really just an int, but more readableNested structs
A struct field can itself be a struct:
typedef struct {
int imag;
float real;
} Complex;
struct Number {
int flags;
Complex phase; // struct inside struct
} num1;
num1.phase.real = 3.14; // dot-chain to nested memberEnums
enum defines a set of named integer constants. Useful for state codes, options, etc.:
enum Color { RED, GREEN, BLUE }; // RED=0, GREEN=1, BLUE=2
enum Day { Monday = 1, Tuesday, Wednesday, ... }; // Monday=1, Tuesday=2, ...By default the constants start at 0; you can override starting values explicitly. Combined with typedef:
typedef enum { RED, GREEN, BLUE } Color;
Color c = GREEN;Structs as array elements
Structs make perfect array elements when you have a uniform collection of records:
typedef struct {
int x, y;
} Point;
Point vertices[100];
vertices[4].x = 23;
vertices[4].y = 18;Each array element is a complete struct; vertices[4].x accesses the x field of the fifth Point.
Pointers to structs
A pointer to a struct lets you pass the struct around without copying it:
Point a = {23, 18};
Point *p = &a;
(*p).x = 34; // dereference and access field
p->x = 34; // shortcut: arrow operatorThe -> operator is shorthand for “dereference and access field” — p->x is (*p).x. The arrow form is universal in C code that uses struct pointers.
Pointers to pointers
Less common, but you’ll see it for functions that need to modify a pointer (e.g., update the head of a linked list):
void allocateInt(int **p) {
*p = malloc(sizeof(int));
}
int *myPtr = NULL;
allocateInt(&myPtr); // now myPtr points to the new intThe double-pointer is “address of a pointer.” Inside the function, *p is the pointer, and assigning to it changes what the caller’s pointer points at.
This pattern shows up everywhere in Linked list operations — to insert at the head of a list, you need to modify the caller’s head pointer, which requires node **head.
Alignment and padding
Struct fields are not packed contiguously in memory. The compiler inserts padding so each field starts on an address that’s a multiple of its alignment requirement — typically the field’s size up to the platform word width (8 bytes on 64-bit). This matters because most CPUs penalize or fault on misaligned accesses.
struct Bad {
char a; // offset 0, size 1
// 3 bytes padding here so the int starts at offset 4
int b; // offset 4, size 4
char c; // offset 8, size 1
// 3 bytes trailing padding so sizeof(Bad) is a multiple of int's alignment
};
// sizeof(struct Bad) == 12, not 6.
struct Good {
int b; // offset 0
char a; // offset 4
char c; // offset 5
// 2 bytes trailing padding
};
// sizeof(struct Good) == 8.Reordering fields from largest alignment to smallest minimizes padding. For embedded code where every byte matters, or when defining a struct that mirrors a hardware register layout, you also need to know about __attribute__((packed)) (GCC/Clang) or #pragma pack (MSVC), which removes padding entirely — at the cost of slow or trapping unaligned access on some platforms.
Why structs matter
Structs are the foundation of every non-trivial C data structure. A linked list node is struct node { int value; struct node *next; }; — a struct holding data plus a pointer to the next struct. Trees, graphs, hash table buckets, queue nodes — all built from structs with pointer fields.
For the C basics that complement structs, see Pointer arithmetic and Dynamic memory allocation.