Structures in C

Structures in C are user-defined data types that allow grouping of variables of different data types under a single name. This provides a way to handle complex data in a systematic and organized manner. Structures are essential when you want to manage related data collectively, such as storing information about a student including name, age, and marks in one entity.

Unlike arrays that store elements of the same type, structures can contain a mix of data types including integers, floats, characters, arrays, pointers, and even other structures. This flexibility makes structures one of the most powerful features of C for designing real-world programs.

1. Declaring and Defining Structures

The basic syntax of defining a structure is using the `struct` keyword followed by the structure name and member definitions enclosed in curly braces. The structure itself does not allocate memory; memory is allocated only when you create structure variables.

  • Syntax: struct StructureName { data_type member1; data_type member2; ... };
  • Example: struct Student { char name[50]; int age; float marks; };

Here, `Student` is a structure type, and it contains three members: a character array `name`, an integer `age`, and a float `marks`.

2. Creating Structure Variables

Once a structure is defined, you can create variables of that type. Memory for each member is allocated sequentially in memory.

  • Example: struct Student s1, s2; // Accessing members strcpy(s1.name, "Alice"); s1.age = 20; s1.marks = 85.5;

You can also initialize structure variables at the time of declaration:

  • struct Student s3 = {"Bob", 22, 90.0};

3. Accessing Structure Members

Structure members are accessed using the dot (`.`) operator for regular structure variables.

  • Example: printf("Name: %s\n", s3.name); printf("Age: %d\n", s3.age); printf("Marks: %.2f\n", s3.marks);

4. Pointers to Structures

Pointers can be used to access structures efficiently, especially when passing them to functions. A pointer to a structure stores the memory address of the structure variable. Members can then be accessed using the arrow (`->`) operator.

  • Example: struct Student s4 = {"Charlie", 21, 88.5}; struct Student *ptr = &s4; printf("Name: %s\n", ptr->name); printf("Marks: %.2f\n", ptr->marks);

Pointers to structures are very useful for dynamic memory allocation and handling arrays of structures.

5. Array of Structures

You can create arrays of structures to store multiple records of the same type. This is commonly used for lists of students, employees, or products.

  • Example: struct Student students[3]; // Initializing strcpy(students[0].name, "Alice"); students[0].age = 20; students[0].marks = 85.5; strcpy(students[1].name, "Bob"); students[1].age = 22; students[1].marks = 90.0; strcpy(students[2].name, "Charlie"); students[2].age = 21; students[2].marks = 88.0;

Accessing members of a structure array uses the index and dot operator:

  • printf("%s\n", students[1].name); // Output: Bob

6. Self-Referential Structures

A self-referential structure contains a pointer to the same structure type. It is the foundation for linked lists, trees, and other dynamic data structures.

  • Example: struct Node { int data; struct Node *next; };
  • Here, `Node` contains an integer and a pointer `next` that points to another `Node` structure.

7. Linked List Using Structures

Linked lists are created using self-referential structures. Each node contains data and a pointer to the next node. They allow dynamic insertion and deletion unlike arrays.

  • Example: struct Node *head = NULL; struct Node *newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = 10; newNode->next = head; head = newNode;

This example creates a new node and adds it at the beginning of the list. You can traverse using a pointer:

  • struct Node *temp = head; while(temp != NULL) { printf("%d ", temp->data); temp = temp->next; }

8. Structure Padding and Alignment

Compilers may add padding between members of a structure to align data in memory according to the machine architecture. This ensures faster access but increases memory usage.

  • Example: struct Example { char c; int x; }; // sizeof(Example) may be 8 bytes, not 5, due to padding.

You can use `#pragma pack` in some compilers to control padding, but it may reduce performance.

9. Union

A union is similar to a structure but shares memory for all members. Only one member can store a value at a time. It is used to save memory.

  • Example: union Data { int i; float f; char str[20]; }; union Data d; d.i = 10; // Only i is valid now d.f = 220.5; // Now f is valid, i is overwritten

10. Differences Between Structure and Union

  • Structures allocate separate memory for each member, whereas unions share memory among all members.
  • Structure size is the sum of its members plus padding, while union size is equal to its largest member.
  • Structures are used for storing multiple data simultaneously; unions are used to store one value at a time efficiently.

11. Typedef with Structures

You can use `typedef` to create an alias for a structure type, making code simpler and more readable.

  • Example: typedef struct Student { char name[50]; int age; float marks; } Student; Student s1; // No need to write 'struct Student'

12. Bit-Fields in Structures

Bit-fields allow you to allocate a specific number of bits for a structure member. Useful for memory optimization, especially in embedded systems.

  • Example: struct Flags { unsigned int isReady:1; unsigned int isError:1; unsigned int mode:3; }; // Total size may be smaller than 4 bytes.

13. Nested Structures

Structures can be nested inside other structures. This is useful to model complex data.

  • Example: struct Date { int day; int month; int year; }; struct Student { char name[50]; struct Date dob; }; Student s = {"Alice", {1,1,2000}};

14. Passing Structures to Functions

Structures can be passed to functions either by value or by reference (using pointers). Passing by reference is more memory-efficient for large structures.

  • Pass by value: void printStudent(struct Student s) { printf("%s", s.name); } Pass by reference: void printStudent(struct Student *s) { printf("%s", s->name); }

15. Practical Applications

  • Storing employee records with name, id, salary, etc.
  • Managing student databases with name, roll number, marks, and address.
  • Implementing dynamic data structures like linked lists, stacks, queues, and trees.
  • Embedded systems where memory optimization is crucial using bit-fields and unions.
  • Passing complex data to functions in modular programs.