C Structures - Complete Guide
Master C structures including structures vs unions, structure arrays, pointers to structures, nested structures, bit fields, typedef, and programming best practices.
Structures
User-defined types
vs Unions
Key differences
Arrays & Pointers
Advanced usage
Bit Fields
Memory optimization
Introduction to C Structures
Structures (struct) in C allow you to combine different data types into a single user-defined data type. They enable you to create complex data types that group related variables together, making code more organized and manageable.
Why Structures Matter
- Data Grouping: Combine related data items
- Code Organization: Better code structure and readability
- Real-world Modeling: Represent real-world entities (Student, Employee, etc.)
- Function Arguments: Pass multiple related values as single parameter
- Data Structures: Foundation for linked lists, trees, graphs
- Memory Efficiency: Better memory organization than individual variables
Structure Concepts
- Structure: Collection of different data types
- Union: Shares memory between members
- Array of Structures: Multiple structure instances
- Pointer to Structure: Dynamic structure access
- typedef: Create type aliases
- Nested Structures: Structure within structure
Core Concepts of Structures
Structures create user-defined data types that group related variables of different types. Each variable in a structure is called a member. Structures allocate memory for all members separately, while unions share memory among members. Understanding this fundamental difference is crucial for effective C programming.
Structures vs Unions Comparison
Here is a comprehensive comparison of structures and unions in C:
| Feature | Structure (struct) | Union (union) |
|---|---|---|
| Keyword | struct |
union |
| Memory Allocation | Memory for all members (sum of sizes) | Memory for largest member only |
| Memory Usage | More memory (all members stored) | Less memory (shared space) |
| Access to Members | All members accessible simultaneously | Only one member valid at a time |
| Initialization | All members can be initialized | Only first member can be initialized |
| Use Case | Group related data of different types | Store different types in same memory |
| Size | Sum of all member sizes (+ padding) | Size of largest member (+ padding) |
| Example | struct Student {int id; char name[50];}; |
union Data {int i; float f; char str[20];}; |
Memory Layout Visualization:
Structure Memory Layout
Total: 28 bytes (all members have separate memory)
Union Memory Layout
Total: 20 bytes (all members share same memory)
Basic Structures - Declaration and Usage
Structures allow you to define a new data type that groups variables of different types. Each variable in the structure is called a member or field.
Syntax:
// Structure declaration
struct structure_name {
data_type member1;
data_type member2;
// ... more members
};
// Structure variable declaration
struct structure_name variable_name;
// Accessing members
variable_name.member1 = value;
Key Concepts:
- Member Access: Use dot (.) operator for structure variables
- Memory Allocation: Contiguous memory for all members
- Padding: Compiler may add padding bytes for alignment
- Initialization: Can initialize during declaration
- Assignment: Structures can be assigned to each other (member-wise copy)
- Size: Use
sizeof()to get total size
Basic Structure Examples:
#include <stdio.h>
#include <string.h>
int main() {
printf("=== BASIC STRUCTURE OPERATIONS ===\n\n");
// Structure declaration
struct Student {
int id;
char name[50];
float marks;
char grade;
};
printf("1. Structure variable declaration and initialization:\n");
// Method 1: Declare then initialize
struct Student student1;
student1.id = 101;
strcpy(student1.name, "John Doe");
student1.marks = 85.5;
student1.grade = 'A';
printf(" Student 1: ID=%d, Name=%s, Marks=%.1f, Grade=%c\n",
student1.id, student1.name, student1.marks, student1.grade);
// Method 2: Initialize during declaration
struct Student student2 = {102, "Jane Smith", 92.0, 'A'};
printf(" Student 2: ID=%d, Name=%s, Marks=%.1f, Grade=%c\n",
student2.id, student2.name, student2.marks, student2.grade);
// Method 3: Designated initializers (C99)
struct Student student3 = {
.id = 103,
.name = "Bob Johnson",
.marks = 78.5,
.grade = 'B'
};
printf(" Student 3: ID=%d, Name=%s, Marks=%.1f, Grade=%c\n",
student3.id, student3.name, student3.marks, student3.grade);
printf("\n2. Structure size and alignment:\n");
printf(" Size of Student structure: %zu bytes\n", sizeof(struct Student));
printf(" Size of id (int): %zu bytes\n", sizeof(student1.id));
printf(" Size of name (char[50]): %zu bytes\n", sizeof(student1.name));
printf(" Size of marks (float): %zu bytes\n", sizeof(student1.marks));
printf(" Size of grade (char): %zu bytes\n", sizeof(student1.grade));
printf(" Note: Structure may have padding for alignment\n");
printf("\n3. Structure assignment (member-wise copy):\n");
struct Student student4;
student4 = student1; // Copy all members from student1 to student4
// Modify student4
student4.id = 104;
strcpy(student4.name, "Modified Name");
printf(" Original student1: %s (ID: %d)\n", student1.name, student1.id);
printf(" Copied student4: %s (ID: %d)\n", student4.name, student4.id);
printf(" Note: Assignment creates independent copy\n");
printf("\n4. Structure as function argument:\n");
// Function to display student
void displayStudent(struct Student s) {
printf(" Display: ID=%d, Name=%s, Marks=%.1f, Grade=%c\n",
s.id, s.name, s.marks, s.grade);
}
displayStudent(student1);
// Function to modify student (passed by value - creates copy)
void modifyStudent(struct Student s) {
s.id = 999;
strcpy(s.name, "Modified in Function");
printf(" Inside function: ID=%d, Name=%s\n", s.id, s.name);
}
modifyStudent(student1);
printf(" After function (original unchanged): %s (ID: %d)\n",
student1.name, student1.id);
printf("\n5. Returning structure from function:\n");
// Function to create and return a student
struct Student createStudent(int id, const char *name, float marks, char grade) {
struct Student s;
s.id = id;
strcpy(s.name, name);
s.marks = marks;
s.grade = grade;
return s;
}
struct Student student5 = createStudent(105, "Alice Brown", 88.5, 'A');
printf(" Created student5: %s (ID: %d, Marks: %.1f)\n",
student5.name, student5.id, student5.marks);
printf("\n6. Structure comparison:\n");
// Structures cannot be compared directly with ==
// if(student1 == student2) // ERROR: Invalid
// Must compare member by member
if(student1.id == student2.id &&
strcmp(student1.name, student2.name) == 0 &&
student1.marks == student2.marks &&
student1.grade == student2.grade) {
printf(" student1 and student2 are equal\n");
} else {
printf(" student1 and student2 are different\n");
}
printf("\n7. Anonymous structures (C11):\n");
// Structure without tag name
struct {
int x;
int y;
} point1, point2;
point1.x = 10;
point1.y = 20;
point2.x = 30;
point2.y = 40;
printf(" Anonymous structure points: (%d,%d) and (%d,%d)\n",
point1.x, point1.y, point2.x, point2.y);
return 0;
}
=== BASIC STRUCTURE OPERATIONS ===
1. Structure variable declaration and initialization:
Student 1: ID=101, Name=John Doe, Marks=85.5, Grade=A
Student 2: ID=102, Name=Jane Smith, Marks=92.0, Grade=A
Student 3: ID=103, Name=Bob Johnson, Marks=78.5, Grade=B
2. Structure size and alignment:
Size of Student structure: 60 bytes
Size of id (int): 4 bytes
Size of name (char[50]): 50 bytes
Size of marks (float): 4 bytes
Size of grade (char): 1 byte
Note: Structure may have padding for alignment
3. Structure assignment (member-wise copy):
Original student1: John Doe (ID: 101)
Copied student4: Modified Name (ID: 104)
Note: Assignment creates independent copy
4. Structure as function argument:
Display: ID=101, Name=John Doe, Marks=85.5, Grade=A
Inside function: ID=999, Name=Modified in Function
After function (original unchanged): John Doe (ID: 101)
5. Returning structure from function:
Created student5: Alice Brown (ID: 105, Marks: 88.5)
6. Structure comparison:
student1 and student2 are different
7. Anonymous structures (C11):
Anonymous structure points: (10,20) and (30,40)
sizeof() a structure may be larger than the sum of its members. Use #pragma pack(1) to disable padding
(but may cause performance issues on some architectures).
Unions - Complete Guide
Unions are similar to structures but all members share the same memory location. Only one member can contain a value at any given time. Unions are useful for saving memory when you need to store different types of data in the same location.
Syntax:
// Union declaration
union union_name {
data_type member1;
data_type member2;
// ... more members
};
// Union variable declaration
union union_name variable_name;
// Accessing members
variable_name.member1 = value;
Key Characteristics:
- Shared Memory: All members share the same memory space
- Size: Size of union = size of its largest member
- One Active Member: Only one member contains valid data at a time
- Memory Efficiency: Uses less memory than equivalent structure
- Type Punning: Can be used to interpret same memory as different types
- Initialization: Can only initialize first member during declaration
Union Examples:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
int main() {
printf("=== COMPREHENSIVE UNION USAGE ===\n\n");
printf("1. Basic union example:\n");
union Data {
int i;
float f;
char str[20];
};
union Data data;
printf(" Size of union Data: %zu bytes\n", sizeof(union Data));
printf(" Size of int: %zu, float: %zu, char[20]: %zu\n",
sizeof(int), sizeof(float), sizeof(char[20]));
// Store integer
data.i = 42;
printf(" data.i = %d\n", data.i);
printf(" data.f = %f (garbage - interpreting int as float)\n", data.f);
// Store float (overwrites integer)
data.f = 3.14159;
printf(" After storing float:\n");
printf(" data.f = %f\n", data.f);
printf(" data.i = %d (garbage - interpreting float as int)\n", data.i);
// Store string (overwrites float)
strcpy(data.str, "Hello, Union!");
printf(" After storing string:\n");
printf(" data.str = %s\n", data.str);
printf(" data.i = %d (garbage)\n", data.i);
printf("\n2. Union vs Structure memory comparison:\n");
struct StructData {
int i;
float f;
char str[20];
};
union UnionData {
int i;
float f;
char str[20];
};
printf(" Size of struct StructData: %zu bytes\n", sizeof(struct StructData));
printf(" Size of union UnionData: %zu bytes\n", sizeof(union UnionData));
printf(" Memory saved: %zu bytes\n",
sizeof(struct StructData) - sizeof(union UnionData));
printf("\n3. Practical union use cases:\n");
// Case 1: Variant type (store different types)
typedef enum { TYPE_INT, TYPE_FLOAT, TYPE_STRING } DataType;
typedef struct {
DataType type;
union {
int intValue;
float floatValue;
char stringValue[50];
} data;
} Variant;
Variant var;
var.type = TYPE_INT;
var.data.intValue = 100;
printf(" Variant as int: type=%d, value=%d\n", var.type, var.data.intValue);
var.type = TYPE_FLOAT;
var.data.floatValue = 3.14;
printf(" Variant as float: type=%d, value=%.2f\n", var.type, var.data.floatValue);
// Important: Clear string before use
var.type = TYPE_STRING;
memset(var.data.stringValue, 0, sizeof(var.data.stringValue));
strcpy(var.data.stringValue, "Hello Variant");
printf(" Variant as string: type=%d, value=%s\n", var.type, var.data.stringValue);
printf("\n4. Type punning with unions (C99 legal):\n");
union FloatPun {
float f;
uint32_t i;
} pun;
pun.f = 3.14159;
printf(" Float value: %.5f\n", pun.f);
printf(" Same bits as integer: 0x%08X\n", pun.i);
// Extract sign, exponent, mantissa
uint32_t sign = (pun.i >> 31) & 1;
uint32_t exponent = (pun.i >> 23) & 0xFF;
uint32_t mantissa = pun.i & 0x7FFFFF;
printf(" IEEE 754 representation:\n");
printf(" Sign: %u (%s)\n", sign, sign ? "negative" : "positive");
printf(" Exponent: %u (biased), %d (unbiased)\n", exponent, exponent - 127);
printf(" Mantissa: 0x%06X\n", mantissa);
printf("\n5. Hardware register access (common in embedded systems):\n");
// Simulating a hardware control register
union ControlRegister {
uint32_t raw;
struct {
uint32_t enable : 1;
uint32_t mode : 3;
uint32_t speed : 4;
uint32_t reserved : 24;
} bits;
} ctrl;
ctrl.raw = 0;
ctrl.bits.enable = 1;
ctrl.bits.mode = 5;
ctrl.bits.speed = 9;
printf(" Control register:\n");
printf(" Raw value: 0x%08X\n", ctrl.raw);
printf(" Bits - Enable: %u, Mode: %u, Speed: %u\n",
ctrl.bits.enable, ctrl.bits.mode, ctrl.bits.speed);
printf("\n6. Network packet parsing:\n");
// Simulating network packet with different types
typedef enum { PACKET_TYPE_A, PACKET_TYPE_B } PacketType;
typedef struct {
PacketType type;
uint16_t length;
union {
struct {
uint8_t command;
uint16_t param1;
uint32_t param2;
} typeA;
struct {
uint32_t data[4];
uint8_t flags;
} typeB;
} payload;
} NetworkPacket;
NetworkPacket packet;
// Parse as Type A
packet.type = PACKET_TYPE_A;
packet.length = sizeof(packet.payload.typeA);
packet.payload.typeA.command = 0x10;
packet.payload.typeA.param1 = 0xABCD;
packet.payload.typeA.param2 = 0x12345678;
printf(" Packet Type A:\n");
printf(" Command: 0x%02X, Param1: 0x%04X, Param2: 0x%08X\n",
packet.payload.typeA.command,
packet.payload.typeA.param1,
packet.payload.typeA.param2);
// Same memory, now interpret as Type B
printf("\n Same memory as Type B (garbage):\n");
printf(" Data[0]: 0x%08X, Flags: 0x%02X\n",
packet.payload.typeB.data[0],
packet.payload.typeB.flags);
printf("\n7. Union arrays for mixed data storage:\n");
union MixedArray {
int intArray[10];
float floatArray[10];
char charArray[40];
} mixed;
// Store as integers
for(int i = 0; i < 10; i++) {
mixed.intArray[i] = i * 10;
}
printf(" As integers: ");
for(int i = 0; i < 5; i++) {
printf("%d ", mixed.intArray[i]);
}
printf("...\n");
// Same memory as floats
printf(" Same as floats (garbage): ");
for(int i = 0; i < 5; i++) {
printf("%.1f ", mixed.floatArray[i]);
}
printf("...\n");
printf("\n8. Important union warnings:\n");
// WARNING 1: Reading wrong member
union Data d;
d.i = 65;
printf(" Setting d.i = 65\n");
printf(" Reading d.str = %s (may work if ASCII 'A' then null)\n", d.str);
// WARNING 2: Only last written member is valid
union Data d2;
d2.i = 100;
d2.f = 3.14; // Overwrites the integer
// d2.i is now invalid!
printf(" d2.i = %d (invalid after d2.f = 3.14)\n", d2.i);
// WARNING 3: Union in structures need careful handling
typedef struct {
int type;
union {
int x;
float y;
} value;
} TaggedUnion;
printf("\n Always: Keep track of which union member is active!\n");
return 0;
}
- Only one union member contains valid data at any time
- Reading from a member other than the last one written to causes undefined behavior
- Always keep track of which member is currently active
- Use tagged unions (struct with type field + union) for safety
- Unions can be used for type punning in C99 (legal), but be careful
- Initialize unions properly - only first member can be initialized in declaration
- Unions containing pointers need special care (alignment issues)
Structure Arrays - Complete Guide
Arrays of structures allow you to store multiple instances of a structure in contiguous memory. This is useful for managing collections of similar data, like student records, employee data, or inventory items.
Syntax:
// Array of structures
struct StructureName array_name[size];
// Accessing elements
array_name[index].member = value;
// Dynamic array of structures
struct StructureName *ptr = malloc(n * sizeof(struct StructureName));
Key Characteristics:
- Contiguous Memory: All structures stored sequentially
- Indexed Access: Use array indexing to access elements
- Memory Layout: Predictable memory pattern
- Efficient Processing: Can process all elements in loops
- Dynamic Arrays: Can allocate at runtime using malloc/calloc
- Multi-dimensional: Can create 2D arrays of structures
Structure Array Examples:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// Student structure
typedef struct {
int id;
char name[50];
float marks[5]; // Array inside structure
float average;
char grade;
} Student;
// Function prototypes
void printStudent(const Student *s);
float calculateAverage(const float marks[], int count);
char calculateGrade(float average);
void sortStudentsById(Student students[], int count);
void sortStudentsByAverage(Student students[], int count);
int main() {
printf("=== COMPREHENSIVE STRUCTURE ARRAY USAGE ===\n\n");
printf("1. Static array of structures:\n");
// Array of 5 students
Student class[5];
// Initialize students
for(int i = 0; i < 5; i++) {
class[i].id = 1000 + i;
sprintf(class[i].name, "Student%d", i + 1);
// Initialize marks
for(int j = 0; j < 5; j++) {
class[i].marks[j] = 60 + (i * 5) + j;
}
// Calculate average and grade
class[i].average = calculateAverage(class[i].marks, 5);
class[i].grade = calculateGrade(class[i].average);
}
printf(" Student records:\n");
for(int i = 0; i < 5; i++) {
printf(" ");
printStudent(&class[i]);
}
printf("\n2. Array of structures with initialization:\n");
// Initialize array during declaration
Student initializedClass[] = {
{2001, "Alice", {85, 90, 88, 92, 87}, 0, ' '},
{2002, "Bob", {78, 82, 79, 85, 80}, 0, ' '},
{2003, "Charlie", {92, 95, 88, 90, 93}, 0, ' '}
};
// Calculate averages and grades
int count = sizeof(initializedClass) / sizeof(initializedClass[0]);
for(int i = 0; i < count; i++) {
initializedClass[i].average = calculateAverage(initializedClass[i].marks, 5);
initializedClass[i].grade = calculateGrade(initializedClass[i].average);
}
printf(" Initialized class (%d students):\n", count);
for(int i = 0; i < count; i++) {
printf(" ");
printStudent(&initializedClass[i]);
}
printf("\n3. Dynamic array of structures:\n");
int dynamicCount;
printf(" Enter number of students: ");
scanf("%d", &dynamicCount);
// Allocate dynamic array
Student *dynamicClass = (Student*)malloc(dynamicCount * sizeof(Student));
if(dynamicClass == NULL) {
printf(" Memory allocation failed!\n");
return 1;
}
// Initialize dynamic array
for(int i = 0; i < dynamicCount; i++) {
dynamicClass[i].id = 3000 + i;
sprintf(dynamicClass[i].name, "DynamicStudent%d", i + 1);
for(int j = 0; j < 5; j++) {
dynamicClass[i].marks[j] = 70 + (i * 3) + j;
}
dynamicClass[i].average = calculateAverage(dynamicClass[i].marks, 5);
dynamicClass[i].grade = calculateGrade(dynamicClass[i].average);
}
printf(" Dynamic class (%d students):\n", dynamicCount);
for(int i = 0; i < dynamicCount && i < 3; i++) { // Show first 3
printf(" ");
printStudent(&dynamicClass[i]);
}
if(dynamicCount > 3) {
printf(" ... and %d more\n", dynamicCount - 3);
}
printf("\n4. Sorting array of structures:\n");
// Sort by ID
sortStudentsById(class, 5);
printf(" Sorted by ID:\n");
for(int i = 0; i < 5; i++) {
printf(" ID: %d, Name: %s\n", class[i].id, class[i].name);
}
// Sort by average
sortStudentsByAverage(class, 5);
printf("\n Sorted by average (highest first):\n");
for(int i = 0; i < 5; i++) {
printf(" %s: %.2f (%c)\n",
class[i].name, class[i].average, class[i].grade);
}
printf("\n5. 2D array of structures:\n");
// 3 classes, 4 students each
Student school[3][4];
// Initialize 2D array
for(int classNum = 0; classNum < 3; classNum++) {
for(int studentNum = 0; studentNum < 4; studentNum++) {
school[classNum][studentNum].id = 4000 + (classNum * 100) + studentNum;
sprintf(school[classNum][studentNum].name,
"C%d_S%d", classNum + 1, studentNum + 1);
for(int mark = 0; mark < 5; mark++) {
school[classNum][studentNum].marks[mark] =
60 + (classNum * 10) + (studentNum * 5) + mark;
}
school[classNum][studentNum].average =
calculateAverage(school[classNum][studentNum].marks, 5);
school[classNum][studentNum].grade =
calculateGrade(school[classNum][studentNum].average);
}
}
printf(" 2D array (3 classes x 4 students):\n");
for(int classNum = 0; classNum < 3; classNum++) {
printf(" Class %d:\n", classNum + 1);
for(int studentNum = 0; studentNum < 4; studentNum++) {
printf(" ");
printStudent(&school[classNum][studentNum]);
}
}
printf("\n6. Array of structures with pointers:\n");
// Array of pointers to structures
Student *studentPtrs[5];
for(int i = 0; i < 5; i++) {
studentPtrs[i] = &class[i]; // Point to existing structures
}
printf(" Array of pointers to students:\n");
for(int i = 0; i < 5; i++) {
printf(" Pointer[%d] -> ", i);
printStudent(studentPtrs[i]);
}
// Modify through pointer
studentPtrs[0]->id = 9999;
strcpy(studentPtrs[0]->name, "Modified Through Pointer");
printf("\n After modification through pointer:\n");
printf(" Original: ");
printStudent(&class[0]);
printf("\n7. Passing structure array to functions:\n");
// Function to find student with highest average
Student* findTopStudent(Student students[], int count) {
if(count <= 0) return NULL;
Student *top = &students[0];
for(int i = 1; i < count; i++) {
if(students[i].average > top->average) {
top = &students[i];
}
}
return top;
}
Student *topStudent = findTopStudent(class, 5);
if(topStudent != NULL) {
printf(" Top student: ");
printStudent(topStudent);
}
// Function to count students by grade
int countByGrade(Student students[], int count, char grade) {
int counter = 0;
for(int i = 0; i < count; i++) {
if(students[i].grade == grade) {
counter++;
}
}
return counter;
}
printf(" Grade distribution:\n");
char grades[] = {'A', 'B', 'C', 'D', 'F'};
for(int i = 0; i < 5; i++) {
int count = countByGrade(class, 5, grades[i]);
printf(" Grade %c: %d students\n", grades[i], count);
}
printf("\n8. Array of unions:\n");
union Value {
int intVal;
float floatVal;
char strVal[20];
};
union Value values[5];
// Store different types in array
values[0].intVal = 100;
values[1].floatVal = 3.14;
strcpy(values[2].strVal, "Hello");
values[3].intVal = 200;
values[4].floatVal = 2.718;
printf(" Array of unions (interpreted as integers):\n ");
for(int i = 0; i < 5; i++) {
printf("%d ", values[i].intVal);
}
printf("\n Note: Only some values are valid as integers\n");
// Free dynamic memory
free(dynamicClass);
dynamicClass = NULL;
return 0;
}
// Function implementations
void printStudent(const Student *s) {
printf("ID: %4d, Name: %-15s, Avg: %5.2f, Grade: %c\n",
s->id, s->name, s->average, s->grade);
}
float calculateAverage(const float marks[], int count) {
float sum = 0;
for(int i = 0; i < count; i++) {
sum += marks[i];
}
return sum / count;
}
char calculateGrade(float average) {
if(average >= 90) return 'A';
if(average >= 80) return 'B';
if(average >= 70) return 'C';
if(average >= 60) return 'D';
return 'F';
}
void sortStudentsById(Student students[], int count) {
for(int i = 0; i < count - 1; i++) {
for(int j = 0; j < count - i - 1; j++) {
if(students[j].id > students[j + 1].id) {
Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
}
void sortStudentsByAverage(Student students[], int count) {
for(int i = 0; i < count - 1; i++) {
for(int j = 0; j < count - i - 1; j++) {
if(students[j].average < students[j + 1].average) {
Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
}
Pointers to Structures - Complete Guide
Pointers to structures allow dynamic allocation of structures, passing structures efficiently to functions (by reference), and creating complex data structures like linked lists and trees.
Syntax:
// Pointer to structure
struct StructureName *ptr;
// Dynamic allocation
ptr = (struct StructureName*)malloc(sizeof(struct StructureName));
// Accessing members
(*ptr).member = value; // Method 1
ptr->member = value; // Method 2 (arrow operator)
Key Concepts:
- Arrow Operator (->): Shortcut for
(*ptr).member - Dynamic Allocation: Create structures at runtime
- Pass by Reference: Efficient function parameter passing
- Linked Structures: Foundation for linked lists, trees
- Memory Management: Must free allocated structures
- Pointer Arithmetic: Works with arrays of structures
Pointer to Structure Examples:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Basic structure
typedef struct {
int id;
char name[50];
float salary;
} Employee;
// Linked list node
typedef struct Node {
Employee data;
struct Node *next;
} Node;
// Binary tree node
typedef struct TreeNode {
Employee data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// Function prototypes
void printEmployee(const Employee *emp);
Employee* createEmployee(int id, const char *name, float salary);
void modifyEmployee(Employee *emp, float newSalary);
Node* createNode(const Employee *emp);
void printList(Node *head);
TreeNode* createTreeNode(const Employee *emp);
void printTree(TreeNode *root);
int main() {
printf("=== COMPREHENSIVE POINTER TO STRUCTURE USAGE ===\n\n");
printf("1. Basic pointer to structure:\n");
Employee emp1 = {101, "John Doe", 50000.0};
Employee *ptr = &emp1;
printf(" Accessing through pointer:\n");
printf(" Using (*ptr). : ID=%d, Name=%s, Salary=%.2f\n",
(*ptr).id, (*ptr).name, (*ptr).salary);
printf(" Using ptr-> : ID=%d, Name=%s, Salary=%.2f\n",
ptr->id, ptr->name, ptr->salary);
// Modify through pointer
ptr->salary = 55000.0;
strcpy(ptr->name, "John Smith");
printf(" After modification: %s, $%.2f\n", emp1.name, emp1.salary);
printf("\n2. Dynamic structure allocation:\n");
// Single dynamic structure
Employee *dynamicEmp = (Employee*)malloc(sizeof(Employee));
if(dynamicEmp == NULL) {
printf(" Memory allocation failed!\n");
return 1;
}
dynamicEmp->id = 102;
strcpy(dynamicEmp->name, "Jane Smith");
dynamicEmp->salary = 60000.0;
printf(" Dynamic employee: ");
printEmployee(dynamicEmp);
// Free single dynamic structure
free(dynamicEmp);
dynamicEmp = NULL;
printf("\n3. Dynamic array of structures:\n");
int count = 3;
Employee *empArray = (Employee*)malloc(count * sizeof(Employee));
if(empArray == NULL) {
printf(" Array allocation failed!\n");
return 1;
}
// Initialize array
for(int i = 0; i < count; i++) {
empArray[i].id = 200 + i;
sprintf(empArray[i].name, "Employee%d", i + 1);
empArray[i].salary = 45000.0 + (i * 5000);
}
printf(" Dynamic array:\n");
for(int i = 0; i < count; i++) {
printf(" ");
printEmployee(&empArray[i]);
}
// Pointer arithmetic with structure array
Employee *current = empArray;
printf("\n Using pointer arithmetic:\n");
for(int i = 0; i < count; i++) {
printf(" ");
printEmployee(current);
current++; // Moves to next structure
}
free(empArray);
empArray = NULL;
printf("\n4. Functions with structure pointers:\n");
Employee *newEmp = createEmployee(301, "Bob Johnson", 48000.0);
if(newEmp != NULL) {
printf(" Created via function: ");
printEmployee(newEmp);
modifyEmployee(newEmp, 52000.0);
printf(" After raise: ");
printEmployee(newEmp);
free(newEmp);
newEmp = NULL;
}
printf("\n5. Linked list of structures:\n");
// Create employees
Employee emp2 = {401, "Alice Brown", 55000};
Employee emp3 = {402, "Charlie White", 60000};
Employee emp4 = {403, "Diana Green", 52000};
// Create linked list
Node *head = createNode(&emp1);
head->next = createNode(&emp2);
head->next->next = createNode(&emp3);
head->next->next->next = createNode(&emp4);
printf(" Linked list of employees:\n");
printList(head);
// Free linked list
Node *temp;
while(head != NULL) {
temp = head;
head = head->next;
free(temp);
}
printf("\n6. Binary tree of structures:\n");
// Create tree nodes
TreeNode *root = createTreeNode(&emp1);
root->left = createTreeNode(&emp2);
root->right = createTreeNode(&emp3);
root->left->left = createTreeNode(&emp4);
printf(" Binary tree (in-order):\n");
printTree(root);
// Free tree (simplified - would need proper traversal)
free(root->left->left);
free(root->left);
free(root->right);
free(root);
printf("\n7. Array of pointers to structures:\n");
Employee employees[3] = {
{501, "Eve Black", 47000},
{502, "Frank Blue", 53000},
{503, "Grace Red", 49000}
};
// Array of pointers
Employee *empPtrs[3];
for(int i = 0; i < 3; i++) {
empPtrs[i] = &employees[i];
}
printf(" Array of pointers:\n");
for(int i = 0; i < 3; i++) {
printf(" ");
printEmployee(empPtrs[i]);
}
// Sort array of pointers (without moving structures)
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2 - i; j++) {
if(empPtrs[j]->salary > empPtrs[j + 1]->salary) {
Employee *tempPtr = empPtrs[j];
empPtrs[j] = empPtrs[j + 1];
empPtrs[j + 1] = tempPtr;
}
}
}
printf("\n Sorted by salary (pointers only):\n");
for(int i = 0; i < 3; i++) {
printf(" ");
printEmployee(empPtrs[i]);
}
printf("\n Original array unchanged:\n");
for(int i = 0; i < 3; i++) {
printf(" ");
printEmployee(&employees[i]);
}
printf("\n8. Pointer to pointer to structure:\n");
Employee emp5 = {601, "Henry Ford", 65000};
Employee *singlePtr = &emp5;
Employee **doublePtr = &singlePtr;
printf(" Double pointer access:\n");
printf(" (*doublePtr)->id = %d\n", (*doublePtr)->id);
printf(" (**doublePtr).id = %d\n", (**doublePtr).id);
// Modify through double pointer
(**doublePtr).salary = 70000;
printf(" After double pointer modification: $%.2f\n", emp5.salary);
printf("\n9. Self-referential structures (linked list):\n");
typedef struct ListNode {
int data;
struct ListNode *next;
} ListNode;
// Create simple linked list
ListNode *node1 = (ListNode*)malloc(sizeof(ListNode));
ListNode *node2 = (ListNode*)malloc(sizeof(ListNode));
ListNode *node3 = (ListNode*)malloc(sizeof(ListNode));
node1->data = 10;
node1->next = node2;
node2->data = 20;
node2->next = node3;
node3->data = 30;
node3->next = NULL;
printf(" Self-referential linked list:\n");
ListNode *currentNode = node1;
while(currentNode != NULL) {
printf(" Node data: %d\n", currentNode->data);
currentNode = currentNode->next;
}
free(node1);
free(node2);
free(node3);
printf("\n10. Common pointer to structure errors:\n");
// ERROR 1: Dereferencing NULL pointer
Employee *nullPtr = NULL;
// nullPtr->id = 100; // CRASH: Segmentation fault
// ERROR 2: Using pointer after free
Employee *freedPtr = (Employee*)malloc(sizeof(Employee));
freedPtr->id = 100;
free(freedPtr);
// freedPtr->id = 200; // ERROR: Use after free
// ERROR 3: Memory leak
// Employee *leak = malloc(sizeof(Employee));
// Forgot to free(leak);
printf(" Always: Check for NULL, Free memory, Set to NULL after free\n");
return 0;
}
// Function implementations
void printEmployee(const Employee *emp) {
printf("ID: %d, Name: %-15s, Salary: $%.2f\n",
emp->id, emp->name, emp->salary);
}
Employee* createEmployee(int id, const char *name, float salary) {
Employee *emp = (Employee*)malloc(sizeof(Employee));
if(emp == NULL) return NULL;
emp->id = id;
strcpy(emp->name, name);
emp->salary = salary;
return emp;
}
void modifyEmployee(Employee *emp, float newSalary) {
if(emp == NULL) return;
emp->salary = newSalary;
}
Node* createNode(const Employee *emp) {
Node *node = (Node*)malloc(sizeof(Node));
if(node == NULL) return NULL;
node->data = *emp; // Copy the employee data
node->next = NULL;
return node;
}
void printList(Node *head) {
Node *current = head;
int position = 1;
while(current != NULL) {
printf(" %d. ", position++);
printEmployee(¤t->data);
current = current->next;
}
}
TreeNode* createTreeNode(const Employee *emp) {
TreeNode *node = (TreeNode*)malloc(sizeof(TreeNode));
if(node == NULL) return NULL;
node->data = *emp;
node->left = NULL;
node->right = NULL;
return node;
}
void printTree(TreeNode *root) {
if(root == NULL) return;
printTree(root->left);
printf(" ");
printEmployee(&root->data);
printTree(root->right);
}
Nested Structures and Typedef
Nested structures allow you to create complex data types by including structures within other structures. The typedef keyword creates type aliases for easier structure usage.
Syntax:
// Nested structure
struct Date {
int day, month, year;
};
struct Employee {
int id;
char name[50];
struct Date dob; // Nested structure
struct Date joinDate;
};
// Typedef for simpler syntax
typedef struct Employee Employee;
// Or combined:
typedef struct {
int id;
char name[50];
} Person;
Key Concepts:
- Nested Structures: Structure containing other structures
- Multi-level Access: Use multiple dot operators:
emp.dob.day - Typedef: Creates type alias for easier declaration
- Self-Referential: Structures containing pointers to same type
- Forward Declaration: Declare structure before defining for mutual references
- Anonymous Structures: Structures without tag names
Nested Structures and Typedef Examples:
#include <stdio.h>
#include <string.h>
int main() {
printf("=== COMPREHENSIVE NESTED STRUCTURES AND TYPEDEF ===\n\n");
printf("1. Basic nested structures:\n");
// Date structure
struct Date {
int day;
int month;
int year;
};
// Person structure with nested Date
struct Person {
int id;
char name[50];
struct Date birthDate; // Nested structure
};
struct Person person1;
person1.id = 101;
strcpy(person1.name, "John Doe");
person1.birthDate.day = 15;
person1.birthDate.month = 6;
person1.birthDate.year = 1990;
printf(" Person: %s (ID: %d)\n", person1.name, person1.id);
printf(" Birth Date: %d/%d/%d\n",
person1.birthDate.day,
person1.birthDate.month,
person1.birthDate.year);
printf("\n2. Multiple level nesting:\n");
struct Address {
char street[50];
char city[30];
char state[20];
int zipCode;
};
struct Contact {
char email[50];
char phone[15];
struct Address address; // Nested Address
};
struct Employee {
int empId;
char name[50];
struct Contact contact; // Nested Contact
struct Date joinDate; // Nested Date
};
struct Employee emp1 = {
1001,
"Alice Smith",
{"alice@company.com", "123-456-7890",
{"123 Main St", "New York", "NY", 10001}},
{10, 3, 2020}
};
printf(" Employee: %s (ID: %d)\n", emp1.name, emp1.empId);
printf(" Email: %s, Phone: %s\n", emp1.contact.email, emp1.contact.phone);
printf(" Address: %s, %s, %s %d\n",
emp1.contact.address.street,
emp1.contact.address.city,
emp1.contact.address.state,
emp1.contact.address.zipCode);
printf(" Join Date: %d/%d/%d\n",
emp1.joinDate.day, emp1.joinDate.month, emp1.joinDate.year);
printf("\n3. Using typedef for cleaner syntax:\n");
// Method 1: Declare struct then typedef
struct Point {
int x;
int y;
};
typedef struct Point Point; // Now can use just 'Point'
// Method 2: Combine declaration and typedef
typedef struct {
float real;
float imag;
} Complex;
// Method 3: Typedef with tag (less common)
typedef struct Rectangle {
int width;
int height;
} Rectangle;
// Usage
Point p1 = {10, 20};
Complex c1 = {3.5, 2.8};
Rectangle r1 = {100, 50};
printf(" Point: (%d, %d)\n", p1.x, p1.y);
printf(" Complex: %.1f + %.1fi\n", c1.real, c1.imag);
printf(" Rectangle: %d x %d (area: %d)\n",
r1.width, r1.height, r1.width * r1.height);
printf("\n4. Arrays within structures:\n");
typedef struct {
int studentId;
char name[50];
float grades[5]; // Array inside structure
float average;
} Student;
Student student1;
student1.studentId = 2001;
strcpy(student1.name, "Bob Johnson");
// Initialize grades array
for(int i = 0; i < 5; i++) {
student1.grades[i] = 70.0 + (i * 5);
}
// Calculate average
float sum = 0;
for(int i = 0; i < 5; i++) {
sum += student1.grades[i];
}
student1.average = sum / 5;
printf(" Student: %s (ID: %d)\n", student1.name, student1.studentId);
printf(" Grades: ");
for(int i = 0; i < 5; i++) {
printf("%.1f ", student1.grades[i]);
}
printf("\n Average: %.2f\n", student1.average);
printf("\n5. Structure containing pointer to another structure:\n");
typedef struct Node {
int data;
struct Node *next; // Pointer to same type
} Node;
// Create simple linked list
Node node1 = {10, NULL};
Node node2 = {20, NULL};
Node node3 = {30, NULL};
node1.next = &node2;
node2.next = &node3;
printf(" Linked list:\n");
Node *current = &node1;
while(current != NULL) {
printf(" Node data: %d\n", current->data);
current = current->next;
}
printf("\n6. Mutual referencing structures (forward declaration):\n");
// Forward declaration
typedef struct Class Class;
typedef struct StudentNode {
char name[50];
Class *class; // Pointer to Class (not defined yet)
struct StudentNode *next;
} StudentNode;
struct Class {
char className[30];
StudentNode *students; // Pointer to StudentNode
int studentCount;
};
// Create instances
Class mathClass;
strcpy(mathClass.className, "Mathematics");
mathClass.studentCount = 0;
mathClass.students = NULL;
StudentNode studentA = {"Alice", &mathClass, NULL};
StudentNode studentB = {"Bob", &mathClass, NULL};
printf(" Class: %s\n", mathClass.className);
printf(" Student A: %s (in class: %s)\n",
studentA.name, studentA.class->className);
printf("\n7. Anonymous structures within structures:\n");
typedef struct {
int id;
// Anonymous structure
struct {
char first[30];
char last[30];
} name; // No tag name, just variable
float salary;
} Employee2;
Employee2 emp2;
emp2.id = 3001;
strcpy(emp2.name.first, "John");
strcpy(emp2.name.last, "Doe");
emp2.salary = 55000.0;
printf(" Employee: %s %s (ID: %d, Salary: $%.2f)\n",
emp2.name.first, emp2.name.last, emp2.id, emp2.salary);
printf("\n8. Typedef with function pointers in structures:\n");
typedef double (*MathFunc)(double);
typedef struct {
char operation[20];
MathFunc func;
} MathOperation;
// Math functions
double square(double x) { return x * x; }
double cube(double x) { return x * x * x; }
MathOperation ops[] = {
{"Square", square},
{"Cube", cube}
};
printf(" Math operations:\n");
for(int i = 0; i < 2; i++) {
double result = ops[i].func(3.0);
printf(" %s(3) = %.2f\n", ops[i].operation, result);
}
printf("\n9. Complex nested structure with typedef:\n");
// Define types
typedef struct { int x, y; } Point2D;
typedef struct { Point2D start, end; } Line;
typedef struct { Line edges[4]; } Rectangle2D;
// Create rectangle
Rectangle2D rect = {
.edges = {
{{0, 0}, {10, 0}}, // Top edge
{{10, 0}, {10, 5}}, // Right edge
{{10, 5}, {0, 5}}, // Bottom edge
{{0, 5}, {0, 0}} // Left edge
}
};
printf(" Rectangle coordinates:\n");
for(int i = 0; i < 4; i++) {
printf(" Edge %d: (%d,%d) to (%d,%d)\n", i + 1,
rect.edges[i].start.x, rect.edges[i].start.y,
rect.edges[i].end.x, rect.edges[i].end.y);
}
printf("\n10. Benefits of typedef:\n");
printf(" • Cleaner syntax: 'Employee' vs 'struct Employee'\n");
printf(" • Easier to change underlying type\n");
printf(" • Better code readability\n");
printf(" • Consistent naming conventions\n");
printf(" • Forward declarations easier\n");
return 0;
}
Bit Fields and Advanced Topics
Bit fields allow you to specify the exact number of bits that a structure member should occupy. This is useful for memory optimization, hardware register access, and packing data efficiently.
Syntax:
struct {
type member_name : width;
// ... more members
};
type: int, signed int, unsigned int (C99 allows _Bool)
width: Number of bits (1 to sizeof(type)*8)Key Characteristics:
- Memory Optimization: Pack multiple fields into single bytes
- Hardware Access: Map to hardware register bit fields
- Portability Issues: Implementation-dependent (order, padding)
- Limited Types: Mostly integer types
- No Address Operator: Cannot take address of bit field (&)
- Alignment: Compiler may insert padding between bit fields
Bit Field Visualization:
32-bit Integer with Bit Fields
IEEE 754 Single Precision Float (32 bits) using bit fields
Bit Field Examples:
#include <stdio.h>
#include <stdint.h>
int main() {
printf("=== COMPREHENSIVE BIT FIELD USAGE ===\n\n");
printf("1. Basic bit fields:\n");
struct StatusRegister {
unsigned int error : 1; // 1 bit for error flag
unsigned int ready : 1; // 1 bit for ready flag
unsigned int busy : 1; // 1 bit for busy flag
unsigned int reserved : 5; // 5 reserved bits
unsigned int mode : 4; // 4 bits for mode
unsigned int data : 8; // 8 bits for data
};
struct StatusRegister status;
printf(" Size of StatusRegister: %zu bytes\n", sizeof(struct StatusRegister));
printf(" Expected: 3 bytes (1+1+1+5+4+8 = 20 bits)\n");
printf(" Note: May be larger due to padding/alignment\n");
// Set values
status.error = 0;
status.ready = 1;
status.busy = 0;
status.mode = 5;
status.data = 0xAB;
printf(" Status: error=%u, ready=%u, busy=%u, mode=%u, data=0x%02X\n",
status.error, status.ready, status.busy, status.mode, status.data);
printf("\n2. Signed bit fields:\n");
struct SignedFields {
signed int temperature : 10; // 10-bit signed (-512 to 511)
unsigned int pressure : 12; // 12-bit unsigned (0 to 4095)
int : 0; // Force alignment to next boundary
unsigned int humidity : 8; // 8-bit unsigned (0 to 255)
};
struct SignedFields sensor;
sensor.temperature = -25; // Negative value in bit field
sensor.pressure = 1023;
sensor.humidity = 65;
printf(" Sensor: temp=%d°C, pressure=%u hPa, humidity=%u%%\n",
sensor.temperature, sensor.pressure, sensor.humidity);
printf(" Size: %zu bytes\n", sizeof(struct SignedFields));
printf("\n3. Anonymous bit fields for padding:\n");
struct PaddedRegister {
unsigned int flag1 : 1;
unsigned int : 3; // Anonymous - 3 bits padding
unsigned int flag2 : 1;
unsigned int : 0; // Zero-width - force to next boundary
unsigned int data : 16;
};
struct PaddedRegister reg;
printf(" Size with padding: %zu bytes\n", sizeof(struct PaddedRegister));
printf("\n4. Hardware register simulation:\n");
// Simulating a UART control register
union UARTControl {
uint16_t raw;
struct {
uint16_t enable : 1;
uint16_t parity : 2; // 00=none, 01=odd, 10=even
uint16_t stopBits : 1; // 0=1 bit, 1=2 bits
uint16_t dataBits : 2; // 00=5, 01=6, 10=7, 11=8
uint16_t baudRate : 3; // 8 possible baud rates
uint16_t interrupt : 1;
uint16_t : 6; // Reserved bits
} bits;
};
union UARTControl uart;
uart.raw = 0;
// Configure UART
uart.bits.enable = 1;
uart.bits.parity = 2; // Even parity
uart.bits.stopBits = 1; // 2 stop bits
uart.bits.dataBits = 3; // 8 data bits
uart.bits.baudRate = 4; // 9600 baud
uart.bits.interrupt = 1;
printf(" UART Configuration:\n");
printf(" Raw register: 0x%04X\n", uart.raw);
printf(" Bits - Enable: %u, Parity: %u, Stop: %u, Data: %u, Baud: %u, Int: %u\n",
uart.bits.enable, uart.bits.parity, uart.bits.stopBits,
uart.bits.dataBits, uart.bits.baudRate, uart.bits.interrupt);
printf("\n5. RGB color using bit fields:\n");
struct RGBColor {
unsigned int blue : 5; // 5 bits for blue (0-31)
unsigned int green : 6; // 6 bits for green (0-63)
unsigned int red : 5; // 5 bits for red (0-31)
};
struct RGBColor color;
color.red = 31; // Maximum red
color.green = 63; // Maximum green
color.blue = 31; // Maximum blue = white
printf(" RGB Color: R=%u, G=%u, B=%u (16-bit color)\n",
color.red, color.green, color.blue);
printf(" Size: %zu bytes\n", sizeof(struct RGBColor));
printf("\n6. IP header simulation:\n");
// Simplified IP header (first 32 bits)
union IPHeader {
uint32_t raw;
struct {
uint32_t version : 4; // IP version
uint32_t ihl : 4; // Internet Header Length
uint32_t dscp : 6; // Differentiated Services Code Point
uint32_t ecn : 2; // Explicit Congestion Notification
uint32_t totalLength : 16;// Total length
} fields;
};
union IPHeader ip;
ip.raw = 0x4500003C; // Typical IP header
printf(" IP Header:\n");
printf(" Raw: 0x%08X\n", ip.raw);
printf(" Version: %u, IHL: %u, DSCP: %u, ECN: %u, Length: %u\n",
ip.fields.version, ip.fields.ihl, ip.fields.dscp,
ip.fields.ecn, ip.fields.totalLength);
printf("\n7. Date packing using bit fields:\n");
struct PackedDate {
unsigned int day : 5; // 5 bits for day (1-31)
unsigned int month : 4; // 4 bits for month (1-12)
unsigned int year : 7; // 7 bits for year (0-127, offset from 2000)
};
struct PackedDate date;
date.day = 15;
date.month = 6;
date.year = 23; // 2023
printf(" Packed Date: %u/%u/20%u\n", date.day, date.month, date.year);
printf(" Size: %zu bytes (vs 12 bytes for 3 integers)\n",
sizeof(struct PackedDate));
printf("\n8. File permissions (Unix-style):\n");
struct FilePermissions {
unsigned int ownerRead : 1;
unsigned int ownerWrite : 1;
unsigned int ownerExecute : 1;
unsigned int groupRead : 1;
unsigned int groupWrite : 1;
unsigned int groupExecute : 1;
unsigned int othersRead : 1;
unsigned int othersWrite : 1;
unsigned int othersExecute : 1;
unsigned int setuid : 1;
unsigned int setgid : 1;
unsigned int sticky : 1;
};
struct FilePermissions perm;
perm.ownerRead = perm.ownerWrite = perm.ownerExecute = 1;
perm.groupRead = perm.groupExecute = 1;
perm.othersRead = 1;
printf(" File Permissions: ");
printf("%c%c%c", perm.ownerRead ? 'r' : '-',
perm.ownerWrite ? 'w' : '-',
perm.ownerExecute ? 'x' : '-');
printf("%c%c%c", perm.groupRead ? 'r' : '-',
perm.groupWrite ? 'w' : '-',
perm.groupExecute ? 'x' : '-');
printf("%c%c%c", perm.othersRead ? 'r' : '-',
perm.othersWrite ? 'w' : '-',
perm.othersExecute ? 'x' : '-');
printf("\n");
printf("\n9. Important bit field considerations:\n");
printf(" • Implementation-defined: Order, padding, alignment\n");
printf(" • No address operator: Cannot use & on bit fields\n");
printf(" • Portability: May work differently on different compilers\n");
printf(" • Limited types: Mostly integer types\n");
printf(" • Range checking: Your responsibility\n");
printf(" • Performance: May be slower than regular integers\n");
printf("\n10. When to use bit fields:\n");
printf(" • Hardware register access\n");
printf(" • Network protocol headers\n");
printf(" • Memory-constrained environments\n");
printf(" • Packing multiple boolean flags\n");
printf(" • Storing small-range values efficiently\n");
printf(" • When exact bit layout is required\n");
return 0;
}
- Implementation-defined: Order of bits, padding, alignment are compiler-dependent
- No addresses: Cannot take address of bit field with & operator
- Portability: Code with bit fields may not work across different compilers/platforms
- Limited types: Only int, signed int, unsigned int, _Bool (C99)
- Range checking: No automatic bounds checking - overflow wraps around
- Performance: May be slower due to bit manipulation
- Debugging: Harder to debug than regular variables
- Endianness: Bit order depends on endianness of platform
Common Mistakes and Best Practices
Structure Best Practices:
- Use typedef: Makes code cleaner and easier to read
- Initialize structures: Always initialize, especially dynamic ones
- Order members: Place larger members first to minimize padding
- Document: Comment structure purpose and member meanings
- Use const: When structures shouldn't be modified
- Avoid deep nesting: Too many levels make code hard to read
- Create helper functions: For common operations (init, copy, compare)
- Consider memory layout: For performance-critical code
- Test on different platforms: If using bit fields or specific alignment
- Use standard naming: Consistent naming conventions
Union Best Practices:
- Use tagged unions: Always keep track of active member
- Clear before use: When switching between members, especially strings
- Document active member: Clearly document which member should be accessed
- Use for memory optimization: When you need to store different types at different times
- Avoid for general use: Structures are usually better for grouping data
- Be careful with pointers: Union members with pointers need special handling
- Test thoroughly: Unions can cause subtle bugs
- Consider alternatives: Sometimes void pointers are better
Key Takeaways
- Structures (
struct) group related data of different types with separate memory for each member - Unions (
union) share memory between members - only one member is valid at a time - Use dot operator (.) for structure variables, arrow operator (->) for pointers to structures
- Structure arrays allow storing multiple instances, useful for collections
- Pointers to structures enable dynamic allocation and efficient function parameter passing
- Nested structures create complex data types by including structures within structures
typedefcreates type aliases for cleaner syntax:typedef struct { ... } TypeName;- Bit fields specify exact bit allocation for members, useful for memory optimization
- Structures cannot be compared directly with
==- must compare member by member - Structure padding may increase size - compilers add bytes for alignment
- Use designated initializers for clearer structure initialization
- Self-referential structures contain pointers to same type, used for linked lists and trees
- Unions are useful for variant types, hardware registers, and memory-efficient storage
- Always use tagged unions (struct with type field) to track active member
- Bit fields have portability issues - implementation-defined behavior
- Consider memory layout and alignment for performance-critical applications