Lesson 05-C Language Pointers

Pointer Concepts

A pointer is a variable that stores the memory address of another variable, i.e., a location in memory. Each byte in a computer’s memory has a unique address, and a pointer holds this address, enabling indirect access and modification of data in memory.

Pointer Declaration and Usage

Declaring Pointers

A pointer declaration includes a type specifier and an asterisk (*). The type specifier indicates the data type the pointer points to, and the asterisk denotes that it is a pointer variable.

int *p; // Declares a pointer to an integer

Initializing Pointers

A pointer can be initialized to point to the address of an existing variable using the address-of operator (&).

int x = 10;
int *p = &x; // p now points to the address of x

Using Pointers

Once initialized, a pointer can access or modify the value of the variable it points to using the dereference operator (*).

int x = 10;
int *p = &x;
printf("Value of x: %d\n", *p); // Outputs the value of x
*p = 20; // Modifies the value of x

Pointer Arithmetic

Pointers support arithmetic operations like addition and subtraction, which adjust the memory address based on the size of the pointed-to data type.

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // p points to the first element of arr
printf("First element: %d\n", *p); // Outputs the first element
p++; // p now points to the second element
printf("Second element: %d\n", *p); // Outputs the second element

Pointers and Arrays

In C, an array name implicitly converts to a pointer to the first element of the array, allowing array names to be used like pointers.

int arr[5] = {1, 2, 3, 4, 5};
printf("First element: %d\n", *arr); // Outputs the first element

Pointers as Function Parameters

Pointers are often used as function parameters to allow the function to modify the values of variables passed to it directly.

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(&x, &y);
    printf("x: %d, y: %d\n", x, y); // Outputs swapped values
}

Dynamic Memory Allocation

Pointers are used with dynamic memory allocation functions like malloc(), calloc(), realloc(), and free() to manage memory.

int *p = (int *)malloc(sizeof(int)); // Allocates memory
*p = 10;
printf("Value: %d\n", *p);
free(p); // Frees memory

Pointer Pitfalls

  • Wild Pointers: Uninitialized pointers pointing to undefined memory locations.
  • Dangling Pointers: Pointers to memory that has been freed.
  • Array Out-of-Bounds: Accessing beyond an array’s boundaries.
  • Memory Leaks: Failing to free dynamically allocated memory.

Overview of C Pointers

Pointer Basics

A pointer is a variable that stores a memory address, allowing indirect access to data in memory.

#include <stdio.h>

int main() {
    int x = 10; // Declare an integer variable
    int *px = &x; // px is a pointer to an integer, initialized to x's address

    printf("Value of x: %d\n", *px); // Outputs x's value
    printf("Address of x: %p\n", (void*)px); // Outputs x's address

    *px = 20; // Modifies x's value via the pointer
    printf("New value of x: %d\n", x); // Outputs modified x value

    return 0;
}

Pointers and Arrays

An array name is a constant pointer to the first element of the array.

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr; // p points to the first element

    printf("First element: %d\n", *p); // Outputs the first element
    p++; // Moves pointer to the next element
    printf("Second element: %d\n", *p); // Outputs the second element

    return 0;
}

Pointers and Functions

Using pointers as function parameters allows functions to modify the caller’s variables.

#include <stdio.h>

void increment(int *p) {
    (*p)++;
}

int main() {
    int x = 10;
    increment(&x);
    printf("Value of x after increment: %d\n", x);

    return 0;
}

Pointers and Strings

Strings in C are typically character arrays, and pointers can manipulate them.

#include <stdio.h>

void reverseString(char *str) {
    int len = 0;
    while (str[len] != '\0') len++;
    for (int i = 0; i < len / 2; i++) {
        char temp = str[i];
        str[i] = str[len - i - 1];
        str[len - i - 1] = temp;
    }
}

int main() {
    char str[] = "Hello, World!";
    reverseString(str);
    printf("Reversed string: %s\n", str);

    return 0;
}

Pointers and Dynamic Memory

Pointers enable dynamic memory allocation and deallocation.

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = (int *)malloc(sizeof(int));
    *p = 10;
    printf("Value: %d\n", *p);
    free(p); // Free memory

    return 0;
}

Pointers and Structures

Pointers can point to structures, allowing access to structure members.

#include <stdio.h>
#include <string.h>

typedef struct {
    int age;
    char name[50];
} Person;

int main() {
    Person *p = malloc(sizeof(Person));
    p->age = 25;
    strcpy(p->name, "John Doe");

    printf("Age: %d, Name: %s\n", p->age, p->name);
    free(p);

    return 0;
}

Pointers and Function Pointers

Function pointers store the address of a function, allowing it to be called like a regular function.

#include <stdio.h>

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

int main() {
    int (*func)(int, int) = add; // func is a pointer to the add function
    int result = func(5, 3);
    printf("Result: %d\n", result);

    return 0;
}

Pointer Arrays and Array Pointers

A pointer array is an array of pointers, while an array pointer points to an entire array.

#include <stdio.h>

int main() {
    int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int (*p)[3] = arr; // p is a pointer to an array of 3 integers

    printf("Element at [1][2]: %d\n", p[1][2]); // Outputs arr[1][2]

    return 0;
}

Multilevel Pointers

Multilevel pointers are pointers to pointers, useful for complex data structures.

#include <stdio.h>

int main() {
    int x = 10;
    int *p = &x;
    int **q = &p;

    printf("Value of x: %d\n", *p);
    printf("Address of p: %p\n", (void*)q);

    return 0;
}

Pointer Arrays and Array Pointers

Pointer Arrays

A pointer array is an array where each element is a pointer of the same type, each potentially pointing to a different memory location.

#include <stdio.h>

int main() {
    int a = 10, b = 20, c = 30;
    int *ptr_array[3]; // Declare a pointer array

    ptr_array[0] = &a;
    ptr_array[1] = &b;
    ptr_array[2] = &c;

    // Access elements
    printf("Values: %d, %d, %d\n", *ptr_array[0], *ptr_array[1], *ptr_array[2]);

    return 0;
}

Array Pointers

An array pointer is a pointer to an entire array, storing the address of the array’s starting position.

#include <stdio.h>

int main() {
    int arr[3] = {10, 20, 30};
    int (*ptr_to_array)[3]; // Declare a pointer to an array of 3 integers

    ptr_to_array = &arr; // Initialize the array pointer

    // Access elements
    printf("Values: %d, %d, %d\n", (*ptr_to_array)[0], (*ptr_to_array)[1], (*ptr_to_array)[2]);

    return 0;
}

Parsing Declaration Syntax

In C, the precedence of brackets ([]) is higher than the asterisk (*):

  • int *ptr_array[3] is parsed as (int *)[3], an array of 3 pointers to integers.
  • int (*ptr_to_array)[3] is parsed as int (*[3]), a pointer to an array of 3 integers.

Summary

  • Pointer Array: An array where each element is a pointer.
  • Array Pointer: A pointer to an entire array.

String Pointers

Declaring and Initializing String Pointers

String pointers can point to static strings or dynamically allocated strings.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    // Static string
    char *str1 = "Hello, World!";
    printf("%s\n", str1);

    // Dynamic string
    char *str2 = malloc(strlen("Dynamic String") + 1);
    strcpy(str2, "Dynamic String");
    printf("%s\n", str2);
    free(str2);

    return 0;
}

String Pointers as Function Parameters

String pointers can be function parameters, allowing functions to modify or manipulate strings.

#include <stdio.h>
#include <string.h>

void reverseString(char *str) {
    int len = strlen(str);
    for (int i = 0; i < len / 2; i++) {
        char temp = str[i];
        str[i] = str[len - i - 1];
        str[len - i - 1] = temp;
    }
}

int main() {
    char str[] = "Hello, World!";
    reverseString(str);
    printf("Reversed string: %s\n", str);

    return 0;
}

String Pointers and String Arrays

A string array is an array where each element is a pointer to a string.

#include <stdio.h>

int main() {
    char *strArray[] = {"Apple", "Banana", "Cherry"};
    for (int i = 0; i < 3; i++) {
        printf("%s\n", strArray[i]);
    }

    return 0;
}

String Pointers and String Functions

C’s standard library provides functions like strlen(), strcmp(), strcpy(), and strcat(), which typically take string pointers as parameters.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    char str1[] = "Hello, ";
    char str2[] = "World!";
    char *result = malloc(strlen(str1) + strlen(str2) + 1);
    
    strcpy(result, str1);
    strcat(result, str2);
    printf("Concatenated string: %s\n", result);
    
    free(result);

    return 0;
}

String Pointers and Dynamic String Construction

String pointers enable dynamic string construction and modification.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    char *dynamicStr = malloc(100);
    strcpy(dynamicStr, "Hello, ");
    strcat(dynamicStr, "World!");

    printf("%s\n", dynamicStr);
    free(dynamicStr);

    return 0;
}

Pointer Arithmetic

In C, pointer variables support specific operations based on their nature as memory addresses. Below are the primary pointer arithmetic operations:

Pointer Arithmetic Operations

  • Addition: pointer + integer moves the pointer forward by integer elements.
  • Subtraction: pointer - integer moves the pointer backward by integer elements.
  • Pointer Subtraction: pointer1 - pointer2 calculates the number of elements between two pointers, valid only if they point to the same or overlapping array.
  • Note: Pointer arithmetic adjusts the step size based on the pointed-to data type. For example, for an int pointer, pointer + 1 increases the address by sizeof(int).

Comparison Operations

Pointers can be compared using operators like <, >, <=, >=, ==, and != to check equality or relative positions.

Pointer Assignment

  • A pointer can be assigned the value of another pointer of the same type.
  • A pointer can be assigned NULL, indicating it points to no valid memory.

Pointers and Arrays

An array name acts as a pointer to the first element, allowing pointer arithmetic to traverse the array.

Example Code:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr; // p points to arr[0]

    // Pointer arithmetic
    p += 2; // p points to the third element
    printf("Third element: %d\n", *p);

    // Pointer comparison
    int *end = arr + 5; // Points past the last element
    while (p < end) {
        printf("%d ", *p);
        p++;
    }
    printf("\n");

    // Pointer subtraction
    int diff = end - arr; // Number of elements
    printf("Number of elements: %d\n", diff);

    return 0;
}

Pointer Arithmetic Limitations

  • Pointers cannot be multiplied or divided.
  • Arithmetic is valid only within the same array or contiguous memory block.
  • Pointer subtraction is meaningful only for pointers to the same or overlapping array.

Pointer Parameters and Pointer Return Values

Pointers as Function Parameters

When a function needs to modify external variables or access arrays, pointers are used as parameters, allowing direct access and modification.

Example Code:

#include <stdio.h>

void modifyValue(int *valuePtr) {
    *valuePtr = 100; // Modify the pointed-to value
}

int main() {
    int value = 50;
    printf("Before modification: %d\n", value);
    modifyValue(&value);
    printf("After modification: %d\n", value);
    return 0;
}

Pointers as Function Return Values

Functions can return pointers to dynamically allocated memory, arrays, or structures, useful for returning large or dynamically created data.

Example Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *createGreeting(const char *name) {
    int len = strlen(name) + 12; // "Hello, " + name + "!" + null
    char *greeting = (char *)malloc(len * sizeof(char));
    if (greeting != NULL) {
        sprintf(greeting, "Hello, %s!", name);
    }
    return greeting;
}

int main() {
    char *greeting = createGreeting("World");
    if (greeting != NULL) {
        printf("%s\n", greeting);
        free(greeting); // Free dynamically allocated memory
    }
    return 0;
}

Notes

  • When returning a pointer to dynamically allocated memory, the caller must free it to avoid memory leaks.
  • Returning a pointer to a local variable is invalid, as the variable is destroyed after the function exits, creating a dangling pointer.
  • Ensure pointer validity and safety to avoid null pointer dereferences or out-of-bounds access.

NULL Pointers and Void Pointers

NULL Pointer

NULL is a predefined macro, typically defined in <stddef.h> or <stdio.h>, with a value of 0 or (void *)0. It represents a pointer that does not point to any valid memory address, often used to indicate an uninitialized or invalid pointer.

Example:

#include <stdio.h>

int main() {
    int *ptr = NULL; // ptr is a NULL pointer
    if (ptr == NULL) {
        printf("ptr is a NULL pointer.\n");
    }
    return 0;
}

Void Pointer

A void pointer (void *) is a generic pointer that can point to any data type. It can be assigned to any non-void pointer type, but dereferencing or assigning to it requires explicit type casting. Void pointers are used in functions accepting arbitrary pointer types or in dynamic memory allocation functions like malloc() and calloc().

Example:

#include <stdio.h>
#include <stdlib.h>

void printPointer(void *ptr) {
    int *intPtr = (int *)ptr; // Cast to int pointer
    printf("The integer value is %d\n", *intPtr);
}

int main() {
    int myInt = 10;
    void *voidPtr = &myInt; // Void pointer to an integer
    printPointer(voidPtr);
    return 0;
}

Differences Between NULL and Void Pointers

  • Type: NULL is a macro (usually 0), representing an empty pointer; a void pointer is an actual pointer type that can point to any data type.
  • Usage: NULL indicates a pointer does not point to valid memory; void pointers are used for generic pointer operations.
  • Assignment: NULL can be assigned to any pointer type; void pointers can be assigned to any non-void pointer type with casting.

Array-to-Pointer Conversion

Array Name as Pointer

An array name implicitly converts to a pointer to the first element, allowing pointer arithmetic to traverse the array.

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr; // ptr points to arr[0]

    for (int i = 0; i < 5; i++) {
        printf("%d ", *ptr);
        ptr++;
    }
    return 0;
}

Passing Arrays to Functions

When an array is passed to a function, it is passed as a pointer to the first element, allowing the function to modify the array’s contents.

#include <stdio.h>

void printArray(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", *(arr + i));
    }
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    printArray(arr, 5);
    return 0;
}

Array Size and Pointers

Array size information is not passed with the array name, so functions typically require an explicit size parameter.

#include <stdio.h>

void reverseArray(int *arr, int size) {
    int temp;
    for (int i = 0, j = size - 1; i < j; i++, j--) {
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    reverseArray(arr, 5);
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

Pointer Dereferencing with Arrays

The dereference operator (*) retrieves the value at a pointer’s address, and the address-of operator (&) gets an element’s address.

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr;
    printf("Value at ptr: %d\n", *ptr);
    printf("Address of arr[0]: %p\n", (void *)&arr[0]);
    return 0;
}

Double Pointers

Double Pointers

A double pointer is a pointer to a pointer, often used for dynamic two-dimensional arrays or linked list node pointers.

#include <stdio.h>
#include <stdlib.h>

int main() {
    int data = 10;
    int *ptr = &data;
    int **ptrToPtr = &ptr; // Double pointer

    // Modify data
    *ptr = 20;
    printf("Data: %d\n", data);

    // Modify via double pointer
    **ptrToPtr = 30; // Modify data via double pointer
    printf("Data: %d\n", data);

    return 0;
}

Triple Pointers

A triple pointer points to a double pointer, useful for complex memory hierarchies like dynamic three-dimensional arrays or intricate linked lists.

#include <stdio.h>
#include <stdlib.h>

int main() {
    int data = 10;
    int *ptr = &data;
    int **ptrToPtr = &ptr;
    int ***ptrToPtrToPtr = &ptrToPtr; // Triple pointer

    // Modify data via triple pointer
    ***ptrToPtrToPtr = 40;
    printf("Data: %d\n", data);

    return 0;
}

Applications of Multilevel Pointers

Multilevel pointers are useful in:

  • Dynamic Arrays: Creating dynamic two- or three-dimensional arrays with runtime-determined sizes.
  • Linked Lists: Managing nodes with pointers to data and other nodes, especially in complex structures like doubly linked lists.
  • Callback Functions: Passing pointers to functions that modify external variables.

Notes

  • Memory Management: Ensure proper allocation and deallocation for each dynamically allocated block.
  • Pointer Arithmetic: Avoid out-of-bounds or undefined behavior in multilevel pointer operations.
  • Code Readability: Multilevel pointers can complicate code; strive for clarity.

Two-Dimensional Array Pointers

Defining and Initializing Two-Dimensional Arrays

int array[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

Pointer Representation of Two-Dimensional Arrays

A two-dimensional array int array[3][4] is an array of three elements, each a pointer to an array of four integers. A pointer to this array can be defined as:

int (*ptr)[4]; // Pointer to an array of 4 integers
ptr = array; // ptr points to the first element

Accessing Elements

Elements can be accessed directly or via pointers:

printf("%d\n", array[0][0]); // Direct access
printf("%d\n", *(*(array + 0) + 0)); // Pointer access

Traversing Two-Dimensional Arrays

Use pointers to traverse the array:

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("%d ", *(*(array + i) + j));
    }
    printf("\n");
}

Advanced Usage

  • Pointer to Two-Dimensional Array: A pointer to the entire array:
int (*p)[3][4]; // Pointer to a 3x4 array
p = &array; // Points to the entire array
  • Dynamic Two-Dimensional Arrays: Allocate dynamically:
int **dynamicArray = malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++) {
    dynamicArray[i] = malloc(4 * sizeof(int));
}
  • Accessing via Double Pointers:
int val = dynamicArray[1][2]; // Access second row, third column

Freeing Dynamic Two-Dimensional Arrays

Ensure proper deallocation:

for (int i = 0; i < 3; i++) {
    free(dynamicArray[i]);
}
free(dynamicArray);

Function Pointers and Structure Pointers

Function Pointers

Function pointers point to functions, enabling functions to be passed as parameters or stored in data structures for callbacks or event handling.

#include <stdio.h>

// Define a function pointer type
typedef void (*Callback)(int);

// Callback function
void callbackFunction(int x) {
    printf("Callback function called with argument: %d\n", x);
}

// Function accepting a function pointer
void callFunction(Callback func, int arg) {
    func(arg);
}

int main() {
    Callback myCallback = callbackFunction;
    callFunction(myCallback, 10);
    return 0;
}

Structure Pointers

Structure pointers point to structure types, allowing access to members or passing structures to functions, useful for modifying members or handling large data.

#include <stdio.h>
#include <string.h>

// Define a structure
struct Person {
    char name[50];
    int age;
};

// Function accepting a structure pointer
void printPerson(struct Person *person) {
    printf("Name: %s, Age: %d\n", person->name, person->age);
}

int main() {
    struct Person john;
    strcpy(john.name, "John Doe");
    john.age = 30;

    printPerson(&john);
    return 0;
}

Combining Structure and Function Pointers

Structures can include function pointers, useful for plugin architectures or extensible interfaces.

#include <stdio.h>
#include <string.h>

// Define a structure with a function pointer
typedef struct {
    char name[50];
    void (*sayHello)(void);
} Greetable;

// Function for the pointer
void sayHello(void) {
    printf("Hello!\n");
}

int main() {
    Greetable greetObj;
    strcpy(greetObj.name, "Greet Object");
    greetObj.sayHello = sayHello;

    greetObj.sayHello();
    return 0;
}

Pointers to Pointers and Pointer Arrays

Pointers to Pointers

A pointer to a pointer points to another pointer’s address, useful for multilayered indirection.

#include <stdio.h>

int main() {
    int x = 10;
    int *p = &x;
    int **pp = &p;

    printf("Value of x: %d\n", x);
    printf("Address of x: %p\n", (void *)p);
    printf("Address of p: %p\n", (void *)pp);
    printf("Value of *p: %d\n", *p);
    printf("Value of **pp: %d\n", **pp);

    return 0;
}

Pointer Arrays

A pointer array is an array of pointers, where each element can point to different data types.

#include <stdio.h>

int main() {
    int a = 10, b = 20, c = 30;
    int *arr[3];

    arr[0] = &a;
    arr[1] = &b;
    arr[2] = &c;

    for (int i = 0; i < 3; i++) {
        printf("Element at index %d: %d\n", i, *arr[i]);
    }

    return 0;
}

In-Depth Understanding

Pointers to Pointers:

  • Can point to elements of a pointer array, enabling layered access.
  • Useful in complex data structures like linked lists or trees.

Pointer Arrays:

  • Each element is a pointer, potentially to different data types.
  • Used in scenarios like function callbacks or event handling.

Practical Application

This example demonstrates using pointers to pointers and pointer arrays for a dynamically allocated two-dimensional array.

#include <stdio.h>
#include <stdlib.h>

int main() {
    int rows = 3, cols = 4;
    int **matrix = (int **)malloc(rows * sizeof(int *));

    for (int i = 0; i < rows; i++) {
        matrix[i] = (int *)malloc(cols * sizeof(int));
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i * cols + j;
        }
    }

    // Print matrix
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    // Free memory
    for (int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);

    return 0;
}

Pointers to Arrays and Multidimensional Arrays

Pointers to Arrays

A pointer to an array points to the array’s starting address, useful for traversing or passing arrays to functions.

#include <stdio.h>

void print_array(int *array, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int *ptr = numbers;

    print_array(numbers, 5);

    for (int i = 0; i < 5; i++) {
        printf("%d ", *(ptr + i));
    }
    printf("\n");

    return 0;
}

Multidimensional Arrays

Multidimensional arrays are arrays of arrays, often used for tables or matrices, stored contiguously in memory.

#include <stdio.h>

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    return 0;
}

In-Depth Understanding

Pointers to Arrays:

  • Enable traversal of array elements.
  • Array names are pointers to the first element.
  • Pointer arithmetic facilitates array navigation.

Multidimensional Arrays:

  • Stored contiguously in memory.
  • Two-dimensional arrays are arrays of arrays.
  • Accessible via pointers.

Practical Application

This example shows using a pointer to an array to handle a multidimensional array.

#include <stdio.h>

void print_matrix(int (*matrix)[4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    int (*ptr)[4] = matrix;

    print_matrix(ptr, 3);

    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", (*ptr + i)[j]);
        }
        printf("\n");
    }

    return 0;
}
Share your love