Week 4: Arrays (1)

Arrays in C are a collection of variables of the same type, stored in contiguous memory locations. They allow for efficient storage and access to a series of data elements.

Here's a brief introduction to arrays in C, how to define them, and an example:

Defining an Array in C

To define an array in C, you specify the type of the elements and the number of elements required by an array as follows:

type arrayName[arraySize];
  • type: The data type of the elements in the array (e.g., int, float, char).

  • arrayName: The name of the array.

  • arraySize: The number of elements the array will hold. This must be a constant value.

Example of Defining and Using an Array in C

#include <stdio.h>

int main() {
    // Define an array of integers
    int numbers[5];

    // Initialize array elements
    numbers[0] = 10;
    numbers[1] = 20;
    numbers[2] = 30;
    numbers[3] = 40;
    numbers[4] = 50;

    // Access and print elements of the array
    for(int i = 0; i < 5; i++) {
        printf("Value of numbers[%d] = %d\n", i, numbers[i]);
    }

    return 0;
}

sizeof and size_t

sizeof is a unary operator in C and C++ that returns the size in bytes of its operand, which can be a data type, a variable, an array, or a structure. The size is determined based on the architecture and compiler implementation, as the storage size of different data types can vary from one system to another.

Using size_t in the array example is a good practice, especially when it comes to indexing or defining the size of an array, as size_t is an unsigned integer type and is the result of the sizeof operator. This type ensures that you don't encounter negative indices or sizes.

#include <stdio.h>

int main() {
    // Define an array of integers
    int numbers[5];

    // Use size_t for the loop counter to ensure it can represent the size of any object
    printf("%zu ; %zu\n",sizeof(numbers), sizeof(numbers[0]));
    size_t arraySize = sizeof(numbers) / sizeof(numbers[0]);

    // Initialize array elements
    for(size_t i = 0; i < arraySize; i++) {
        numbers[i] = (i + 1) * 10; // Assign values: 10, 20, 30, 40, 50
    }

    // Access and print elements of the array using size_t in the loop
    for(size_t i = 0; i < arraySize; i++) {
        printf("Value of numbers[%zu] = %d\n", i, numbers[i]);
    }

    return 0;
}

size_t is used for the loop counter and for storing the size of the array. The %zu format specifier is used in the printf function to print a size_t value. Using size_t for array indexing and loop counters is a good habit because it's guaranteed to be large enough to represent the size of the largest possible object on the target platform, making your code more portable and safe from integer overflows or underflows.

Characteristics of sizeof:

  1. Compile-time Operation: sizeof is computed at compile time, not run time, so it does not impose a performance penalty during execution.

  2. Operand Types: The operand can be a data type (like int, float), an expression, or a variable. When the operand is a data type, parentheses are required (sizeof(int)). When it's an expression or a variable, parentheses are optional but often used for clarity (sizeof x or sizeof(x)).

  3. Return Type: The result of sizeof is of type size_t, an unsigned integer type.

Initializing an array

In C, there are several ways to initialize an array. Below is a single C program that demonstrates different methods of array initialization:

#include <stdio.h>

int main() {
    // Method 1: Initialize elements individually
    int array1[5];
    array1[0] = 10;
    array1[1] = 20;
    array1[2] = 30;
    array1[3] = 40;
    array1[4] = 50;

    // Method 2: Initialize all elements at once
    int array2[5] = {10, 20, 30, 40, 50};

    // Method 3: Partially initialize the array - the rest will be set to zero
    int array3[5] = {10, 20};

    // Method 4: Implicitly determine the size of the array
    int array4[] = {10, 20, 30, 40, 50};

    // Method 5: Initialize all elements to zero
    int array5[5] = {0};

    // Method 6: Use a loop to initialize the array
    int array6[5];
    for (int i = 0; i < 5; i++) {
        array6[i] = (i + 1) * 10;
    }

    // Printing array values to verify initialization
    for(int i = 0; i < 5; i++) {
        printf("array1[%d] = %d, array2[%d] = %d, array3[%d] = %d, array4[%d] = %d, array5[%d] = %d, array6[%d] = %d\n",
               i, array1[i], i, array2[i], i, array3[i], i, array4[i], i, array5[i], i, array6[i]);
    }

    return 0;
}

More examples of using arrays

Summarize the Survey of 6 Choices from 20 Samples

Assuming you have a survey with 6 choices (labeled as 1 to 6), and you've collected 20 responses, you want to summarize the results.

#include <stdio.h>

int main() {
    int surveyData[20] = {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2}; // Sample survey data
    int summary[6] = {0}; // Array to hold the count of each choice

    // Summarizing the survey
    for (int i = 0; i < 20; i++) {
        int choice = surveyData[i];
        if (choice >= 1 && choice <= 6) {
            summary[choice - 1]++;
        }
    }

    // Printing the summary
    printf("Survey Summary:\n");
    for (int i = 0; i < 6; i++) {
        printf("Choice %d: %d responses\n", i + 1, summary[i]);
    }

    return 0;
}

Rolling 10000 Dice and Summary the Result

This example simulates rolling a six-sided dice 10,000 times and then summarizes the results.

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

int main() {
    int rolls = 10000; // Number of dice rolls
    int results[6] = {0}; // Array to hold the count of each outcome (1-6)

    // Seed the random number generator
    srand(time(NULL));

    // Rolling the dice 10000 times
    for (int i = 0; i < rolls; i++) {
        int roll = rand() % 6 + 1; // Generate a random number between 1 and 6
        results[roll - 1]++; // Increment the count for the rolled number
    }

    // Printing the results
    printf("Dice roll results after %d rolls:\n", rolls);
    for (int i = 0; i < 6; i++) {
        printf("Number %d: %d times\n", i + 1, results[i]);
    }

    return 0;
}

The above example uses rand() to simulate dice rolls, and time(NULL) to seed the random number generator to ensure the rolls are different each time the program is run.

Using character arrays to store and manipulate strings

In C, strings are essentially arrays of characters, terminated by a null character \0. This null character is crucial as it indicates the end of the string, allowing functions to determine where a string stops in memory. Here's how arrays are used to store and manipulate strings:

Storing Strings in Arrays

  1. Initialization at Declaration:

    char str[] = "Hello"; // Automatically adds '\0' at the end

    str is an array of 6 characters ('H', 'e', 'l', 'l', 'o', '\0').

  2. Explicitly Defining Array Size:

    char str[6] = "Hello"; // Size includes the null character

    Here, the size of str is specified explicitly, including space for the null character.

  3. Assigning Characters Individually:

    char str[6];
    str[0] = 'H';
    str[1] = 'e';
    str[2] = 'l';
    str[3] = 'l';
    str[4] = 'o';
    str[5] = '\0'; // Don't forget the null terminator!

Manipulating String Arrays

  1. Accessing Characters: Characters in a string can be accessed and modified using their indices.

    char str[] = "Hello";
    char letter = str[1]; // 'e'
    str[0] = 'M'; // str is now "Mello"
  2. String Functions: The C Standard Library provides various functions to manipulate strings, such as strcpy (copy), strcat (concatenate), strlen (length), strcmp (compare), and more. Remember, these functions expect null-terminated strings.

    char str[20];
    strcpy(str, "Hello"); // Copy "Hello" into str
    strcat(str, " World"); // Concatenate " World" to str
    int len = strlen(str); // Get the length of str
  3. Reading and Writing Strings: You can use scanf, gets (not safe due to potential buffer overflow), fgets for reading strings from the user, and printf for writing strings to the output.

    char str[50];
    printf("Enter a string: ");
    fgets(str, 50, stdin); // Safer than gets, limit input to 49 characters (+ null)
    printf("You entered: %s", str);

Important Notes:

  • Always ensure there's enough space in the array to include the null terminator (\0). Not having it can lead to undefined behavior as functions won't know where the string ends.

  • Be cautious with buffer overflows, particularly when using functions like strcpy and strcat. Functions like strncpy and strncat are safer as they take the size of the destination buffer as an argument.

  • When using scanf to read strings, be aware that it stops reading at the first whitespace. fgets is often a safer alternative for reading lines of text.

How about using scanf to read input string?

Using scanf with a limit on the number of characters read is crucial to prevent buffer overflow, a common issue in C programming. Here's an example of reading a string from the user with scanf, limiting the input to a certain number of characters:

#include <stdio.h>

int main() {
    char str[50]; // Define a character array (string) of size 50

    printf("Enter a string (up to 49 characters): ");

    // Read a string from the user, limit to 49 characters to leave space for null terminator
    // %49s tells scanf to read at most 49 characters
    scanf("%49s", str);

    // Print the string read from the user
    printf("You entered: %s\n", str);

    return 0;
}

In this example:

  • %49s in scanf ensures that no more than 49 characters are read, preventing a buffer overflow. This reserves one space for the automatic null terminator \0 that scanf appends at the end of the input string.

  • str is the buffer where the input string is stored. It's declared with a size of 50 characters, so it can safely store 49 characters plus the null terminator.

  • scanf stops reading input at the first whitespace character. If you need to read a full line including spaces, consider using fgets(str, sizeof(str), stdin) instead.

It's important to always be cautious about the size of the buffers when dealing with strings in C, and scanf with a width specifier is a handy tool for that. However, remember that scanf might leave the newline character in the input buffer, which can affect subsequent reads. You might need to clear the input buffer using something like while ((getchar()) != '\n'); after reading the string if you're going to read more input from the user.

If time allows, we will revisit the potential security issue raised by inputting into a character array and discuss the C standard’s scanf_s function.

Outputting a Character Array That Represents a String

In C, a character array that represents a string is a sequence of characters terminated by a null character ('\0'). This null character signifies the end of the string, which is crucial for various string-handling functions to work properly. Outputting such a string is commonly done using the printf function, which is part of the C standard I/O library.

When outputting a character array with printf, the %s format specifier is used to indicate that you are printing a string. The function automatically reads characters from the provided character array and outputs them until it encounters the null terminator.

Example

Here's a simple example demonstrating how to output a character array that represents a string:

#include <stdio.h>

int main() {
    // Define and initialize a character array with a string
    char str[] = "Hello, World!";

    // Outputting the string using printf and the %s format specifier
    printf("The string is: %s\n", str);

    return 0;
}

In this example:

  • str is a character array initialized with the string "Hello, World!". The compiler automatically adds a null character '\0' at the end of the array.

  • The printf function is used to output the string. %s in the printf function tells the compiler that the corresponding argument is a string (a null-terminated character array).

  • The characters in the array str are printed sequentially by printf until the null terminator is encountered.

Remember, when dealing with strings in C, it's important to ensure that character arrays are properly null-terminated, as many functions rely on the null terminator to determine the end of the string.

Passing array to function

Passing an array to a function in C involves sending the array's base address to the function, essentially passing a pointer to the first element of the array. Arrays are always passed by reference, not by value. This means that any modifications made to the array elements in the function will affect the original array.

Few key points to understand:

  1. Passing the Array:

    • When passing an array to a function, you just need to specify the array's name without square brackets.

    • The function that receives the array should be declared with parameters that indicate it expects an array (or a pointer, since arrays decay to pointers when passed to functions).

  2. Function Declaration:

    • The function that takes an array as an argument can declare the parameter in various ways, such as type name[] or type *name. Both forms allow array access within the function.

    • It's often useful to pass an additional parameter specifying the size of the array, especially since the function receiving the array doesn't inherently know the array's length.

  3. Accessing Array Elements:

    • Inside the function, array elements can be accessed just like in the main program, using square bracket notation.

  4. No Size Information:

    • Arrays in C don't carry size information. Hence, when passing arrays to functions, it's a common practice to pass the size of the array as an additional parameter.

Example

#include <stdio.h>

// Function to print the entire array
void printArray(int arr[], int size, int factor) {
    printf("  + Entire array: ");
    for (int i = 0; i < size; i++) {
        arr[i]*=factor;
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// Function to modify a specific element of the array
void modifyElement(int arrElement) {
    arrElement = arrElement * 2; // Attempt to modify the element
    printf("   + Inside modifyElement, element doubled: %d\n", arrElement);
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    
    // Passing the entire array to the function
    printArray(numbers, size, 1);
    // Passing a single element of the array to the function
    printf("(1) Element before modifyElement: %d\n", numbers[2]);
    
    // Same as above but modify original array
    printArray(numbers, size, 2);
    printf("(2) Element before modifyElement: %d\n", numbers[2]);
    
    modifyElement(numbers[2]);
    printf("(3) Element after modifyElement: %d\n", numbers[2]);

    return 0;
}

Sorting

Bubble Sort is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted. Although it's not suitable for large data sets due to its poor performance (O(n²) complexity), it's intuitive and easy to implement.

Here's how you can implement Bubble Sort in C:

#include <stdio.h>

// Function to print the array
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// Function to perform Bubble Sort
void bubbleSort(int arr[], int size) {
    int temp;
    for (int i = 0; i < size - 1; i++) { // Number of passes
        for (int j = 0; j < size - i - 1; j++) { // Last i elements are already in place
            if (arr[j] > arr[j + 1]) {
                // Swap arr[j] and arr[j + 1]
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
            printf("i = %d: ",i);
            printArray(arr, size);
        }
    }
}

int main() {
    int numbers[] = {64, 34, 25, 12, 22, 11, 90};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    printf("Unsorted array:\n");
    printArray(numbers, size);

    bubbleSort(numbers, size);

    printf("Sorted array:\n");
    printArray(numbers, size);

    return 0;
}

Classwork - Week 4

Frequency Counter using Arrays

Objective: Write a C program to count the frequency of each element in an array.

Description: In this exercise, students will create a program that takes an array of integers as input and then calculates and prints the frequency of each unique element present in the array. The program should work for arrays of any size. The students should ensure that each element's frequency is counted only once.

Instructions:

  1. Input:

    • Hardcode the array for simplicity, e.g., int arr[] = {2, 1, 2, 4, 3, 5, 4, 6, 4, 7};

  2. Output:

    • Display the elements of the arr array and their corresponding frequencies.

    • Ensure that each element is displayed only once along with its frequency.

Sample Output Structure:

Element | Frequency
-------------------
      2 |         2
      1 |         1
      4 |         3
      3 |         1
      5 |         1
      6 |         1
      7 |         1

Homework - Week 4

Extend the classwork exercise by

  • Your program asks the user to input the size and elements of the array.

  • Sorting the element output.

  • Implement input validation to check if the user inputs are integers.

  • Extend the program to handle negative numbers and zeros correctly.

Last updated