Arrays in C
Arrays are fundamental data structures in programming that allow you to store multiple values of the same type under a single variable name. This unit will explore the concept of arrays in C, including their declaration, initialization, manipulation, and applications. Understanding arrays is crucial for efficient data management and manipulation in your programs.
Introduction to Arrays
Definition
- Array: An array is a collection of elements of the same data type stored in contiguous memory locations, which can be individually referenced by adding an index to a unique identifier.
Purpose
- Efficient Data Management: Arrays allow you to store and manage large amounts of data efficiently.
- Indexed Access: Elements can be accessed directly using their index, providing fast retrieval and modification.
- Homogeneous Data Storage: Ideal for storing collections of data items that are logically related and of the same type.
Characteristics
- Homogeneous Elements: All elements in an array must be of the same data type (e.g., all integers, all floats).
- Fixed Size: The size of an array must be specified at the time of declaration and cannot be altered during program execution.
- Contiguous Memory Allocation: Elements are stored in consecutive memory locations, facilitating efficient access and manipulation.
Declaring and Initializing Arrays
Declaring Arrays
Syntax
data_type array_name[array_size];
- data_type: The type of data the array will hold (e.g.,
int,float,char). - array_name: The identifier for the array.
- array_size: The number of elements the array will hold; must be a positive integer.
Examples
One-Dimensional Array
int numbers[10]; // Declares an array named 'numbers' that can hold 10 integers
Two-Dimensional Array
float matrix[3][4]; // Declares a 2D array (matrix) with 3 rows and 4 columns
General Syntax for 2D Arrays:
data_type array_name[rows][columns];
Initializing Arrays
Arrays can be initialized at the time of declaration.
One-Dimensional Arrays
Full Initialization
int numbers[5] = {1, 2, 3, 4, 5};
- Each element is explicitly assigned a value.
Partial Initialization
int numbers[5] = {1, 2}; // Remaining elements are set to zero (for global/static arrays)
- Uninitialized elements:
- For static and global arrays: Remaining elements are automatically initialized to zero.
- For automatic (local) arrays: Remaining elements contain garbage values (undefined).
Size Inference
int numbers[] = {1, 2, 3}; // Array size is inferred from the number of initializers (size is 3)
- The compiler determines the array size based on the initial values provided.
Two-Dimensional Arrays
Row-Major Initialization
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
- Each sub-array represents a row in the matrix.
Flattened Initialization
int matrix[2][3] = {1, 2, 3, 4, 5, 6};
- Values are assigned row by row:
matrix[0][0] = 1matrix[0][1] = 2matrix[0][2] = 3matrix[1][0] = 4matrix[1][1] = 5matrix[1][2] = 6
Defining and Processing One-Dimensional Arrays
Defining One-Dimensional Arrays
Declaration
data_type array_name[array_size];
Example
char letters[26]; // An array to hold the 26 letters of the English alphabet
Accessing Array Elements
Syntax
array_name[index];
- Indexing:
- The first element has an index of
0. - The last element has an index of
array_size - 1.
- The first element has an index of
Example
int numbers[5] = {10, 20, 30, 40, 50};
int first = numbers[0]; // Accesses the first element (10)
int last = numbers[4]; // Accesses the last element (50)
Processing Arrays
Traversal
Using loops to iterate over array elements.
Example: Printing Array Elements
for (int i = 0; i < 5; i++) {
printf("%d\n", numbers[i]);
}
Calculations
Perform operations like sum, average, find maximum/minimum.
Example: Calculating Sum and Average
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += numbers[i];
}
float average = sum / 5.0;
printf("Sum: %d, Average: %.2f\n", sum, average);
Modifying Elements
for (int i = 0; i < 5; i++) {
numbers[i] *= 2; // Doubles each element
}
Defining and Processing Two-Dimensional Arrays
Defining Two-Dimensional Arrays
Declaration
data_type array_name[rows][columns];
Example
int matrix[3][4]; // Declares a 2D array with 3 rows and 4 columns
4.2 Accessing Elements
Syntax
array_name[row_index][column_index];
- Indexing starts from
0for both rows and columns.
Example
int value = matrix[1][2]; // Accesses element at second row and third column
Processing Two-Dimensional Arrays
Traversal
Using nested loops to iterate over rows and columns.
Example: Printing Matrix Elements
for (int i = 0; i < 3; i++) { // Iterates over rows
for (int j = 0; j < 4; j++) { // Iterates over columns
printf("%d ", matrix[i][j]);
}
printf("\n"); // Newline after each row
}
Initializing Elements
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
matrix[i][j] = i + j; // Example initialization
}
}
Applications
- Matrix Operations: Addition, subtraction, multiplication.
- Data Representation: Grids, tables, images, game boards.
- Statistical Data: Storing data sets with multiple attributes.
Array Applications
Common Uses
- Data Storage: Efficiently store and manage collections of data.
- Statistical Analysis: Hold data sets for calculations like mean, median, mode.
- String Handling: Character arrays (
char[]) are used for strings. - Matrices and Vectors: Perform mathematical computations.
- Lookup Tables: Store precomputed values for quick retrieval.
Examples
Storing Marks of Students
int marks[50]; // Stores the marks of 50 students
Temperature Readings
float temperatures[24]; // Stores hourly temperature readings for a day
Character Arrays (Strings)
char message[] = "Hello, World!";
- Note: In C, strings are represented as arrays of characters terminated by a null character
'\0'.
Passing Arrays to Functions
Passing One-Dimensional Arrays
Function Definition
void functionName(data_type array_name[], int size);
- data_type: Type of the array elements.
- array_name[]: Indicates that an array is expected.
- size: Number of elements in the array.
Function Call
functionName(array, array_size);
Example: Printing an Array
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
printArray(numbers, 5);
return 0;
}
Passing Two-Dimensional Arrays
Requirement
- When passing a 2D array, you must specify the number of columns in the function definition.
Function Definition
void functionName(data_type array_name[][columns], int rows);
- columns: The size of the second dimension (number of columns) must be known.
Function Call
functionName(matrix, number_of_rows);
Example: Printing a Matrix
void printMatrix(int matrix[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) { // '3' is the number of columns
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
int main() {
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printMatrix(matrix, 2);
return 0;
}
Notes on Passing Arrays
- Passed by Reference:
- The array name acts as a pointer to the first element.
- Modifications to array elements within the function affect the original array.
- Size Information:
- The function needs to know the size of the array (either passed as a parameter or predetermined).
Inserting and Deleting Elements in an Array
Insertion
Process
- Validate Position: Ensure the position is within the valid range (0 to size).
- Shift Elements: Move elements starting from the position to the right to create space.
- Insert Element: Place the new element at the desired position.
- Update Size: Increment the array size.
Example Function: Inserting an Element
void insertElement(int arr[], int *size, int element, int position) {
if (position < 0 || position > *size) {
printf("Invalid position!\n");
return;
}
for (int i = *size; i > position; i--) {
arr[i] = arr[i - 1]; // Shift elements right
}
arr[position] = element;
(*size)++; // Increment size
}
Deletion
Process
- Validate Position: Ensure the position is within valid range (0 to size - 1).
- Remove Element: Element at the position is to be removed.
- Shift Elements: Move elements after the position to the left to fill the gap.
- Update Size: Decrement the array size.
Example Function: Deleting an Element
void deleteElement(int arr[], int *size, int position) {
if (position < 0 || position >= *size) {
printf("Invalid position!\n");
return;
}
for (int i = position; i < *size - 1; i++) {
arr[i] = arr[i + 1]; // Shift elements left
}
(*size)--; // Decrement size
}
Searching in Arrays
Linear Search
Algorithm
- Start from the first element.
- Compare each element with the search key.
- Continue until the key is found or the array ends.
Time Complexity
- Worst Case: O(n), where n is the number of elements.
Implementation
int linearSearch(int arr[], int size, int key) {
for (int i = 0; i < size; i++) {
if (arr[i] == key) {
return i; // Key found at index i
}
}
return -1; // Key not found
}
Use Cases
- Suitable for unsorted arrays.
- Simple and easy to implement.
Binary Search
Precondition
- The array must be sorted in ascending or descending order.
Algorithm
- Initialize:
low = 0high = size - 1
- Loop:
- While
low <= high:- Calculate
mid = (low + high) / 2. - If
arr[mid] == key, returnmid. - If
arr[mid] < key, setlow = mid + 1. - If
arr[mid] > key, sethigh = mid - 1.
- Calculate
- While
- Not Found:
- If the loop exits, the key is not in the array.
Time Complexity
- Average and Worst Case: O(log n)
Implementation
int binarySearch(int arr[], int size, int key) {
int low = 0, high = size - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == key) {
return mid; // Key found at index mid
} else if (arr[mid] < key) {
low = mid + 1; // Search in the right half
} else {
high = mid - 1; // Search in the left half
}
}
return -1; // Key not found
}
Use Cases
- Efficient for large datasets.
- Requires that the data is sorted.
Sorting Arrays
Bubble Sort
Algorithm
- Outer Loop: Iterate over each element of the array.
- Inner Loop: For each element, compare it to the next element.
- If the current element is greater than the next one, swap them.
- Repeat:
- After each pass, the largest unsorted element "bubbles" to its correct position at the end.
Time Complexity
- Average and Worst Case: O(n²)
- Best Case (already sorted): O(n) with optimization.
Implementation
void bubbleSort(int arr[], int size) {
for (int i = 0; i < size - 1; i++) { // Number of passes
for (int j = 0; j < size - i - 1; j++) { // Pairwise comparisons
if (arr[j] > arr[j + 1]) {
// Swap arr[j] and arr[j + 1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
Steps Explained
First Pass:
- Compare
arr[0]andarr[1], swap if necessary. - Continue for all elements up to
arr[size - 1]. - Largest element moves to
arr[size - 1].
- Compare
Subsequent Passes:
- Repeat the process, ignoring the already sorted elements at the end.
Optimization: Early Termination
- Introduce a flag to monitor if any swaps occurred during a pass.
- If no swaps occurred, the array is sorted, and you can break early.
Optimized Implementation
void optimizedBubbleSort(int arr[], int size) {
int swapped;
for (int i = 0; i < size - 1; i++) {
swapped = 0; // Reset swap flag
for (int j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Swap
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = 1; // Set swap flag
}
}
if (swapped == 0) {
break; // The array is sorted
}
}
}
Applications of Sorting
- Searching: Prepare data for efficient searches (e.g., binary search).
- Data Organization: Facilitates data analysis and reporting.
- Duplicate Removal: Easier to identify duplicates in sorted data.
- Data Normalization: Sorting data before processing.
Best Practices with Arrays
Bounds Checking
- Ensure Valid Indices:
- Always check that your index is within
0andarray_size - 1.
- Always check that your index is within
- Avoid Out-of-Bounds Access:
- Accessing beyond array boundaries can lead to undefined behavior, crashes, or security vulnerabilities.
Initializing Arrays
- Initialize Arrays:
- Set default values to avoid working with garbage values.
- Global vs. Local:
- Global/static arrays are automatically initialized to zero.
- Local (automatic) arrays contain garbage values unless explicitly initialized.
Passing Arrays to Functions
- Prevent Stack Overflow:
- Be cautious when passing large arrays by value; it can consume significant stack memory.
- Use Pointers When Appropriate:
- For large data, pass a pointer to the array to avoid copying the entire array.
Memory Considerations
- Large Arrays:
- Be aware that large arrays consume significant memory.
- Consider using dynamic memory allocation (
malloc,calloc,realloc,free) for very large arrays.
Multi-Dimensional Arrays
- Visualization:
- Think of 2D arrays as arrays of arrays.
- Index Management:
- Carefully manage loop variables and indices when traversing multi-dimensional arrays.
Avoiding Magic Numbers
Use Constants:
Instead of hardcoding numbers, define constants or use
#definedirectives.#define SIZE 10 int numbers[SIZE];
Improves Readability:
- Makes code easier to understand and maintain.
Documentation
- Comment Your Code:
- Explain the purpose of arrays, especially when sizes and dimensions are critical.
- Function Comments:
- Document functions that manipulate arrays, including preconditions (e.g., arrays must be sorted).
Error Handling
- Robust Functions:
- When writing functions that manipulate arrays, include error checking for invalid inputs.