Advanced Structures
Advanced structs are like decorating a house — it's not just about building a frame, but also about efficient space usage (alignment), saving materials (bit-fields), and flexible expansion (flexible arrays).
Advanced Struct Usage with Functions
Functions That Return a Struct
C allows functions to return a struct directly — the compiler handles the copying:
struct Point make_point(int x, int y) {
struct Point p = {x, y};
return p;
}
int main(void) {
struct Point pt = make_point(3, 4);
printf("(%d, %d)\n", pt.x, pt.y);
return 0;
}
Filling a Struct Through a Pointer
void fill_student(struct Student *s, const char *name, int age, float score) {
strncpy(s->name, name, 19);
s->name[19] = '\0';
s->age = age;
s->score = score;
}
int main(void) {
struct Student stu;
fill_student(&stu, "Zhang", 20, 88.5);
return 0;
}
const Protection for Struct Parameters
To prevent a function from modifying a struct, use const on the pointer parameter:
void print_student(const struct Student *s) {
printf("%s %d %.1f\n", s->name, s->age, s->score);
}
const struct Student *s means you cannot modify the pointed-to struct through s, but s itself can point to something else.
typedef for Type Aliases
typedef creates an alias for a type, reducing the need to repeatedly write the struct keyword.
Basic Usage
typedef struct {
char name[20];
int age;
float score;
} Student;
Student s1 = {"Zhang", 20, 89.5};
Student *ps = &s1;
struct Student anymore — just use Student directly.
typedef with Struct Pointers
typedef struct Node {
int data;
struct Node *next;
} Node, *NodePtr;
Node n1 = {10, NULL};
NodePtr head = &n1;
struct Node *next because the alias isn't in effect yet at that point.
Other Uses of typedef
typedef unsigned char Byte;
typedef int (*Comparator)(const void *, const void *);
Byte flag = 0xFF;
Comparator cmp = my_compare;
Example
#include <stdio.h>
#include <string.h>
typedef struct {
char title[50];
int pages;
float price;
} Book;
Book create_book(const char *title, int pages, float price) {
Book b;
strncpy(b.title, title, 49);
b.title[49] = '\0';
b.pages = pages;
b.price = price;
return b;
}
void discount(Book *b, float rate) {
b->price *= rate;
}
void print_book(const Book *b) {
printf("<%s> %d pages $%.2f\n", b->title, b->pages, b->price);
}
int main(void) {
Book b1 = create_book("C Programming", 320, 59.0);
print_book(&b1);
discount(&b1, 0.8);
print_book(&b1);
return 0;
}
<C Programming> 320 pages $59.00
<C Programming> 320 pages $47.20
Struct Memory Alignment
Struct members are not tightly packed in memory — the compiler inserts padding bytes according to alignment rules.
Alignment Rules
- Each member's offset must be a multiple of that member's size
- The total size of the struct must be a multiple of the largest member's size
struct Align1 {
char a;
int b;
char c;
};
struct Align2 {
char a;
char c;
int b;
};
printf("%zu\n", sizeof(struct Align1));
printf("%zu\n", sizeof(struct Align2));
12
8
Layout of Align1: a (1 byte) + 3 bytes padding + b (4 bytes) + c (1 byte) + 3 bytes padding = 12.
Layout of Align2: a (1 byte) + c (1 byte) + 2 bytes padding + b (4 bytes) = 8.
#pragma pack
You can specify the alignment boundary to compress a struct:
#pragma pack(push, 1)
struct Packed {
char a;
int b;
char c;
};
#pragma pack(pop)
printf("%zu\n", sizeof(struct Packed));
6
Bit-Fields
Bit-fields allocate space to members on a bit-by-bit basis, saving memory.
struct Flags {
unsigned int ready : 1;
unsigned int error : 1;
unsigned int mode : 3;
unsigned int : 0;
unsigned int count : 12;
};
readyoccupies 1 bit (0 or 1)erroroccupies 1 bitmodeoccupies 3 bits (0-7): 0forces alignment to the next storage unitcountoccupies 12 bits
struct Flags f = {1, 0, 5, 1024};
printf("ready=%u error=%u mode=%u count=%u\n", f.ready, f.error, f.mode, f.count);
printf("Struct size: %zu\n", sizeof(f));
ready=1 error=0 mode=5 count=1024
Struct size: 8
&f.ready is illegal).
Flexible Array Members
C99 allows the last member of a struct to be an array of length 0, called a Flexible Array Member.
typedef struct {
int len;
int data[];
} IntVec;
When using it, allocate extra space as needed:
int n = 5;
IntVec *v = (IntVec *)malloc(sizeof(IntVec) + sizeof(int) * n);
v->len = n;
for (int i = 0; i < n; i++) {
v->data[i] = i * 100;
}
for (int i = 0; i < v->len; i++) {
printf("%d ", v->data[i]);
}
free(v);
0 100 200 300 400
sizeof to get the size of a flexible array.
❓ FAQ
& operator cannot be used on bit-fields.📖 Summary
- Functions can return structs; for large structs, prefer filling through a pointer for efficiency
typedefcreates type aliases, simplifying struct type names- Structs have memory alignment — member ordering affects total size
#pragma packcan override alignment rules, useful for protocol parsing and similar scenarios- Bit-fields allocate space bit by bit, saving memory but are non-addressable and non-portable
- Flexible array members enable variable-length structs with contiguous data and simple allocation/deallocation
📝 Exercises
- Define a struct containing char, short, int, and double. Try two different member orderings and use sizeof to verify the size difference
- Use typedef to define a linked list node type, then write functions to create and traverse a singly linked list
- Use a flexible array member to implement a dynamic string struct (with len and data[]), supporting an append-character operation



