Structures

Defining and using structures to group related data together. Accessing structure members using the dot operator.


Structures and Functions in C

Structures:

Definition:

A structure in C is a user-defined data type that allows you to group together variables of different data types under a single name. It's a way to create your own custom data type that represents a real-world entity or concept. Think of it as a container for related information.

Syntax:

 struct structure_name {
    data_type member1;
    data_type member2;
    // ... more members
}; 

Example:

 struct Point {
    int x;
    int y;
};

struct Student {
    char name[50];
    int roll_number;
    float marks;
}; 

Explanation:

  • struct keyword initiates the structure definition.
  • structure_name is the name you give to your new structure type (e.g., Point, Student).
  • Inside the curly braces {}, you define the members of the structure. Each member has a data_type and a member_name (e.g., int x;, char name[50];).
  • The semicolon ; after the closing curly brace is important; it terminates the structure definition.

Declaring Structure Variables:

Once you've defined a structure, you can declare variables of that structure type:

 struct Point p1; // Declares a variable 'p1' of type 'struct Point'
struct Student student1; // Declares a variable 'student1' of type 'struct Student' 

Accessing Structure Members:

You access individual members of a structure variable using the dot operator (.):

 p1.x = 10;  // Assigns the value 10 to the 'x' member of 'p1'
student1.roll_number = 123; // Assigns the value 123 to the 'roll_number' member of 'student1' 

Functions:

Definition:

Functions are self-contained blocks of code that perform a specific task. They are essential for organizing your C programs into reusable and manageable modules. Functions help to avoid code duplication and make your code more readable.

Syntax:

 return_type function_name(parameter_list) {
    // Function body (code to be executed)
    return return_value; // If return_type is not 'void'
} 

Explanation:

  • return_type: The data type of the value that the function returns (e.g., int, float, void, struct Point). If the function doesn't return a value, use void.
  • function_name: The name you give to your function.
  • parameter_list: A list of parameters that the function accepts as input. Each parameter has a data type and a name (e.g., int a, float b). If the function doesn't take any parameters, you can leave the parentheses empty: ().
  • function body: The code that is executed when the function is called.
  • return return_value;: This statement returns a value from the function back to the caller. The data type of return_value must match the return_type of the function. If the return type is `void`, no return statement is needed or can be `return;`

Example:

 int add(int a, int b) {
    return a + b;
}

void print_message() {
    printf("Hello, world!\n");
} 

Passing Structures as Arguments to Functions:

You can pass structures to functions just like you pass other data types (int, float, char, etc.). This allows functions to work with the data stored within structures.

Passing by Value:

When you pass a structure by value, a copy of the entire structure is created and passed to the function. Any changes made to the structure members within the function do not affect the original structure. This is safe but can be inefficient for large structures.

Example (Passing by Value):

 #include <stdio.h>

struct Point {
    int x;
    int y;
};

void print_point(struct Point p) {
    printf("Point: (%d, %d)\n", p.x, p.y);
    p.x = 100; // Modifying the copy inside the function
    p.y = 200;
    printf("Point inside function (modified): (%d, %d)\n", p.x, p.y);
}

int main() {
    struct Point my_point = {5, 7};
    printf("Original Point: (%d, %d)\n", my_point.x, my_point.y);
    print_point(my_point);
    printf("Original Point (after function call): (%d, %d)\n", my_point.x, my_point.y); // Remains unchanged
    return 0;
} 

Passing by Reference (using pointers):

When you pass a structure by reference (using a pointer to the structure), the function receives the memory address of the original structure. Any changes made to the structure members within the function do affect the original structure. This is more efficient, especially for large structures, but requires careful handling to avoid unintended modifications.

Example (Passing by Reference):

 #include <stdio.h>

struct Point {
    int x;
    int y;
};

void modify_point(struct Point *p) {  // p is a pointer to a struct Point
    p->x = 100;  // Accessing members using the arrow operator (->)
    p->y = 200;
    printf("Point inside function (modified): (%d, %d)\n", p->x, p->y);
}

int main() {
    struct Point my_point = {5, 7};
    printf("Original Point: (%d, %d)\n", my_point.x, my_point.y);
    modify_point(&my_point); // Pass the address of my_point
    printf("Original Point (after function call): (%d, %d)\n", my_point.x, my_point.y); // Modified
    return 0;
} 

Returning Structures from Functions:

Functions can also return structures as their return values. This allows you to create functions that compute and return complex data as a single unit.

Returning by Value:

When a function returns a structure by value, a copy of the structure is created and returned to the caller.

Example (Returning by Value):

 #include <stdio.h>

struct Point {
    int x;
    int y;
};

struct Point create_point(int x, int y) {
    struct Point p;
    p.x = x;
    p.y = y;
    return p;  // Returns a copy of the 'p' structure
}

int main() {
    struct Point new_point = create_point(20, 30);
    printf("New Point: (%d, %d)\n", new_point.x, new_point.y);
    return 0;
} 

Returning by Reference (using pointers):

While technically possible, returning a pointer to a locally created structure within a function is generally **strongly discouraged** because the memory allocated for that structure within the function's scope is deallocated when the function returns. The pointer returned will then point to invalid memory, leading to undefined behavior. If you need to return a structure using a pointer, the structure should be allocated dynamically (e.g., using `malloc`) or be a member of a larger, persistent data structure.

Example (Returning a pointer to dynamically allocated structure - USE WITH CAUTION):

 #include <stdio.h>
#include <stdlib.h> // Required for malloc

struct Point {
    int x;
    int y;
};

struct Point* create_point(int x, int y) {
    struct Point *p = (struct Point*)malloc(sizeof(struct Point)); // Allocate memory dynamically
    if (p == NULL) {
        fprintf(stderr, "Memory allocation failed!\n");
        return NULL;
    }
    p->x = x;
    p->y = y;
    return p; // Returns a pointer to the dynamically allocated structure
}

int main() {
    struct Point *new_point = create_point(20, 30);
    if (new_point != NULL) {
        printf("New Point: (%d, %d)\n", new_point->x, new_point->y);
        free(new_point); // Free the allocated memory when done
    }
    return 0;
} 

Important: When you dynamically allocate memory, you are responsible for freeing it using `free()` when you are finished with it to prevent memory leaks.