C program to find the largest and smallest number in an array using functions

Category: C, Programming | July 15, 2023

Introduction

When working with arrays in C, it often becomes necessary to find the largest and smallest numbers within the array. This task can be efficiently accomplished by utilizing functions to modularize the code and improve code organization. In this article, we will explore a detailed C program that employs functions to find the largest and smallest numbers in an array.

Approach and Design

To solve the problem of finding the largest and smallest numbers in an array, we will adopt a structured approach that involves breaking down the task into smaller functions. This approach offers several advantages, such as improved code organization, code reusability, and better readability.

Breaking into Smaller Problems

int findLargestNumber(...){}
int findSmallestNumber(...){}
int main(){}

By dividing the problem into smaller functions, we can enhance the clarity and maintainability of our code. Each function will be responsible for a specific subtask, making the overall logic easier to comprehend. Moreover, if there are changes or updates needed in the future, it will be easier to modify or enhance a specific function without affecting the entire program.

Designing the Program

In our program, we will design the code to have small functions with well-defined inputs and expected outputs. These functions will be responsible for finding the largest and smallest numbers in the array. Additionally, we will have a main function that orchestrates the solution by calling these auxiliary functions and displaying the results.

Function Implementation

Now let’s delve into the implementation of our program, focusing on the functions responsible for finding the largest and smallest numbers in the array. For each function, we will provide a detailed description of its inputs, output, and algorithm, followed by the corresponding code snippet.

Function to Find the Largest Number

This function takes the array’s pointer and the array’s size as inputs and returns the largest number found within the array.

Inputs:

  • array: A pointer to the array of integers.
  • size: The number of elements in the array.

Output:

The largest number is found in the array.

Algorithm:

  1. Initialize a variable largest with the first element of the array.
  2. Iterate through the array from the second element.
  3. Compare each element with the current largest value.
  4. If an element is greater than, update largest with that element.
  5. Continue iterating until all elements have been checked.
  6. Return the final value of largest.

Here’s the corresponding code snippet:

int findLargestNumber(int *array, int size){
    int largest = array[0];  //Assume the first element is the largest

    for (int i = 1; i < size; i++) {
        if (array[i] > largest) {
            largest = array[i];
        }
    }
    return largest;
}

Function to Find the Smallest Number

Similarly, this function takes the array’s pointer and the array’s size as inputs and returns the smallest number found within the array.

Inputs:

  • array: A pointer to the array of integers.
  • size: The number of elements in the array.

Output:

The smallest number is found in the array.

Algorithm:

  1. Initialize a variable smallest with the first element of the array.
  2. Iterate through the array from the second element.
  3. Compare each element with the current smallest value.
  4. If an element is smaller than, update smallest with that element.
  5. Continue iterating until all elements have been checked.
  6. Return the final value of smallest.

Here’s the corresponding code snippet:

int findSmallestNumber(int *array, int size){
    int smallest = array[0];  //Assume the first element is the smallest

    for (int i = 1; i < size; i++) {
        if (array[i] < smallest) {
            smallest = array[i];
        }
    }
    return smallest;
}

Main Function

The main function serves as the entry point of our program and is responsible for initializing the array, calling the functions to find the largest and smallest numbers, and displaying the results.

Test and result

Here’s an example of the main function in code:

Main_func_code

And below are the results:

Main_func_code

How the main code in your program looks like is entirely controlled by you. A real-life example of our program is to measure student grades in a class. Applying the two functions we created earlier, we can build one of the main functions of the program which is to find the highest and the lowest grade.

int main(){
//What is your program's application? It is entirely up to you
    int grades[] = {5, 2, 7, 4, 3, 8};
    int size = sizeof(grades) / sizeof(grades[0]);
    
    int largest = findLargestNumber(grades, size);
    int smallest = findSmallestNumber(grades, size);
    
    printf("The highest grade is: %d\n",largest);
    printf("The lowest grade is: %d\n", smallest);
    return 0;
}

Improve Our Program

Of course, we are not stopping here. In this section, you can learn some tricks to make our program even more robust and enhance its functionality.

Interacting with user

To make the program more interactive, we can prompt the user to enter the size of the array and the array elements dynamically. This way, the program can handle arrays of varying sizes, providing a more practical and user-friendly experience.

int main(){
    int size;

    printf("Enter the size of the array: ");
    scanf("%d", &size);

    int *array = (int *)malloc(size * sizeof(int)); // remember to include <stdlib.h>

    printf("Enter the elements of the array:\n");
    for (int i = 0; i < size; i++) {
        printf("Element %d: ", i + 1);
        scanf("%d", &array[i]);
    }
    //Rest of the code...
}

In the code above, we have dynamically allocated memory to the array, so don’t forget to free it after use: free(array);

Error and Edge Cases Handling

Validate User Inputs

To ensure that user inputs are within acceptable ranges, we can implement validation checks. Let’s modify our main function to validate the size of the array entered by the user:

int main() {
    int size;

    do {
        printf("Enter the size of the array (must be greater than 0): ");
        scanf("%d", &size);
    } while (size <= 0);
    // Rest of the code...
}

In this code snippet, we use a do-while loop to repeatedly prompt the user for the size of the array until a valid input (greater than 0) is provided. This prevents the program from proceeding with an invalid array size.

While at that, we can add advanced entry validation into this program. For example, we can check whether the user enters a number and or a character. Here we also use a while loop to ask the user for an element until a valid entry is made.

int main(){
    //Have already initiated array and its size...
    printf("Enter the elements of the array:\n");
    for (int i = 0; i < size; i++) {
        int validEntry = 0;
        while (!validEntry) {
            printf("Element %d: ", i + 1);
            if (scanf("%d", &array[i]) == 1) {
                validEntry = 1;
            } else {
                printf("Invalid input. Please enter a valid number.\n");
                while (getchar() != '\n') {
                    continue;  // Clear the input buffer
                }
            }
        }
    }
    //Rest of the code...
}

The getchar() function is used to clear the input buffer to prevent any remaining invalid input from affecting subsequent iterations. This approach ensures that each element is entered correctly before proceeding, allowing the user to re-enter the element if necessary.

Handling Empty or Single-Element Arrays

To handle cases where the array is empty or contains only one element, we can add checks before finding the largest and smallest numbers.

int main() {
    // ...
    if (size == 0) {
        printf("Array is empty.\n");
        return 1;
    }

    if (size == 1) {
        printf("Array contains only one element: %d\n", array[0]);
        return 1;
    }
    // Rest of the code...
}

In the code snippet above, we check if the size of the array is 0 or 1. If it is 0, we display a message indicating that the array is empty. If it is 1, we simply display the single element present in the array and exit the program.

Error-Checking Mechanisms

Implementing error-checking mechanisms can help handle invalid inputs or out-of-bounds array access. Here’s an example of error-checking when accessing array elements:

int findLargestNumber(int *array, int size) {
    if (array == NULL || size <= 0) {
        printf("Invalid array or size.\n");
        return INT_MAX;  // remember to add #include 
    }

    int largest = array[0];

    for (int i = 1; i < size; i++) {
        if (array[i] > largest) {
            largest = array[i];
        }
    }
    return largest;
}

In this code snippet, we first check if the array pointer is NULL or if the size is less than or equal to 0. If either of these conditions is true, we display an error message and return a sentinel value (INT_MIN) to indicate an error. This allows the calling function to handle the error appropriately.

Reminder

There are many ways to handle errors and edge cases, but it’s not necessary to add all of that to your program. Remember that while error checking and handling edge cases are important, overprotective programming can lead to unnecessarily complex and lengthy code. Strive for a balance between robustness and code simplicity, focusing on critical errors and maintaining a clean, efficient code structure. Here’s an example of how you should implement your program:

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

int findLargestNumber(int *array, int size) {
    int largest = array[0];  

    for (int i = 1; i < size; i++) {
        if (array[i] > largest) {
            largest = array[i];
        }
    }
    return largest;
}

int findSmallestNumber(int *array, int size) {
    int smallest = array[0];  

    for (int i = 1; i < size; i++) {
        if (array[i] > smallest) {
            smallest = array[i];
        }
    }
    return smallest;
}

int main(){
    int size;
    do {
        printf("Enter the size of the array (must be greater than 0): ");
        if(scanf("%d", &size) !=1){
            printf("Invalid input.\n");
            while (getchar() != '\n') {
                continue;  
            }
        }
    } while (size <= 0);

    int *array = (int *)malloc(size * sizeof(int)); 

    printf("Enter the elements of the array:\n");
    for (int i = 0; i < size; i++) {
        int validEntry = 0;
        while (!validEntry) {
            printf("Element %d: ", i + 1);
            if (scanf("%d", &array[i]) == 1) {
                validEntry = 1;
            } else {
                printf("Invalid input. Please enter a valid number.\n");
                while (getchar() != '\n') {
                    continue;  
                }
            }
        }
    }

    int largest = findLargestNumber(array, size);
    int smallest = findSmallestNumber(array, size);

    printf("The largest number is: %d\n",largest);
    printf("The smallest number is: %d\n", smallest);
}

Another approach

If you’ve been coding for a while, you’ve probably heard of the concept of recursion. Simply put, it is a technique in which a module or function calls itself either directly or indirectly. That suggests a different approach to this problem.

We only need to create a function that can be used to find the largest and the smallest number in an array at the same time. Because they work on the same principle, so by analyzing one case, you will be able to see how it works in the other.

Finding the largest number

I will choose to analyze the case of finding the largest number:

int findLargest(int array[], int start, int end){
//"start": first element
//"end": last element
...
}

Let’s fill in the algorithm. We use the divide-and-conquer strategy for solving this problem. First, we split the array into 2 parts, the left part and the right part. We can do that by choosing a pivot mid that is in the middle of the array.

int mid = (start + end) / 2;

The left part will include the elements from the first element to the mid element, and the right part will contain the remaining elements. This is the divide step.

Next, we’ll call this function again for both parts, to find the largest numbers in each of them. This is the recursion we are discussing about.

int leftMax = findLargest(array, start, mid);
int rightMax = findLargest(array, mid + 1, end);

As a final step, we simply compare these two numbers to find the largest of them. This is the conquer step.

max = (leftMax > rightMax) ? leftMax : rightMax;

But first, we need to work around the default case: if the array has only 1 element.

if (start == end){
    return array[start];
}

Naturally, that single element will be both the smallest and the largest number in the array.

Find the smallest number

We can do the same for the case of finding the smallest number.

int findSmallest(int array[], int start, int end){
    // Base case: if there is only one element in the array
    if (start == end) {
        return array[start];
    }
    else {
        int mid = (start + end) / 2;
        int leftMin = findSmallest(array, start, mid); // Recursive call for the left half
        int rightMin = findSmallest(array, mid + 1, end); // Recursive call for the right half
        return (leftMin < rightMin) ? leftMin : rightMin; // Return the smaller of the two
    }
}

Combine

Lastly, we can combine these two cases into a single function

#include <stdio.h>
// Recursive function to find the largest and smallest numbers in an array
void findMinMax(int array[], int start, int end, int* min, int* max) {
    // Base case: if there is only one element in the array
    if (start == end) {
        *min = *max = array[start];
        return;
    }

    // Recursive calls for the left and right halves
    int mid = (start + end) / 2;
    int leftMin, leftMax, rightMin, rightMax;
    findMinMax(array, start, mid, &leftMin, &leftMax);
    findMinMax(array, mid + 1, end, &rightMin, &rightMax);

    // Compare and update the minimum and maximum values
    *min = (leftMin < rightMin) ? leftMin : rightMin;
    *max = (leftMax > rightMax) ? leftMax : rightMax;
}

int main() {
    int array[] = {5, 2, 7, 4, 3, 8};
    int size = sizeof(array) / sizeof(array[0]);

    int min, max;
    findMinMax(array, 0, size - 1, &min, &max);

    printf("Largest number: %d\n", max);
    printf("Smallest number: %d\n", min);

    return 0;
}

Basically, this function uses a divide-and-conquer approach to recursively split the array into smaller halves until reaching the base case of a single element. It then compares and returns the larger or smaller value between the results obtained from the left and right halves.

Modularity and Reusability

To further enhance the modularity and reusability of our program, we can consider the following steps:

  1. Separate the function declarations into a header file (functions.h) and the function definitions into a source file (functions.c).
  2. Include the header file in both the main program and any other programs that may require the same functionality.
  3. Create a makefile to compile and link the source files, facilitating code management and reuse.
Makefile

Overall, these steps promote modularity by encapsulating functionality into separate files, providing a clear interface for accessing functions, and facilitating code reuse through the inclusion of header files and automated build processes.

Conclusion

In conclusion, utilizing functions in our program to find the largest and smallest numbers in an array brings significant advantages in terms of modularity and code reusability. By breaking down the problem into smaller, self-contained functions, we improve the organization and maintainability of our code. This modular approach allows us to isolate specific functionality, making it easier to understand and modify individual parts of the program without affecting the entire codebase. Additionally, functions enable us to reuse the same code in other programs or projects that require similar functionality, saving time and effort in development.