Structures and Unions
Structure Basics in C
Table of Contents
- ā¢Introduction
- ā¢What is a Structure?
- ā¢Declaring Structures
- ā¢Creating Structure Variables
- ā¢Accessing Structure Members
- ā¢Initializing Structures
- ā¢Structure Assignment
- ā¢Size of Structures
- ā¢Structure Padding and Alignment
- ā¢typedef with Structures
- ā¢Anonymous Structures
- ā¢Structures vs Arrays
- ā¢Common Use Cases
- ā¢Best Practices
- ā¢Summary
Introduction
Structures are one of the most powerful features in C programming. They allow you to group related variables of different data types under a single name, creating custom data types that can represent real-world entities.
Why Do We Need Structures?
Consider storing information about a student:
- ā¢Name (string)
- ā¢Roll number (integer)
- ā¢GPA (float)
- ā¢Age (integer)
Without structures, you would need separate arrays for each attribute:
char names[100][50];
int roll_numbers[100];
float gpas[100];
int ages[100];
This approach has problems:
- ā¢Data is scattered across multiple arrays
- ā¢Difficult to manage related data together
- ā¢Easy to make indexing mistakes
- ā¢Cannot pass all student data as a single unit
With structures, all related data stays together:
struct Student {
char name[50];
int roll_number;
float gpa;
int age;
};
What is a Structure?
A structure is a user-defined data type that groups together variables of different types under a single name. Each variable within a structure is called a member or field.
Key Characteristics
| Feature | Description |
|---|---|
| Heterogeneous | Can contain different data types |
| Contiguous Memory | Members stored in consecutive memory locations |
| Named Access | Members accessed by name, not index |
| User-Defined | You define the structure template |
| Value Type | Structures are passed by value (copied) |
Visual Representation
struct Student
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā name[50] ā roll_number ā gpa ā age ā
ā (50 bytes) ā (4 bytes) ā(4 bytes)ā(4 bytes)ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Declaring Structures
Basic Syntax
struct structure_name {
data_type member1;
data_type member2;
// ... more members
};
Example: Student Structure
struct Student {
char name[50];
int roll_number;
float gpa;
int age;
};
Example: Point Structure
struct Point {
int x;
int y;
};
Example: Date Structure
struct Date {
int day;
int month;
int year;
};
Example: Complex Structure
struct Employee {
char name[100];
int emp_id;
char department[50];
float salary;
struct Date joining_date; // Nested structure
int is_active;
};
Important Notes
- ā¢Semicolon Required: Structure declaration MUST end with semicolon
- ā¢No Memory Allocated: Declaration only creates a template
- ā¢Scope: Structure declaration follows normal scoping rules
- ā¢Self-Reference: Structure can contain pointer to itself
Creating Structure Variables
Method 1: Declare Then Define Variables
// Declaration
struct Student {
char name[50];
int roll_number;
float gpa;
};
// Define variables
struct Student s1;
struct Student s2, s3;
Method 2: Declare and Define Together
struct Student {
char name[50];
int roll_number;
float gpa;
} s1, s2, s3; // Variables declared here
Method 3: Using typedef
typedef struct {
char name[50];
int roll_number;
float gpa;
} Student;
Student s1; // No 'struct' keyword needed
Student s2;
Global vs Local Structure Variables
struct Point {
int x;
int y;
};
struct Point global_point; // Global variable (initialized to 0)
int main() {
struct Point local_point; // Local variable (uninitialized)
static struct Point static_point; // Static (initialized to 0)
return 0;
}
Accessing Structure Members
Dot Operator (.)
The dot operator is used to access members of a structure variable.
struct Student s1;
// Assigning values
s1.roll_number = 101;
s1.gpa = 3.75;
s1.age = 20;
strcpy(s1.name, "John Doe");
// Reading values
printf("Name: %s\n", s1.name);
printf("Roll: %d\n", s1.roll_number);
printf("GPA: %.2f\n", s1.gpa);
Accessing Nested Structure Members
struct Date {
int day, month, year;
};
struct Employee {
char name[50];
struct Date dob;
struct Date joining;
};
struct Employee emp;
// Access nested members with multiple dots
emp.dob.day = 15;
emp.dob.month = 8;
emp.dob.year = 1990;
emp.joining.year = 2020;
Arrow Operator (->)
Used with structure pointers (covered in Pointers to Structures topic):
struct Student *ptr = &s1;
// Using arrow operator
ptr->roll_number = 101;
printf("%s\n", ptr->name);
// Equivalent to
(*ptr).roll_number = 101;
Initializing Structures
Method 1: Member-by-Member
struct Student s1;
s1.roll_number = 101;
s1.gpa = 3.75;
s1.age = 20;
strcpy(s1.name, "John");
Method 2: Initializer List
struct Student s1 = {"John Doe", 101, 3.75, 20};
Members must be in the same order as declared.
Method 3: Designated Initializers (C99)
struct Student s1 = {
.name = "John Doe",
.roll_number = 101,
.gpa = 3.75,
.age = 20
};
Benefits:
- ā¢Order doesn't matter
- ā¢Self-documenting code
- ā¢Unspecified members initialized to zero
Method 4: Partial Initialization
struct Student s1 = {"John"}; // Only name initialized
// roll_number, gpa, age are set to 0
Method 5: Zero Initialization
struct Student s1 = {0}; // All members set to zero/null
Initializing Nested Structures
struct Employee emp = {
.name = "Alice",
.dob = {15, 8, 1990},
.joining = {.day = 1, .month = 6, .year = 2020}
};
Structure Assignment
Direct Assignment
Structures can be directly assigned to each other (unlike arrays).
struct Student s1 = {"John", 101, 3.75, 20};
struct Student s2;
s2 = s1; // Complete copy - all members copied
What Happens During Assignment
struct Student s1 = {"John", 101, 3.75, 20};
struct Student s2 = s1; // Copy initialization
// After assignment:
// s2.name = "John" (copied character by character)
// s2.roll_number = 101
// s2.gpa = 3.75
// s2.age = 20
// s1 and s2 are independent - changing one doesn't affect other
s2.roll_number = 102; // s1.roll_number still 101
Important: Shallow Copy
Structure assignment performs a shallow copy:
struct Data {
int value;
char *ptr; // Pointer member
};
struct Data d1;
d1.value = 100;
d1.ptr = malloc(50);
strcpy(d1.ptr, "Hello");
struct Data d2 = d1; // Shallow copy
// d1.ptr and d2.ptr point to SAME memory!
// Modifying d2.ptr affects d1.ptr
// Freeing one leaves the other as dangling pointer
Comparing Structures
Structures cannot be compared directly with == operator:
struct Point p1 = {10, 20};
struct Point p2 = {10, 20};
// if (p1 == p2) // ERROR! Won't compile
// Must compare member by member
if (p1.x == p2.x && p1.y == p2.y) {
printf("Points are equal\n");
}
Size of Structures
Using sizeof Operator
struct Student {
char name[50];
int roll_number;
float gpa;
int age;
};
printf("Size: %zu bytes\n", sizeof(struct Student));
Size May Not Be Sum of Members
Due to padding, structure size can be larger than the sum of member sizes:
struct Example {
char a; // 1 byte
int b; // 4 bytes
char c; // 1 byte
};
// Expected: 1 + 4 + 1 = 6 bytes
// Actual: 12 bytes (due to padding)
Structure Padding and Alignment
Why Padding?
CPUs access memory most efficiently when data is aligned to certain boundaries. The compiler adds padding bytes to ensure proper alignment.
Alignment Rules
| Data Type | Typical Alignment |
|---|---|
| char | 1 byte |
| short | 2 bytes |
| int | 4 bytes |
| float | 4 bytes |
| double | 8 bytes |
| pointer | 4 or 8 bytes |
Visual Example
struct Padded {
char a; // 1 byte
// 3 bytes padding
int b; // 4 bytes
char c; // 1 byte
// 3 bytes padding
};
// Total: 12 bytes
Memory Layout:
āāāāāāā¬āāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāā¬āāāāāā¬āāāāāāāāāāāāāā
ā a ā padding ā b ā c ā padding ā
ā 1B ā 3B ā 4B ā 1B ā 3B ā
āāāāāāā“āāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāā“āāāāāā“āāāāāāāāāāāāāā
Offset: 0 1-3 4-7 8 9-11
Minimizing Padding
Order members from largest to smallest:
// Poorly ordered - 12 bytes
struct Bad {
char a; // 1 + 3 padding
int b; // 4
char c; // 1 + 3 padding
};
// Well ordered - 8 bytes
struct Good {
int b; // 4
char a; // 1
char c; // 1 + 2 padding
};
Packing Structures
Force no padding (may reduce performance):
// GCC specific
struct __attribute__((packed)) Packed {
char a;
int b;
char c;
};
// Size: 6 bytes (no padding)
// Or use pragma
#pragma pack(push, 1)
struct Packed {
char a;
int b;
char c;
};
#pragma pack(pop)
typedef with Structures
Basic Usage
// Without typedef
struct Student {
char name[50];
int id;
};
struct Student s1; // Need 'struct' keyword
// With typedef
typedef struct {
char name[50];
int id;
} Student;
Student s1; // No 'struct' keyword needed
Named Structure with typedef
typedef struct Student {
char name[50];
int id;
struct Student *next; // Self-reference using struct name
} Student;
// Can use either:
struct Student s1;
Student s2;
Multiple typedefs
typedef struct Point {
int x, y;
} Point, *PointPtr;
Point p1; // Structure variable
PointPtr ptr = &p1; // Pointer to structure
typedef for Function Pointers in Structures
typedef void (*Callback)(int);
typedef struct {
int value;
Callback on_change;
} Observable;
Anonymous Structures
Basic Anonymous Structure
struct {
int x;
int y;
} point1, point2; // Only these two variables can exist
point1.x = 10;
point2.y = 20;
// Cannot create more variables of this type!
// struct ??? point3; // Error - no name to refer to
Anonymous Structure with typedef
typedef struct {
int x;
int y;
} Point;
Point p1, p2, p3; // Can create as many as needed
Anonymous Structures Inside Structures (C11)
struct Container {
int id;
struct {
int x;
int y;
}; // Anonymous member
};
struct Container c;
c.id = 1;
c.x = 10; // Direct access - no intermediate name
c.y = 20;
Structures vs Arrays
| Feature | Structure | Array |
|---|---|---|
| Data Types | Different types | Same type |
| Access | By member name | By index |
| Assignment | Direct copy allowed | Must copy element by element |
| Comparison | Must compare members | Must compare elements |
| Size | May include padding | Exact: n Ć element_size |
| Flexibility | Very flexible | Fixed structure |
| Use Case | Related different data | Collection of same data |
Example Comparison
// Array - homogeneous data
int scores[5] = {90, 85, 78, 92, 88};
for (int i = 0; i < 5; i++) {
printf("%d ", scores[i]);
}
// Structure - heterogeneous data
struct Result {
char subject[20];
int score;
char grade;
};
struct Result r = {"Math", 90, 'A'};
printf("%s: %d (%c)\n", r.subject, r.score, r.grade);
Common Use Cases
1. Representing Real-World Entities
struct Car {
char brand[30];
char model[30];
int year;
float price;
int mileage;
};
2. Grouping Related Configuration
struct Config {
int max_connections;
int timeout_seconds;
char log_file[256];
int debug_mode;
};
3. Function Return Multiple Values
struct DivResult {
int quotient;
int remainder;
};
struct DivResult divide(int a, int b) {
struct DivResult result;
result.quotient = a / b;
result.remainder = a % b;
return result;
}
4. Linked Data Structures
struct Node {
int data;
struct Node *next; // Self-referential
};
5. Date/Time Representation
struct DateTime {
int year, month, day;
int hour, minute, second;
};
6. Geometric Shapes
struct Rectangle {
struct Point top_left;
struct Point bottom_right;
};
struct Circle {
struct Point center;
float radius;
};
Best Practices
1. Use Meaningful Names
// Bad
struct S {
int a, b;
char c[50];
};
// Good
struct Employee {
int employee_id;
int department_id;
char full_name[50];
};
2. Use typedef for Convenience
typedef struct {
int x, y;
} Point;
// Cleaner function signatures
Point add_points(Point p1, Point p2);
3. Initialize All Members
// Good - explicit initialization
struct Config cfg = {
.max_connections = 100,
.timeout = 30,
.debug = 0
};
// Or zero-initialize
struct Config cfg = {0};
4. Order Members to Minimize Padding
// Order from largest to smallest
struct Optimized {
double big; // 8 bytes
int medium; // 4 bytes
short small; // 2 bytes
char tiny; // 1 byte
char tiny2; // 1 byte
}; // 16 bytes (minimal padding)
5. Document Structure Purpose
/**
* Represents a 2D point in Cartesian coordinates.
* Used for graphics operations and collision detection.
*/
struct Point {
int x; /**< X coordinate */
int y; /**< Y coordinate */
};
6. Use const for Read-Only Parameters
void print_student(const struct Student *s) {
printf("Name: %s\n", s->name);
// Cannot modify s members here
}
7. Create Initialization Functions
struct Student create_student(const char *name, int id, float gpa) {
struct Student s;
strncpy(s.name, name, sizeof(s.name) - 1);
s.name[sizeof(s.name) - 1] = '\0';
s.id = id;
s.gpa = gpa;
return s;
}
Summary
Key Points
- ā¢Definition: Structures group related variables of different types
- ā¢Declaration: Use
structkeyword with member definitions - ā¢Access: Use dot operator (.) for variables, arrow (->) for pointers
- ā¢Initialization: Use initializer lists or designated initializers
- ā¢Assignment: Direct assignment copies all members (shallow copy)
- ā¢Size: May be larger than sum of members due to padding
- ā¢typedef: Simplifies structure type names
Common Operations
| Operation | Syntax |
|---|---|
| Declare structure | struct Name { members }; |
| Create variable | struct Name var; |
| Access member | var.member |
| Initialize | {value1, value2, ...} |
| Assign | s2 = s1; |
| Get size | sizeof(struct Name) |
Memory Diagram
struct Student s1 = {"John", 101, 3.75};
Memory:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā "John\0"... ā 101 ā 3.75 ā padding ā
ā (50 bytes) ā(4 bytes)ā(4 bytes)ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā
s1.name s1.roll s1.gpa
When to Use Structures
- ā¢Grouping related data of different types
- ā¢Representing real-world entities
- ā¢Returning multiple values from functions
- ā¢Creating custom data types
- ā¢Building complex data structures
Next Topics
- ā¢Nested Structures
- ā¢Array of Structures
- ā¢Pointers to Structures
- ā¢Passing Structures to Functions
- ā¢Unions
- ā¢Bit Fields